From 2966c2c3adf0cb1499284a43ddc69d611d72c604 Mon Sep 17 00:00:00 2001 From: ekmb Date: Thu, 9 Feb 2023 22:13:49 -0800 Subject: [PATCH 01/19] remove TN Signed-off-by: ekmb --- Dockerfile | 7 +- Jenkinsfile | 92 +- .../tts/aligner_heteronym_disambiguation.py | 12 +- .../{text_processing => tts}/g2p/README.md | 0 .../g2p/conf/g2p_conformer_ctc.yaml | 8 +- .../g2p/conf/heteronym_classification.yaml | 6 +- .../g2p/conf/t5_g2p.yaml | 6 +- .../g2p/g2p_inference.py | 2 +- .../g2p/g2p_train_and_evaluate.py | 4 +- .../g2p/heteronym_classification_inference.py | 2 +- ...ronym_classification_train_and_evaluate.py | 2 +- .../{text_processing => tts}/g2p/utils.py | 4 +- .../text_to_speech/tokenizer_wrapper.py | 2 +- .../text_to_speech/tts_tokenizers.py | 24 +- .../collections/tts/g2p/data}/__init__.py | 4 +- .../collections/tts}/g2p/data/ctc_g2p.py | 2 +- .../collections/tts}/g2p/data/data_utils.py | 0 .../g2p/data/heteronym_classification_data.py | 0 .../collections/tts}/g2p/data/t5_g2p.py | 2 +- .../collections/tts}/g2p/models/__init__.py | 4 +- .../collections/tts}/g2p/models/ctc_g2p.py | 4 +- .../collections/tts}/g2p/models/g2p_model.py | 0 .../g2p/models/heteronym_classification.py | 4 +- .../collections/tts}/g2p/models/t5_g2p.py | 4 +- .../collections/tts}/g2p/modules.py | 10 +- nemo/collections/tts/models/aligner.py | 10 +- nemo/collections/tts/models/fastpitch.py | 13 +- nemo/collections/tts/models/mixer_tts.py | 10 +- nemo/collections/tts/models/radtts.py | 17 +- nemo/collections/tts/models/tacotron2.py | 10 +- nemo/collections/tts/models/vits.py | 18 +- nemo/collections/tts/torch/g2ps.py | 1 - nemo_text_processing/README.md | 8 - nemo_text_processing/__init__.py | 13 - nemo_text_processing/fst_alignment/README.md | 27 - .../fst_alignment/alignment.cpp | 194 - .../fst_alignment/alignment.py | 254 - nemo_text_processing/g2p/__init__.py | 13 - nemo_text_processing/g2p/data/__init__.py | 18 - nemo_text_processing/hybrid/model_utils.py | 158 - nemo_text_processing/hybrid/utils.py | 696 - .../hybrid/wfst_lm_rescoring.py | 335 - nemo_text_processing/install_pynini.sh | 2 - .../inverse_text_normalization/README.md | 11 - .../inverse_text_normalization/__init__.py | 13 - .../inverse_text_normalization/de/__init__.py | 17 - .../de/taggers/__init__.py | 13 - .../de/taggers/cardinal.py | 67 - .../de/taggers/date.py | 90 - .../de/taggers/decimal.py | 59 - .../de/taggers/electronic.py | 38 - .../de/taggers/fraction.py | 67 - .../de/taggers/measure.py | 103 - .../de/taggers/money.py | 91 - .../de/taggers/ordinal.py | 42 - .../de/taggers/telephone.py | 53 - .../de/taggers/time.py | 43 - .../de/taggers/tokenize_and_classify.py | 141 - .../de/taggers/whitelist.py | 34 - .../de/verbalizers/__init__.py | 13 - .../de/verbalizers/cardinal.py | 35 - .../de/verbalizers/decimal.py | 43 - .../de/verbalizers/measure.py | 64 - .../de/verbalizers/money.py | 40 - .../de/verbalizers/time.py | 52 - .../de/verbalizers/verbalize.py | 45 - .../de/verbalizers/verbalize_final.py | 43 - .../inverse_text_normalization/en/__init__.py | 17 - .../en/clean_eval_data.py | 342 - .../en/data/__init__.py | 13 - .../en/data/currency.tsv | 35 - .../en/data/electronic/__init__.py | 13 - .../en/data/electronic/domain.tsv | 10 - .../en/data/electronic/server_name.tsv | 17 - .../en/data/electronic/symbols.tsv | 22 - .../en/data/magnitudes.tsv | 4 - .../en/data/measurements.tsv | 109 - .../en/data/months.tsv | 12 - .../en/data/numbers/__init__.py | 13 - .../en/data/numbers/digit.tsv | 9 - .../en/data/numbers/hundred.tsv | 1 - .../en/data/numbers/teen.tsv | 10 - .../en/data/numbers/thousands.tsv | 22 - .../en/data/numbers/ties.tsv | 9 - .../en/data/numbers/zero.tsv | 1 - .../en/data/ordinals/__init__.py | 13 - .../en/data/ordinals/digit.tsv | 9 - .../en/data/ordinals/teen.tsv | 1 - .../en/data/time/__init__.py | 13 - .../en/data/time/minute_to.tsv | 59 - .../en/data/time/time_suffix.tsv | 8 - .../en/data/time/time_zone.tsv | 7 - .../en/data/time/to_hour.tsv | 12 - .../en/data/whitelist.tsv | 12 - .../en/taggers/__init__.py | 13 - .../en/taggers/cardinal.py | 143 - .../en/taggers/date.py | 154 - .../en/taggers/decimal.py | 94 - .../en/taggers/electronic.py | 91 - .../en/taggers/fraction.py | 26 - .../en/taggers/measure.py | 97 - .../en/taggers/money.py | 115 - .../en/taggers/ordinal.py | 44 - .../en/taggers/punctuation.py | 35 - .../en/taggers/telephone.py | 136 - .../en/taggers/time.py | 143 - .../en/taggers/tokenize_and_classify.py | 111 - .../en/taggers/whitelist.py | 34 - .../en/taggers/word.py | 30 - .../inverse_text_normalization/en/utils.py | 47 - .../en/verbalizers/__init__.py | 13 - .../en/verbalizers/cardinal.py | 48 - .../en/verbalizers/date.py | 80 - .../en/verbalizers/decimal.py | 58 - .../en/verbalizers/electronic.py | 55 - .../en/verbalizers/fraction.py | 25 - .../en/verbalizers/measure.py | 62 - .../en/verbalizers/money.py | 41 - .../en/verbalizers/ordinal.py | 58 - .../en/verbalizers/telephone.py | 41 - .../en/verbalizers/time.py | 81 - .../en/verbalizers/verbalize.py | 62 - .../en/verbalizers/verbalize_final.py | 44 - .../en/verbalizers/whitelist.py | 38 - .../en/verbalizers/word.py | 33 - .../inverse_text_normalization/es/__init__.py | 17 - .../es/data/__init__.py | 13 - .../es/data/currency_plural.tsv | 6 - .../es/data/currency_singular.tsv | 6 - .../es/data/electronic/__init__.py | 13 - .../es/data/electronic/domain.tsv | 25 - .../es/data/electronic/server_name.tsv | 17 - .../es/data/electronic/symbols.tsv | 4 - .../es/data/measurements_plural.tsv | 19 - .../es/data/measurements_singular.tsv | 19 - .../es/data/months.tsv | 12 - .../es/data/numbers/__init__.py | 13 - .../es/data/numbers/digit.tsv | 12 - .../es/data/numbers/hundreds.tsv | 18 - .../es/data/numbers/teen.tsv | 10 - .../es/data/numbers/ties.tsv | 9 - .../es/data/numbers/twenties.tsv | 14 - .../es/data/numbers/zero.tsv | 1 - .../es/data/ordinals/__init__.py | 13 - .../es/data/ordinals/digit.tsv | 22 - .../es/data/ordinals/hundreds.tsv | 18 - .../es/data/ordinals/teen.tsv | 55 - .../es/data/ordinals/ties.tsv | 15 - .../es/data/ordinals/twenties.tsv | 20 - .../es/data/time/__init__.py | 13 - .../es/data/time/time_suffix.tsv | 12 - .../es/data/time/time_to.tsv | 24 - .../es/data/whitelist.tsv | 2 - .../es/taggers/__init__.py | 13 - .../es/taggers/cardinal.py | 188 - .../es/taggers/date.py | 59 - .../es/taggers/decimal.py | 117 - .../es/taggers/electronic.py | 96 - .../es/taggers/measure.py | 96 - .../es/taggers/money.py | 111 - .../es/taggers/ordinal.py | 91 - .../es/taggers/punctuation.py | 34 - .../es/taggers/telephone.py | 138 - .../es/taggers/time.py | 143 - .../es/taggers/tokenize_and_classify.py | 111 - .../es/taggers/whitelist.py | 33 - .../es/taggers/word.py | 29 - .../inverse_text_normalization/es/utils.py | 27 - .../es/verbalizers/__init__.py | 13 - .../es/verbalizers/cardinal.py | 48 - .../es/verbalizers/date.py | 65 - .../es/verbalizers/decimal.py | 66 - .../es/verbalizers/electronic.py | 55 - .../es/verbalizers/measure.py | 61 - .../es/verbalizers/money.py | 40 - .../es/verbalizers/ordinal.py | 45 - .../es/verbalizers/telephone.py | 32 - .../es/verbalizers/time.py | 82 - .../es/verbalizers/verbalize.py | 62 - .../es/verbalizers/verbalize_final.py | 43 - .../es/verbalizers/whitelist.py | 37 - .../es/verbalizers/word.py | 32 - .../inverse_text_normalization/fr/README.md | 30 - .../inverse_text_normalization/fr/__init__.py | 17 - .../fr/data/__init__.py | 13 - .../fr/data/electronic/__init__.py | 13 - .../fr/data/electronic/domain.tsv | 25 - .../fr/data/electronic/server_name.tsv | 17 - .../fr/data/electronic/symbols.tsv | 12 - .../fr/data/fractions.tsv | 41 - .../fr/data/measurements/__init__.py | 13 - .../fr/data/measurements/magnitudes.tsv | 16 - .../fr/data/measurements/measurements.tsv | 27 - .../fr/data/money/__init__.py | 13 - .../fr/data/money/currency_major.tsv | 40 - .../fr/data/money/currency_minor.tsv | 5 - .../fr/data/months.tsv | 12 - .../fr/data/numbers/__init__.py | 13 - .../fr/data/numbers/digit.tsv | 10 - .../fr/data/numbers/hundreds.tsv | 2 - .../fr/data/numbers/teen.tsv | 6 - .../fr/data/numbers/thousands.tsv | 23 - .../fr/data/numbers/ties.tsv | 13 - .../fr/data/numbers/ties_unique.tsv | 25 - .../fr/data/numbers/zero.tsv | 1 - .../fr/data/ordinals/__init__.py | 13 - .../fr/data/ordinals/digits_root_change.tsv | 17 - .../fr/data/ordinals/firsts.tsv | 4 - .../fr/data/ordinals/key_nouns.tsv | 1 - .../fr/data/ordinals/second.tsv | 4 - .../fr/data/roman/__init__.py | 13 - .../fr/data/roman/digits_large.tsv | 9 - .../fr/data/roman/hundreds_large.tsv | 9 - .../fr/data/roman/ties_large.tsv | 9 - .../fr/data/suppletive.tsv | 0 .../fr/data/time/__init__.py | 13 - .../fr/data/time/hour_to_night.tsv | 12 - .../fr/data/time/hours.tsv | 26 - .../fr/data/time/hours_to.tsv | 25 - .../fr/data/time/minutes.tsv | 63 - .../fr/data/time/minutes_to.tsv | 59 - .../fr/data/time/time_suffix_am.tsv | 1 - .../fr/data/time/time_suffix_pm.tsv | 2 - .../fr/data/whitelist.tsv | 16 - .../fr/graph_utils.py | 189 - .../fr/taggers/__init__.py | 13 - .../fr/taggers/cardinal.py | 278 - .../fr/taggers/date.py | 55 - .../fr/taggers/decimal.py | 128 - .../fr/taggers/electronic.py | 110 - .../fr/taggers/fraction.py | 75 - .../fr/taggers/measure.py | 92 - .../fr/taggers/money.py | 134 - .../fr/taggers/ordinal.py | 77 - .../fr/taggers/punctuation.py | 36 - .../fr/taggers/telephone.py | 82 - .../fr/taggers/time.py | 115 - .../fr/taggers/tokenize_and_classify.py | 116 - .../fr/taggers/whitelist.py | 33 - .../fr/taggers/word.py | 29 - .../inverse_text_normalization/fr/utils.py | 27 - .../fr/verbalizers/__init__.py | 13 - .../fr/verbalizers/cardinal.py | 48 - .../fr/verbalizers/date.py | 76 - .../fr/verbalizers/decimal.py | 94 - .../fr/verbalizers/electronic.py | 45 - .../fr/verbalizers/fraction.py | 53 - .../fr/verbalizers/measure.py | 72 - .../fr/verbalizers/money.py | 45 - .../fr/verbalizers/ordinal.py | 81 - .../fr/verbalizers/telephone.py | 32 - .../fr/verbalizers/time.py | 68 - .../fr/verbalizers/verbalize.py | 64 - .../fr/verbalizers/verbalize_final.py | 43 - .../fr/verbalizers/whitelist.py | 42 - .../fr/verbalizers/word.py | 37 - .../inverse_normalize.py | 148 - .../inverse_text_normalization/pt/__init__.py | 17 - .../pt/data/__init__.py | 13 - .../pt/data/currency_plural.tsv | 5 - .../pt/data/currency_singular.tsv | 5 - .../pt/data/electronic/__init__.py | 13 - .../pt/data/electronic/domain.tsv | 26 - .../pt/data/electronic/server_name.tsv | 11 - .../pt/data/electronic/symbols.tsv | 6 - .../pt/data/measurements_plural.tsv | 56 - .../pt/data/measurements_singular.tsv | 55 - .../pt/data/months.tsv | 12 - .../pt/data/numbers/__init__.py | 13 - .../pt/data/numbers/digit.tsv | 11 - .../pt/data/numbers/hundreds.tsv | 17 - .../pt/data/numbers/onehundred.tsv | 1 - .../pt/data/numbers/teen.tsv | 11 - .../pt/data/numbers/ties.tsv | 8 - .../pt/data/numbers/twenties.tsv | 9 - .../pt/data/numbers/zero.tsv | 1 - .../pt/data/ordinals/__init__.py | 13 - .../pt/data/ordinals/digit.tsv | 18 - .../pt/data/ordinals/hundreds.tsv | 28 - .../pt/data/ordinals/ties.tsv | 20 - .../pt/data/time/__init__.py | 13 - .../pt/data/time/hour_to_am.tsv | 1 - .../pt/data/time/hour_to_pm.tsv | 1 - .../pt/data/time/hours_to.tsv | 23 - .../pt/data/time/minutes_to.tsv | 59 - .../pt/data/time/time_suffix_am.tsv | 2 - .../pt/data/time/time_suffix_pm.tsv | 2 - .../pt/data/whitelist.tsv | 5 - .../pt/taggers/__init__.py | 13 - .../pt/taggers/cardinal.py | 385 - .../pt/taggers/date.py | 85 - .../pt/taggers/decimal.py | 119 - .../pt/taggers/electronic.py | 96 - .../pt/taggers/measure.py | 95 - .../pt/taggers/money.py | 127 - .../pt/taggers/ordinal.py | 84 - .../pt/taggers/punctuation.py | 34 - .../pt/taggers/telephone.py | 131 - .../pt/taggers/time.py | 214 - .../pt/taggers/tokenize_and_classify.py | 110 - .../pt/taggers/whitelist.py | 33 - .../pt/taggers/word.py | 29 - .../inverse_text_normalization/pt/utils.py | 27 - .../pt/verbalizers/__init__.py | 13 - .../pt/verbalizers/cardinal.py | 48 - .../pt/verbalizers/date.py | 87 - .../pt/verbalizers/decimal.py | 66 - .../pt/verbalizers/electronic.py | 55 - .../pt/verbalizers/measure.py | 61 - .../pt/verbalizers/money.py | 40 - .../pt/verbalizers/ordinal.py | 44 - .../pt/verbalizers/telephone.py | 32 - .../pt/verbalizers/time.py | 82 - .../pt/verbalizers/verbalize.py | 62 - .../pt/verbalizers/verbalize_final.py | 43 - .../pt/verbalizers/whitelist.py | 37 - .../pt/verbalizers/word.py | 32 - .../inverse_text_normalization/ru/__init__.py | 13 - .../ru/taggers/__init__.py | 13 - .../ru/taggers/cardinal.py | 46 - .../ru/taggers/date.py | 37 - .../ru/taggers/decimals.py | 54 - .../ru/taggers/electronic.py | 38 - .../ru/taggers/measure.py | 38 - .../ru/taggers/money.py | 38 - .../ru/taggers/ordinal.py | 44 - .../ru/taggers/telephone.py | 38 - .../ru/taggers/time.py | 69 - .../ru/taggers/tokenize_and_classify.py | 114 - .../ru/taggers/whitelist.py | 36 - .../ru/verbalizers/__init__.py | 13 - .../ru/verbalizers/cardinal.py | 48 - .../ru/verbalizers/date.py | 30 - .../ru/verbalizers/decimal.py | 42 - .../ru/verbalizers/electronic.py | 31 - .../ru/verbalizers/measure.py | 37 - .../ru/verbalizers/money.py | 31 - .../ru/verbalizers/ordinal.py | 36 - .../ru/verbalizers/telephone.py | 31 - .../ru/verbalizers/time.py | 45 - .../ru/verbalizers/verbalize.py | 64 - .../ru/verbalizers/verbalize_final.py | 43 - .../run_evaluate.py | 113 - .../inverse_text_normalization/vi/__init__.py | 17 - .../vi/data/__init__.py | 13 - .../vi/data/currency.tsv | 11 - .../vi/data/electronic/__init__.py | 13 - .../vi/data/electronic/domain.tsv | 11 - .../vi/data/electronic/server_name.tsv | 16 - .../vi/data/electronic/symbols.tsv | 20 - .../vi/data/magnitudes.tsv | 5 - .../vi/data/math/__init__.py | 13 - .../vi/data/math/symbols.tsv | 10 - .../vi/data/measurements.tsv | 185 - .../vi/data/months.tsv | 14 - .../vi/data/numbers/__init__.py | 13 - .../vi/data/numbers/digit.tsv | 10 - .../vi/data/numbers/hundred.tsv | 1 - .../vi/data/numbers/teen.tsv | 11 - .../vi/data/numbers/thousands.tsv | 6 - .../vi/data/numbers/ties.tsv | 9 - .../vi/data/numbers/zero.tsv | 1 - .../vi/data/ordinals/__init__.py | 13 - .../vi/data/ordinals/digit.tsv | 12 - .../vi/data/time/__init__.py | 13 - .../vi/data/time/hours.tsv | 29 - .../vi/data/time/hours_to.tsv | 25 - .../vi/data/time/hours_to_night.tsv | 12 - .../vi/data/time/minutes.tsv | 118 - .../vi/data/time/minutes_to.tsv | 59 - .../vi/data/time/time_suffix.tsv | 8 - .../vi/data/time/time_zone.tsv | 7 - .../vi/data/whitelist.tsv | 0 .../vi/graph_utils.py | 152 - .../vi/taggers/__init__.py | 13 - .../vi/taggers/cardinal.py | 153 - .../vi/taggers/date.py | 157 - .../vi/taggers/decimal.py | 131 - .../vi/taggers/electronic.py | 93 - .../vi/taggers/fraction.py | 57 - .../vi/taggers/measure.py | 103 - .../vi/taggers/money.py | 75 - .../vi/taggers/ordinal.py | 38 - .../vi/taggers/punctuation.py | 35 - .../vi/taggers/telephone.py | 40 - .../vi/taggers/time.py | 125 - .../vi/taggers/tokenize_and_classify.py | 116 - .../vi/taggers/whitelist.py | 34 - .../vi/taggers/word.py | 30 - .../inverse_text_normalization/vi/utils.py | 27 - .../vi/verbalizers/__init__.py | 13 - .../vi/verbalizers/cardinal.py | 48 - .../vi/verbalizers/date.py | 73 - .../vi/verbalizers/decimal.py | 58 - .../vi/verbalizers/electronic.py | 55 - .../vi/verbalizers/fraction.py | 43 - .../vi/verbalizers/measure.py | 83 - .../vi/verbalizers/money.py | 41 - .../vi/verbalizers/ordinal.py | 39 - .../vi/verbalizers/telephone.py | 33 - .../vi/verbalizers/time.py | 89 - .../vi/verbalizers/verbalize.py | 66 - .../vi/verbalizers/verbalize_final.py | 44 - .../vi/verbalizers/whitelist.py | 43 - .../vi/verbalizers/word.py | 38 - .../text_normalization/README.md | 10 - .../text_normalization/__init__.py | 13 - .../text_normalization/data_loader_utils.py | 351 - .../text_normalization/de/__init__.py | 13 - .../text_normalization/de/data/__init__.py | 13 - .../de/data/electronic/__init__.py | 13 - .../de/data/electronic/domain.tsv | 9 - .../de/data/electronic/server_name.tsv | 12 - .../de/data/electronic/symbols.tsv | 20 - .../text_normalization/de/data/fractions.tsv | 30 - .../de/data/measure/__init__.py | 13 - .../de/data/measure/measurements.tsv | 82 - .../de/data/measure/suppletive.tsv | 10 - .../de/data/money/__init__.py | 13 - .../de/data/money/currency.tsv | 25 - .../de/data/money/currency_minor_plural.tsv | 3 - .../de/data/money/currency_minor_singular.tsv | 3 - .../de/data/months/__init__.py | 13 - .../de/data/months/abbr_to_name.tsv | 13 - .../de/data/months/numbers.tsv | 24 - .../de/data/numbers/__init__.py | 13 - .../de/data/numbers/digit.tsv | 8 - .../de/data/numbers/ones.tsv | 3 - .../de/data/numbers/quantities.tsv | 12 - .../de/data/numbers/teen.tsv | 10 - .../de/data/numbers/ties.tsv | 8 - .../de/data/numbers/zero.tsv | 1 - .../de/data/ordinals/__init__.py | 13 - .../de/data/ordinals/digit.tsv | 9 - .../de/data/ordinals/thousands.tsv | 4 - .../de/data/ordinals/ties.tsv | 8 - .../de/data/time/__init__.py | 13 - .../de/data/time/hour_to.tsv | 12 - .../de/data/time/hour_to_night.tsv | 13 - .../de/data/time/minute_to.tsv | 59 - .../de/data/time/time_zone.tsv | 7 - .../text_normalization/de/data/whitelist.tsv | 7 - .../text_normalization/de/taggers/__init__.py | 13 - .../text_normalization/de/taggers/cardinal.py | 199 - .../text_normalization/de/taggers/date.py | 141 - .../text_normalization/de/taggers/decimal.py | 82 - .../de/taggers/electronic.py | 65 - .../text_normalization/de/taggers/fraction.py | 54 - .../text_normalization/de/taggers/measure.py | 187 - .../text_normalization/de/taggers/money.py | 148 - .../text_normalization/de/taggers/ordinal.py | 49 - .../de/taggers/telephone.py | 72 - .../text_normalization/de/taggers/time.py | 97 - .../de/taggers/tokenize_and_classify.py | 144 - .../de/taggers/whitelist.py | 63 - .../text_normalization/de/taggers/word.py | 33 - .../text_normalization/de/utils.py | 48 - .../de/verbalizers/__init__.py | 13 - .../de/verbalizers/cardinal.py | 42 - .../text_normalization/de/verbalizers/date.py | 61 - .../de/verbalizers/decimal.py | 58 - .../de/verbalizers/electronic.py | 74 - .../de/verbalizers/fraction.py | 71 - .../de/verbalizers/measure.py | 53 - .../de/verbalizers/money.py | 73 - .../de/verbalizers/ordinal.py | 50 - .../de/verbalizers/telephone.py | 42 - .../text_normalization/de/verbalizers/time.py | 120 - .../de/verbalizers/verbalize.py | 76 - .../de/verbalizers/verbalize_final.py | 71 - .../text_normalization/en/__init__.py | 17 - .../text_normalization/en/clean_eval_data.py | 342 - .../text_normalization/en/data/__init__.py | 13 - .../en/data/address/__init__.py | 13 - .../en/data/address/address_word.tsv | 14 - .../en/data/address/state.tsv | 52 - .../en/data/date/__init__.py | 13 - .../text_normalization/en/data/date/day.tsv | 31 - .../en/data/date/month_abbr.tsv | 12 - .../en/data/date/month_name.tsv | 12 - .../en/data/date/month_number.tsv | 24 - .../en/data/date/year_suffix.tsv | 16 - .../en/data/electronic/__init__.py | 13 - .../en/data/electronic/domain.tsv | 12 - .../en/data/electronic/symbol.tsv | 21 - .../en/data/measure/__init__.py | 13 - .../en/data/measure/math_operation.tsv | 8 - .../en/data/measure/unit.tsv | 127 - .../en/data/measure/unit_alternatives.tsv | 43 - .../en/data/money/__init__.py | 13 - .../en/data/money/currency_major.tsv | 39 - .../en/data/money/currency_minor_plural.tsv | 4 - .../en/data/money/currency_minor_singular.tsv | 3 - .../en/data/money/per_unit.tsv | 2 - .../en/data/number/__init__.py | 13 - .../en/data/number/cardinal_number_name.far | Bin 325078 -> 0 bytes .../en/data/number/digit.tsv | 9 - .../en/data/number/fraction.tsv | 18 - .../en/data/number/hundred.tsv | 1 - .../en/data/number/quantity_abbr.tsv | 10 - .../en/data/number/teen.tsv | 10 - .../en/data/number/thousand.tsv | 22 - .../text_normalization/en/data/number/ty.tsv | 8 - .../en/data/number/zero.tsv | 1 - .../en/data/ordinal/__init__.py | 13 - .../en/data/ordinal/digit.tsv | 9 - .../en/data/ordinal/teen.tsv | 1 - .../en/data/roman/README.md | 20 - .../en/data/roman/__init__.py | 13 - .../en/data/roman/female.tsv | 4998 ------ .../en/data/roman/key_word.tsv | 6 - .../text_normalization/en/data/roman/male.tsv | 2946 ---- .../en/data/roman/roman_to_spoken.tsv | 2000 --- .../text_normalization/en/data/suppletive.tsv | 83 - .../en/data/telephone/__init__.py | 13 - .../en/data/telephone/ip_prompt.tsv | 2 - .../en/data/telephone/ssn_prompt.tsv | 4 - .../en/data/telephone/telephone_prompt.tsv | 5 - .../en/data/time/__init__.py | 13 - .../en/data/time/suffix.tsv | 12 - .../text_normalization/en/data/time/zone.tsv | 14 - .../en/data/whitelist/__init__.py | 13 - .../en/data/whitelist/alternatives.tsv | 45 - .../whitelist/alternatives_all_format.tsv | 14 - .../en/data/whitelist/asr.tsv | 14713 ---------------- .../en/data/whitelist/ipa_symbols.tsv | 521 - .../en/data/whitelist/lj_speech.tsv | 21 - .../en/data/whitelist/symbol.tsv | 23 - .../en/data/whitelist/tts.tsv | 3851 ---- .../text_normalization/en/graph_utils.py | 196 - .../text_normalization/en/taggers/__init__.py | 13 - .../en/taggers/abbreviation.py | 50 - .../text_normalization/en/taggers/cardinal.py | 138 - .../text_normalization/en/taggers/date.py | 368 - .../text_normalization/en/taggers/decimal.py | 129 - .../en/taggers/electronic.py | 87 - .../text_normalization/en/taggers/fraction.py | 55 - .../text_normalization/en/taggers/measure.py | 304 - .../text_normalization/en/taggers/money.py | 192 - .../text_normalization/en/taggers/ordinal.py | 61 - .../en/taggers/punctuation.py | 65 - .../text_normalization/en/taggers/range.py | 102 - .../text_normalization/en/taggers/roman.py | 114 - .../text_normalization/en/taggers/serial.py | 136 - .../en/taggers/telephone.py | 133 - .../text_normalization/en/taggers/time.py | 132 - .../en/taggers/tokenize_and_classify.py | 215 - .../en/taggers/tokenize_and_classify_lm.py | 228 - .../tokenize_and_classify_with_audio.py | 229 - .../en/taggers/whitelist.py | 150 - .../text_normalization/en/taggers/word.py | 90 - .../text_normalization/en/utils.py | 60 - .../en/verbalizers/__init__.py | 13 - .../en/verbalizers/abbreviation.py | 35 - .../en/verbalizers/cardinal.py | 45 - .../text_normalization/en/verbalizers/date.py | 101 - .../en/verbalizers/decimal.py | 67 - .../en/verbalizers/electronic.py | 97 - .../en/verbalizers/fraction.py | 88 - .../en/verbalizers/measure.py | 102 - .../en/verbalizers/money.py | 71 - .../en/verbalizers/ordinal.py | 53 - .../en/verbalizers/post_processing.py | 182 - .../en/verbalizers/roman.py | 68 - .../en/verbalizers/telephone.py | 63 - .../text_normalization/en/verbalizers/time.py | 102 - .../en/verbalizers/verbalize.py | 82 - .../en/verbalizers/verbalize_final.py | 76 - .../en/verbalizers/whitelist.py | 39 - .../text_normalization/en/verbalizers/word.py | 35 - .../text_normalization/es/README.md | 22 - .../text_normalization/es/data/__init__.py | 13 - .../es/data/dates/__init__.py | 13 - .../es/data/dates/months.tsv | 12 - .../es/data/electronic/__init__.py | 13 - .../es/data/electronic/domain.tsv | 14 - .../es/data/electronic/server_name.tsv | 11 - .../es/data/electronic/symbols.tsv | 22 - .../es/data/fractions/__init__.py | 13 - .../es/data/fractions/ordinal_exceptions.tsv | 2 - .../es/data/fractions/powers_of_ten.tsv | 9 - .../es/data/fractions/powers_of_ten_fem.tsv | 6 - .../es/data/measures/__init__.py | 13 - .../es/data/measures/measurements.tsv | 17 - .../data/measures/measurements_plural_fem.tsv | 3 - .../measures/measurements_plural_masc.tsv | 15 - .../es/data/money/__init__.py | 13 - .../es/data/money/currency_major.tsv | 29 - .../es/data/money/currency_minor.tsv | 4 - .../es/data/money/currency_plural_fem.tsv | 8 - .../es/data/money/currency_plural_masc.tsv | 19 - .../es/data/numbers/__init__.py | 13 - .../es/data/numbers/digit.tsv | 9 - .../es/data/numbers/hundreds.tsv | 8 - .../es/data/numbers/quantities.tsv | 6 - .../es/data/numbers/teen.tsv | 10 - .../es/data/numbers/ties.tsv | 7 - .../es/data/numbers/twenties.tsv | 10 - .../es/data/numbers/zero.tsv | 1 - .../es/data/ordinals/__init__.py | 13 - .../es/data/ordinals/digit.tsv | 11 - .../es/data/ordinals/hundreds.tsv | 18 - .../es/data/ordinals/teen.tsv | 10 - .../es/data/ordinals/ties.tsv | 8 - .../es/data/ordinals/twenties.tsv | 11 - .../es/data/roman/__init__.py | 13 - .../es/data/roman/digit.tsv | 9 - .../es/data/roman/hundreds.tsv | 9 - .../text_normalization/es/data/roman/ties.tsv | 9 - .../es/data/time/__init__.py | 13 - .../es/data/time/afternoon_times.tsv | 4 - .../es/data/time/alt_minutes.tsv | 2 - .../es/data/time/evening_times.tsv | 7 - .../es/data/time/hour_to_12.tsv | 12 - .../es/data/time/hour_to_24.tsv | 25 - .../es/data/time/hour_to_night.tsv | 13 - .../es/data/time/minute_to.tsv | 59 - .../es/data/time/morning_times.tsv | 12 - .../es/data/time/time_suffix.tsv | 20 - .../es/data/time/time_zone.tsv | 7 - .../text_normalization/es/data/whitelist.tsv | 42 - .../text_normalization/es/graph_utils.py | 174 - .../text_normalization/es/taggers/__init__.py | 13 - .../text_normalization/es/taggers/cardinal.py | 176 - .../text_normalization/es/taggers/date.py | 96 - .../text_normalization/es/taggers/decimals.py | 127 - .../es/taggers/electronic.py | 74 - .../text_normalization/es/taggers/fraction.py | 114 - .../text_normalization/es/taggers/measure.py | 173 - .../text_normalization/es/taggers/money.py | 181 - .../text_normalization/es/taggers/ordinal.py | 175 - .../es/taggers/telephone.py | 144 - .../text_normalization/es/taggers/time.py | 208 - .../es/taggers/tokenize_and_classify.py | 150 - .../es/taggers/whitelist.py | 62 - .../text_normalization/es/taggers/word.py | 32 - .../text_normalization/es/utils.py | 42 - .../es/verbalizers/__init__.py | 13 - .../es/verbalizers/cardinal.py | 60 - .../text_normalization/es/verbalizers/date.py | 79 - .../es/verbalizers/decimals.py | 88 - .../es/verbalizers/electronic.py | 77 - .../es/verbalizers/fraction.py | 192 - .../es/verbalizers/measure.py | 108 - .../es/verbalizers/money.py | 166 - .../es/verbalizers/ordinal.py | 69 - .../es/verbalizers/telephone.py | 35 - .../text_normalization/es/verbalizers/time.py | 256 - .../es/verbalizers/verbalize.py | 73 - .../es/verbalizers/verbalize_final.py | 71 - .../text_normalization/normalize.py | 543 - .../normalize_with_audio.py | 554 - .../text_normalization/ru/__init__.py | 13 - .../text_normalization/ru/alphabet.py | 57 - .../text_normalization/ru/data/__init__.py | 13 - .../ru/data/currency/__init__.py | 13 - .../ru/data/currency/currency_plural.tsv | 53 - .../ru/data/currency/currency_singular.tsv | 48 - .../ru/data/electronic/__init__.py | 13 - .../ru/data/electronic/domain.tsv | 9 - .../ru/data/electronic/server_name.tsv | 17 - .../ru/data/electronic/symbols.tsv | 28 - .../ru/data/latin_to_cyrillic.tsv | 38 - .../ru/data/measurements.tsv | 321 - .../ru/data/months/__init__.py | 13 - .../ru/data/months/abbr.tsv | 12 - .../ru/data/months/abbr_to_name.tsv | 62 - .../ru/data/months/numbers.tsv | 21 - .../data/numbers/1_cardinals_nominative.tsv | 50 - .../ru/data/numbers/2_cardinals_genitive.tsv | 45 - .../ru/data/numbers/3_cardinals_dative.tsv | 45 - .../data/numbers/4_cardinals_accusative.tsv | 48 - .../data/numbers/5_cardinals_instrumental.tsv | 47 - .../numbers/6_cardinals_prepositional.tsv | 46 - .../ru/data/numbers/__init__.py | 13 - .../data/numbers/cardinals_alternatives.tsv | 16 - .../numbers/cardinals_nominative_case.tsv | 37 - .../ru/data/numbers/decimal_delimiter.tsv | 15 - .../ru/data/numbers/decimal_endings.tsv | 37 - .../data/numbers/digits_nominative_case.tsv | 10 - .../ru/data/numbers/ordinal_endings.tsv | 40 - .../ru/data/numbers/ordinals.tsv | 752 - .../ru/data/numbers/quantity.tsv | 16 - .../ru/data/time/__init__.py | 13 - .../ru/data/time/increment_hour_cardinal.tsv | 24 - .../ru/data/time/increment_hour_ordinal.tsv | 24 - .../ru/data/time/minutes_to_hour.tsv | 25 - .../ru/data/time/time_convert.tsv | 12 - .../ru/data/utils/__init__.py | 13 - .../text_normalization/ru/data/utils/g.fst | Bin 3206 -> 0 bytes .../data/utils/universal_thousands_punct.far | Bin 69495 -> 0 bytes .../ru/data/utils/util_arithmetic.far | Bin 7085564 -> 0 bytes .../ru/data/utils/util_byte.far | Bin 14099 -> 0 bytes .../text_normalization/ru/data/whitelist.tsv | 37 - .../text_normalization/ru/taggers/__init__.py | 13 - .../text_normalization/ru/taggers/cardinal.py | 146 - .../text_normalization/ru/taggers/date.py | 136 - .../text_normalization/ru/taggers/decimals.py | 107 - .../ru/taggers/electronic.py | 111 - .../text_normalization/ru/taggers/measure.py | 170 - .../text_normalization/ru/taggers/money.py | 95 - .../ru/taggers/number_names.py | 138 - .../text_normalization/ru/taggers/ordinal.py | 66 - .../ru/taggers/telephone.py | 78 - .../text_normalization/ru/taggers/time.py | 111 - .../ru/taggers/tokenize_and_classify.py | 142 - .../ru/taggers/whitelist.py | 63 - .../text_normalization/ru/taggers/word.py | 33 - .../text_normalization/ru/utils.py | 48 - .../ru/verbalizers/__init__.py | 13 - .../ru/verbalizers/cardinal.py | 44 - .../text_normalization/ru/verbalizers/date.py | 36 - .../ru/verbalizers/decimal.py | 51 - .../ru/verbalizers/electronic.py | 36 - .../ru/verbalizers/measure.py | 48 - .../ru/verbalizers/money.py | 36 - .../ru/verbalizers/ordinal.py | 36 - .../ru/verbalizers/telephone.py | 36 - .../text_normalization/ru/verbalizers/time.py | 55 - .../ru/verbalizers/verbalize.py | 69 - .../ru/verbalizers/verbalize_final.py | 71 - .../text_normalization/run_evaluate.py | 117 - .../text_normalization/token_parser.py | 193 - .../text_normalization/zh/README.md | 138 - .../text_normalization/zh/__init__.py | 13 - .../text_normalization/zh/data/__init__.py | 13 - .../zh/data/char/__init__.py | 13 - .../zh/data/char/charset_extension.tsv | 6 - .../charset_national_standard_2013_8105.tsv | 8105 --------- .../zh/data/char/fullwidth_to_halfwidth.tsv | 92 - .../zh/data/char/oov_tags.tsv | 1 - .../zh/data/char/punctuations_zh.tsv | 72 - .../zh/data/char/upper_to_lower.tsv | 9 - .../zh/data/date/__init__.py | 13 - .../zh/data/date/year_suffix.tsv | 6 - .../zh/data/denylist/__init__.py | 13 - .../zh/data/denylist/denylist.tsv | 2 - .../zh/data/erhua/__init__.py | 13 - .../zh/data/erhua/whitelist.tsv | 35 - .../zh/data/math/__init__.py | 13 - .../text_normalization/zh/data/math/score.tsv | 2 - .../zh/data/math/symbol.tsv | 7 - .../zh/data/measure/__init__.py | 13 - .../zh/data/measure/units_en.tsv | 90 - .../zh/data/measure/units_zh.tsv | 211 - .../zh/data/money/__init__.py | 13 - .../zh/data/money/currency_code.tsv | 117 - .../zh/data/money/currency_symbol.tsv | 63 - .../zh/data/number/__init__.py | 13 - .../zh/data/number/digit.tsv | 9 - .../zh/data/number/digit_teen.tsv | 9 - .../zh/data/number/sign.tsv | 2 - .../zh/data/number/zero.tsv | 1 - .../zh/data/time/__init__.py | 13 - .../text_normalization/zh/data/time/digit.tsv | 10 - .../text_normalization/zh/data/time/hour.tsv | 24 - .../zh/data/time/suffix.tsv | 10 - .../text_normalization/zh/data/time/tens.tsv | 7 - .../text_normalization/zh/data/time/zero.tsv | 1 - .../zh/data/whitelist/__init__.py | 13 - .../zh/data/whitelist/default.tsv | 79 - .../text_normalization/zh/graph_utils.py | 128 - .../text_normalization/zh/taggers/__init__.py | 13 - .../text_normalization/zh/taggers/cardinal.py | 107 - .../text_normalization/zh/taggers/char.py | 27 - .../text_normalization/zh/taggers/date.py | 105 - .../text_normalization/zh/taggers/fraction.py | 39 - .../zh/taggers/math_symbol.py | 41 - .../text_normalization/zh/taggers/measure.py | 52 - .../text_normalization/zh/taggers/money.py | 41 - .../zh/taggers/preprocessor.py | 46 - .../text_normalization/zh/taggers/time.py | 71 - .../zh/taggers/tokenize_and_classify.py | 92 - .../zh/taggers/whitelist.py | 38 - .../text_normalization/zh/utils.py | 64 - .../zh/verbalizers/__init__.py | 13 - .../zh/verbalizers/cardinal.py | 29 - .../text_normalization/zh/verbalizers/char.py | 27 - .../text_normalization/zh/verbalizers/date.py | 70 - .../zh/verbalizers/fraction.py | 31 - .../zh/verbalizers/math_symbol.py | 29 - .../zh/verbalizers/measure.py | 50 - .../zh/verbalizers/money.py | 31 - .../zh/verbalizers/postprocessor.py | 74 - .../text_normalization/zh/verbalizers/time.py | 93 - .../zh/verbalizers/verbalize.py | 62 - .../zh/verbalizers/verbalize_final.py | 49 - .../zh/verbalizers/whitelist.py | 29 - reinstall.sh | 3 - .../requirements_nemo_text_processing.txt | 3 +- .../export_wikihomograph_data_to_manifest.py | 4 +- .../text_to_speech/test_tts_tokenizers.py | 2 +- .../tts}/g2p/data/test_data_utils.py | 14 +- .../tts}/g2p/phoneme_dict/test_dict_de.txt | 0 .../tts}/g2p/phoneme_dict/test_dict_en.txt | 0 .../tts}/g2p/phoneme_dict/test_dict_es.txt | 0 .../tts}/g2p/test_modules.py | 4 +- tests/collections/tts/test_torch_tts.py | 2 +- tests/conftest.py | 15 - tests/nemo_text_processing/__init__.py | 13 - tests/nemo_text_processing/de/__init__.py | 13 - .../test_cases_cardinal.txt | 62 - .../test_cases_date.txt | 22 - .../test_cases_decimal.txt | 10 - .../test_cases_electronic.txt | 9 - .../test_cases_fraction.txt | 34 - .../test_cases_measure.txt | 28 - .../test_cases_money.txt | 23 - .../test_cases_ordinal.txt | 20 - .../test_cases_telephone.txt | 1 - .../test_cases_time.txt | 24 - .../test_cases_whitelist.txt | 6 - .../test_cases_word.txt | 49 - .../test_cases_cardinal.txt | 50 - .../test_cases_date.txt | 16 - .../test_cases_decimal.txt | 7 - .../test_cases_electronic.txt | 9 - .../test_cases_fraction.txt | 34 - .../test_cases_measure.txt | 29 - .../test_cases_money.txt | 21 - .../test_cases_normalize_with_audio.txt | 131 - .../test_cases_ordinal.txt | 19 - .../test_cases_telephone.txt | 4 - .../test_cases_time.txt | 26 - .../test_cases_whitelist.txt | 6 - .../test_cases_word.txt | 49 - .../nemo_text_processing/de/test_cardinal.py | 74 - tests/nemo_text_processing/de/test_date.py | 73 - tests/nemo_text_processing/de/test_decimal.py | 73 - .../de/test_electronic.py | 73 - .../nemo_text_processing/de/test_fraction.py | 74 - tests/nemo_text_processing/de/test_measure.py | 74 - tests/nemo_text_processing/de/test_money.py | 74 - .../de/test_normalization_with_audio.py | 52 - tests/nemo_text_processing/de/test_ordinal.py | 65 - ..._sparrowhawk_inverse_text_normalization.sh | 84 - .../de/test_sparrowhawk_normalization.sh | 84 - .../nemo_text_processing/de/test_telephone.py | 74 - tests/nemo_text_processing/de/test_time.py | 73 - .../nemo_text_processing/de/test_whitelist.py | 74 - tests/nemo_text_processing/de/test_word.py | 74 - tests/nemo_text_processing/en/__init__.py | 13 - .../test_cases_cardinal.txt | 28 - .../test_cases_date.txt | 34 - .../test_cases_decimal.txt | 63 - .../test_cases_electronic.txt | 25 - .../test_cases_measure.txt | 112 - .../test_cases_money.txt | 52 - .../test_cases_ordinal.txt | 34 - .../test_cases_telephone.txt | 18 - .../test_cases_time.txt | 29 - .../test_cases_whitelist.txt | 7 - .../test_cases_word.txt | 52 - .../test_cases_address.txt | 9 - .../test_cases_cardinal.txt | 17 - .../test_cases_date.txt | 53 - .../test_cases_decimal.txt | 12 - .../test_cases_electronic.txt | 29 - .../test_cases_fraction.txt | 16 - .../test_cases_math.txt | 4 - .../test_cases_measure.txt | 19 - .../test_cases_money.txt | 65 - .../test_cases_normalize_with_audio.txt | 165 - .../test_cases_ordinal.txt | 27 - .../test_cases_punctuation.txt | 64 - .../test_cases_punctuation_match_input.txt | 13 - .../test_cases_range.txt | 18 - .../test_cases_roman.txt | 4 - .../test_cases_serial.txt | 31 - .../test_cases_special_text.txt | 10 - .../test_cases_telephone.txt | 20 - .../test_cases_time.txt | 19 - .../test_cases_whitelist.txt | 6 - .../test_cases_word.txt | 38 - tests/nemo_text_processing/en/test_address.py | 58 - .../nemo_text_processing/en/test_cardinal.py | 72 - tests/nemo_text_processing/en/test_date.py | 96 - tests/nemo_text_processing/en/test_decimal.py | 72 - .../en/test_electronic.py | 70 - .../nemo_text_processing/en/test_fraction.py | 55 - tests/nemo_text_processing/en/test_math.py | 57 - tests/nemo_text_processing/en/test_measure.py | 71 - tests/nemo_text_processing/en/test_money.py | 73 - .../en/test_normalization_with_audio.py | 47 - tests/nemo_text_processing/en/test_ordinal.py | 73 - .../en/test_punctuation.py | 56 - tests/nemo_text_processing/en/test_range.py | 57 - tests/nemo_text_processing/en/test_roman.py | 57 - tests/nemo_text_processing/en/test_serial.py | 56 - ..._sparrowhawk_inverse_text_normalization.sh | 79 - .../en/test_sparrowhawk_normalization.sh | 115 - .../en/test_special_text.py | 58 - .../nemo_text_processing/en/test_telephone.py | 73 - .../en/test_text_split.py | 50 - tests/nemo_text_processing/en/test_time.py | 72 - .../nemo_text_processing/en/test_whitelist.py | 91 - tests/nemo_text_processing/en/test_word.py | 73 - tests/nemo_text_processing/es/__init__.py | 13 - .../test_cases_cardinal.txt | 50 - .../test_cases_date.txt | 6 - .../test_cases_decimal.txt | 29 - .../test_cases_electronic.txt | 13 - .../test_cases_measure.txt | 13 - .../test_cases_money.txt | 15 - .../test_cases_ordinal.txt | 29 - .../test_cases_telephone.txt | 6 - .../test_cases_time.txt | 19 - .../test_cases_whitelist.txt | 4 - .../test_cases_word.txt | 49 - .../test_cases_cardinal.txt | 69 - .../test_cases_date.txt | 13 - .../test_cases_decimal.txt | 19 - .../test_cases_electronic.txt | 12 - .../test_cases_fraction.txt | 72 - .../test_cases_measure.txt | 24 - .../test_cases_money.txt | 25 - .../test_cases_normalize_with_audio.txt | 162 - .../test_cases_ordinal.txt | 107 - .../test_cases_telephone.txt | 3 - .../test_cases_time.txt | 26 - .../test_cases_whitelist.txt | 3 - .../test_cases_word.txt | 48 - .../nemo_text_processing/es/test_cardinal.py | 76 - tests/nemo_text_processing/es/test_date.py | 75 - tests/nemo_text_processing/es/test_decimal.py | 73 - .../es/test_electronic.py | 73 - .../nemo_text_processing/es/test_fraction.py | 58 - tests/nemo_text_processing/es/test_measure.py | 74 - tests/nemo_text_processing/es/test_money.py | 74 - .../es/test_normalization_with_audio.py | 50 - tests/nemo_text_processing/es/test_ordinal.py | 73 - ..._sparrowhawk_inverse_text_normalization.sh | 64 - .../es/test_sparrowhawk_normalization.sh | 84 - .../nemo_text_processing/es/test_telephone.py | 74 - tests/nemo_text_processing/es/test_time.py | 73 - .../nemo_text_processing/es/test_whitelist.py | 72 - tests/nemo_text_processing/es/test_word.py | 72 - tests/nemo_text_processing/fr/__init__.py | 13 - .../test_cases_cardinal.txt | 106 - .../test_cases_date.txt | 6 - .../test_cases_decimal.txt | 15 - .../test_cases_electronic.txt | 10 - .../test_cases_fraction.txt | 30 - .../test_cases_measure.txt | 15 - .../test_cases_money.txt | 22 - .../test_cases_ordinal.txt | 23 - .../test_cases_telephone.txt | 4 - .../test_cases_time.txt | 18 - .../test_cases_whitelist.txt | 8 - .../test_cases_word.txt | 49 - .../nemo_text_processing/fr/test_cardinal.py | 43 - tests/nemo_text_processing/fr/test_date.py | 42 - tests/nemo_text_processing/fr/test_decimal.py | 42 - .../fr/test_electronic.py | 42 - .../nemo_text_processing/fr/test_fraction.py | 43 - tests/nemo_text_processing/fr/test_measure.py | 43 - tests/nemo_text_processing/fr/test_money.py | 43 - tests/nemo_text_processing/fr/test_ordinal.py | 43 - ..._sparrowhawk_inverse_text_normalization.sh | 84 - .../nemo_text_processing/fr/test_telephone.py | 43 - tests/nemo_text_processing/fr/test_time.py | 42 - .../nemo_text_processing/fr/test_whitelist.py | 43 - tests/nemo_text_processing/fr/test_word.py | 43 - tests/nemo_text_processing/pt/__init__.py | 13 - .../test_cases_cardinal.txt | 70 - .../test_cases_date.txt | 15 - .../test_cases_decimal.txt | 26 - .../test_cases_electronic.txt | 13 - .../test_cases_measure.txt | 12 - .../test_cases_money.txt | 25 - .../test_cases_ordinal.txt | 19 - .../test_cases_telephone.txt | 27 - .../test_cases_time.txt | 19 - .../test_cases_whitelist.txt | 6 - .../test_cases_word.txt | 49 - .../nemo_text_processing/pt/test_cardinal.py | 43 - tests/nemo_text_processing/pt/test_date.py | 42 - tests/nemo_text_processing/pt/test_decimal.py | 42 - .../pt/test_electronic.py | 42 - tests/nemo_text_processing/pt/test_measure.py | 43 - tests/nemo_text_processing/pt/test_money.py | 43 - tests/nemo_text_processing/pt/test_ordinal.py | 43 - ..._sparrowhawk_inverse_text_normalization.sh | 84 - .../nemo_text_processing/pt/test_telephone.py | 43 - tests/nemo_text_processing/pt/test_time.py | 42 - .../nemo_text_processing/pt/test_whitelist.py | 43 - tests/nemo_text_processing/pt/test_word.py | 43 - tests/nemo_text_processing/ru/__init__.py | 13 - .../test_cases_cardinal.txt | 30 - .../test_cases_date.txt | 15 - .../test_cases_decimal.txt | 16 - .../test_cases_electronic.txt | 4 - .../test_cases_measure.txt | 16 - .../test_cases_money.txt | 9 - .../test_cases_ordinal.txt | 15 - .../test_cases_ordinal_hard.txt | 57 - .../test_cases_telephone.txt | 1 - .../test_cases_time.txt | 9 - .../test_cases_whitelist.txt | 4 - .../test_cases_word.txt | 50 - .../ru/data_text_normalization/__init__.py | 13 - .../test_cases_cardinal.txt | 28 - ...st_cases_cardinal_normalize_with_audio.txt | 51 - .../test_cases_date.txt | 6 - .../test_cases_decimal.txt | 17 - .../test_cases_electronic.txt | 4 - .../test_cases_measure.txt | 13 - .../test_cases_money.txt | 9 - .../test_cases_ordinal.txt | 75 - .../test_cases_telephone.txt | 1 - .../test_cases_time.txt | 9 - .../test_cases_whitelist.txt | 4 - .../test_cases_word.txt | 3 - .../ru/test_ru_inverse_normalization.py | 147 - .../ru/test_ru_normalization.py | 152 - ..._sparrowhawk_inverse_text_normalization.sh | 79 - tests/nemo_text_processing/utils.py | 66 - tests/nemo_text_processing/vi/__init__.py | 13 - .../test_cases_cardinal.txt | 93 - .../test_cases_date.txt | 16 - .../test_cases_decimal.txt | 25 - .../test_cases_electronic.txt | 10 - .../test_cases_fraction.txt | 14 - .../test_cases_measure.txt | 22 - .../test_cases_money.txt | 17 - .../test_cases_ordinal.txt | 12 - .../test_cases_telephone.txt | 4 - .../test_cases_time.txt | 22 - .../test_cases_whitelist.txt | 1 - .../test_cases_word.txt | 17 - .../nemo_text_processing/vi/test_cardinal.py | 43 - tests/nemo_text_processing/vi/test_date.py | 42 - tests/nemo_text_processing/vi/test_decimal.py | 42 - .../vi/test_electronic.py | 42 - .../nemo_text_processing/vi/test_fraction.py | 43 - tests/nemo_text_processing/vi/test_measure.py | 43 - tests/nemo_text_processing/vi/test_money.py | 43 - tests/nemo_text_processing/vi/test_ordinal.py | 43 - ..._sparrowhawk_inverse_text_normalization.sh | 79 - .../nemo_text_processing/vi/test_telephone.py | 43 - tests/nemo_text_processing/vi/test_time.py | 42 - .../nemo_text_processing/vi/test_whitelist.py | 43 - tests/nemo_text_processing/vi/test_word.py | 43 - tests/nemo_text_processing/zh/__init__.py | 13 - .../test_cases_char.txt | 2 - .../test_cases_date.txt | 5 - .../test_cases_fraction.txt | 3 - .../test_cases_math.txt | 3 - .../test_cases_measure.txt | 6 - .../test_cases_money.txt | 7 - .../test_cases_preprocess.txt | 1 - .../test_cases_time.txt | 4 - tests/nemo_text_processing/zh/test_char.py | 30 - tests/nemo_text_processing/zh/test_date.py | 30 - .../nemo_text_processing/zh/test_fraction.py | 30 - tests/nemo_text_processing/zh/test_math.py | 30 - tests/nemo_text_processing/zh/test_measure.py | 30 - tests/nemo_text_processing/zh/test_money.py | 30 - .../zh/test_preprocess.py | 30 - .../zh/test_sparrowhawk_normalization.sh | 60 - tests/nemo_text_processing/zh/test_time.py | 30 - tools/ctc_segmentation/requirements.txt | 1 + tools/text_processing_deployment/Dockerfile | 37 - tools/text_processing_deployment/README.rst | 10 - .../docker/build.sh | 18 - .../docker/launch.sh | 45 - .../export_grammars.sh | 80 - .../pynini_export.py | 173 - .../ITN_with_Thutmose_Tagger.ipynb | 1140 -- .../Text_(Inverse)_Normalization.ipynb | 468 - tutorials/text_processing/WFST_Tutorial.ipynb | 7054 -------- .../text_processing/images/audio_based_tn.png | Bin 88329 -> 0 bytes tutorials/text_processing/images/cent.PNG | Bin 10352 -> 0 bytes .../text_processing/images/cent_to_100.PNG | Bin 11898 -> 0 bytes .../text_processing/images/cent_vingt_bad.PNG | Bin 13247 -> 0 bytes .../images/cent_vingt_good.PNG | Bin 31222 -> 0 bytes .../images/cent_vingt_to_120.PNG | Bin 10937 -> 0 bytes .../text_processing/images/deployment.png | Bin 154145 -> 0 bytes .../text_processing/images/dix_to_digits.PNG | Bin 17537 -> 0 bytes .../images/dix_to_digits_with_insert.PNG | Bin 27468 -> 0 bytes tutorials/text_processing/images/pipeline.png | Bin 225984 -> 0 bytes .../text_processing/images/romanization.PNG | Bin 36700 -> 0 bytes .../text_processing/images/task_overview.png | Bin 49787 -> 0 bytes .../thutmose_tagger_alignment_bottom.png | Bin 18489 -> 0 bytes .../images/thutmose_tagger_alignment_top.png | Bin 13009 -> 0 bytes .../images/thutmose_tagger_architecture.png | Bin 47623 -> 0 bytes .../thutmose_tagger_final_alignment.png | Bin 18211 -> 0 bytes .../images/thutmose_tagger_tag_vocabulary.png | Bin 6134 -> 0 bytes 1088 files changed, 143 insertions(+), 95653 deletions(-) rename examples/{text_processing => tts}/g2p/README.md (100%) rename examples/{text_processing => tts}/g2p/conf/g2p_conformer_ctc.yaml (95%) rename examples/{text_processing => tts}/g2p/conf/heteronym_classification.yaml (89%) rename examples/{text_processing => tts}/g2p/conf/t5_g2p.yaml (92%) rename examples/{text_processing => tts}/g2p/g2p_inference.py (98%) rename examples/{text_processing => tts}/g2p/g2p_train_and_evaluate.py (97%) rename examples/{text_processing => tts}/g2p/heteronym_classification_inference.py (99%) rename examples/{text_processing => tts}/g2p/heteronym_classification_train_and_evaluate.py (98%) rename examples/{text_processing => tts}/g2p/utils.py (96%) rename {nemo_text_processing/text_normalization/es => nemo/collections/tts/g2p/data}/__init__.py (85%) rename {nemo_text_processing => nemo/collections/tts}/g2p/data/ctc_g2p.py (99%) rename {nemo_text_processing => nemo/collections/tts}/g2p/data/data_utils.py (100%) rename {nemo_text_processing => nemo/collections/tts}/g2p/data/heteronym_classification_data.py (100%) rename {nemo_text_processing => nemo/collections/tts}/g2p/data/t5_g2p.py (98%) rename {nemo_text_processing => nemo/collections/tts}/g2p/models/__init__.py (84%) rename {nemo_text_processing => nemo/collections/tts}/g2p/models/ctc_g2p.py (99%) rename {nemo_text_processing => nemo/collections/tts}/g2p/models/g2p_model.py (100%) rename {nemo_text_processing => nemo/collections/tts}/g2p/models/heteronym_classification.py (99%) rename {nemo_text_processing => nemo/collections/tts}/g2p/models/t5_g2p.py (98%) rename {nemo_text_processing => nemo/collections/tts}/g2p/modules.py (98%) delete mode 100644 nemo_text_processing/README.md delete mode 100644 nemo_text_processing/__init__.py delete mode 100644 nemo_text_processing/fst_alignment/README.md delete mode 100644 nemo_text_processing/fst_alignment/alignment.cpp delete mode 100644 nemo_text_processing/fst_alignment/alignment.py delete mode 100644 nemo_text_processing/g2p/__init__.py delete mode 100644 nemo_text_processing/g2p/data/__init__.py delete mode 100644 nemo_text_processing/hybrid/model_utils.py delete mode 100644 nemo_text_processing/hybrid/utils.py delete mode 100644 nemo_text_processing/hybrid/wfst_lm_rescoring.py delete mode 100644 nemo_text_processing/install_pynini.sh delete mode 100644 nemo_text_processing/inverse_text_normalization/README.md delete mode 100644 nemo_text_processing/inverse_text_normalization/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/de/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/de/taggers/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/de/taggers/cardinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/de/taggers/date.py delete mode 100644 nemo_text_processing/inverse_text_normalization/de/taggers/decimal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/de/taggers/electronic.py delete mode 100644 nemo_text_processing/inverse_text_normalization/de/taggers/fraction.py delete mode 100644 nemo_text_processing/inverse_text_normalization/de/taggers/measure.py delete mode 100644 nemo_text_processing/inverse_text_normalization/de/taggers/money.py delete mode 100644 nemo_text_processing/inverse_text_normalization/de/taggers/ordinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/de/taggers/telephone.py delete mode 100644 nemo_text_processing/inverse_text_normalization/de/taggers/time.py delete mode 100644 nemo_text_processing/inverse_text_normalization/de/taggers/tokenize_and_classify.py delete mode 100644 nemo_text_processing/inverse_text_normalization/de/taggers/whitelist.py delete mode 100644 nemo_text_processing/inverse_text_normalization/de/verbalizers/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/de/verbalizers/cardinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/de/verbalizers/decimal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/de/verbalizers/measure.py delete mode 100644 nemo_text_processing/inverse_text_normalization/de/verbalizers/money.py delete mode 100644 nemo_text_processing/inverse_text_normalization/de/verbalizers/time.py delete mode 100644 nemo_text_processing/inverse_text_normalization/de/verbalizers/verbalize.py delete mode 100644 nemo_text_processing/inverse_text_normalization/de/verbalizers/verbalize_final.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/clean_eval_data.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/data/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/data/currency.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/en/data/electronic/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/data/electronic/domain.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/en/data/electronic/server_name.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/en/data/electronic/symbols.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/en/data/magnitudes.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/en/data/measurements.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/en/data/months.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/en/data/numbers/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/data/numbers/digit.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/en/data/numbers/hundred.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/en/data/numbers/teen.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/en/data/numbers/thousands.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/en/data/numbers/ties.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/en/data/numbers/zero.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/en/data/ordinals/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/data/ordinals/digit.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/en/data/ordinals/teen.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/en/data/time/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/data/time/minute_to.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/en/data/time/time_suffix.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/en/data/time/time_zone.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/en/data/time/to_hour.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/en/data/whitelist.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/en/taggers/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/taggers/cardinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/taggers/date.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/taggers/decimal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/taggers/electronic.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/taggers/fraction.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/taggers/measure.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/taggers/money.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/taggers/ordinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/taggers/punctuation.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/taggers/telephone.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/taggers/time.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/taggers/tokenize_and_classify.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/taggers/whitelist.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/taggers/word.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/utils.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/verbalizers/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/verbalizers/cardinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/verbalizers/date.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/verbalizers/decimal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/verbalizers/electronic.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/verbalizers/fraction.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/verbalizers/measure.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/verbalizers/money.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/verbalizers/ordinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/verbalizers/telephone.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/verbalizers/time.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/verbalizers/verbalize.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/verbalizers/verbalize_final.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/verbalizers/whitelist.py delete mode 100644 nemo_text_processing/inverse_text_normalization/en/verbalizers/word.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/currency_plural.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/currency_singular.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/electronic/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/electronic/domain.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/electronic/server_name.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/electronic/symbols.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/measurements_plural.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/measurements_singular.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/months.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/numbers/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/numbers/digit.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/numbers/hundreds.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/numbers/teen.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/numbers/ties.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/numbers/twenties.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/numbers/zero.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/ordinals/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/ordinals/digit.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/ordinals/hundreds.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/ordinals/teen.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/ordinals/ties.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/ordinals/twenties.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/time/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/time/time_suffix.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/time/time_to.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/es/data/whitelist.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/es/taggers/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/taggers/cardinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/taggers/date.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/taggers/decimal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/taggers/electronic.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/taggers/measure.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/taggers/money.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/taggers/ordinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/taggers/punctuation.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/taggers/telephone.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/taggers/time.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/taggers/tokenize_and_classify.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/taggers/whitelist.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/taggers/word.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/utils.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/verbalizers/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/verbalizers/cardinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/verbalizers/date.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/verbalizers/decimal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/verbalizers/electronic.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/verbalizers/measure.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/verbalizers/money.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/verbalizers/ordinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/verbalizers/telephone.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/verbalizers/time.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/verbalizers/verbalize.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/verbalizers/verbalize_final.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/verbalizers/whitelist.py delete mode 100644 nemo_text_processing/inverse_text_normalization/es/verbalizers/word.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/README.md delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/electronic/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/electronic/domain.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/electronic/server_name.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/electronic/symbols.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/fractions.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/measurements/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/measurements/magnitudes.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/measurements/measurements.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/money/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/money/currency_major.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/money/currency_minor.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/months.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/numbers/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/numbers/digit.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/numbers/hundreds.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/numbers/teen.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/numbers/thousands.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/numbers/ties.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/numbers/ties_unique.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/numbers/zero.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/ordinals/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/ordinals/digits_root_change.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/ordinals/firsts.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/ordinals/key_nouns.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/ordinals/second.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/roman/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/roman/digits_large.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/roman/hundreds_large.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/roman/ties_large.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/suppletive.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/time/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/time/hour_to_night.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/time/hours.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/time/hours_to.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/time/minutes.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/time/minutes_to.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/time/time_suffix_am.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/time/time_suffix_pm.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/data/whitelist.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/graph_utils.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/taggers/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/taggers/cardinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/taggers/date.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/taggers/decimal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/taggers/electronic.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/taggers/fraction.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/taggers/measure.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/taggers/money.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/taggers/ordinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/taggers/punctuation.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/taggers/telephone.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/taggers/time.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/taggers/tokenize_and_classify.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/taggers/whitelist.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/taggers/word.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/utils.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/verbalizers/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/verbalizers/cardinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/verbalizers/date.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/verbalizers/decimal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/verbalizers/electronic.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/verbalizers/fraction.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/verbalizers/measure.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/verbalizers/money.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/verbalizers/ordinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/verbalizers/telephone.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/verbalizers/time.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/verbalizers/verbalize.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/verbalizers/verbalize_final.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/verbalizers/whitelist.py delete mode 100644 nemo_text_processing/inverse_text_normalization/fr/verbalizers/word.py delete mode 100644 nemo_text_processing/inverse_text_normalization/inverse_normalize.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/currency_plural.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/currency_singular.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/electronic/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/electronic/domain.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/electronic/server_name.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/electronic/symbols.tsv delete mode 100755 nemo_text_processing/inverse_text_normalization/pt/data/measurements_plural.tsv delete mode 100755 nemo_text_processing/inverse_text_normalization/pt/data/measurements_singular.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/months.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/numbers/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/numbers/digit.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/numbers/hundreds.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/numbers/onehundred.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/numbers/teen.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/numbers/ties.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/numbers/twenties.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/numbers/zero.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/ordinals/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/ordinals/digit.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/ordinals/hundreds.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/ordinals/ties.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/time/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/time/hour_to_am.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/time/hour_to_pm.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/time/hours_to.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/time/minutes_to.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/time/time_suffix_am.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/time/time_suffix_pm.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/data/whitelist.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/taggers/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/taggers/cardinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/taggers/date.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/taggers/decimal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/taggers/electronic.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/taggers/measure.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/taggers/money.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/taggers/ordinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/taggers/punctuation.py delete mode 100755 nemo_text_processing/inverse_text_normalization/pt/taggers/telephone.py delete mode 100755 nemo_text_processing/inverse_text_normalization/pt/taggers/time.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/taggers/tokenize_and_classify.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/taggers/whitelist.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/taggers/word.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/utils.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/verbalizers/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/verbalizers/cardinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/verbalizers/date.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/verbalizers/decimal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/verbalizers/electronic.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/verbalizers/measure.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/verbalizers/money.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/verbalizers/ordinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/verbalizers/telephone.py delete mode 100755 nemo_text_processing/inverse_text_normalization/pt/verbalizers/time.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/verbalizers/verbalize.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/verbalizers/verbalize_final.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/verbalizers/whitelist.py delete mode 100644 nemo_text_processing/inverse_text_normalization/pt/verbalizers/word.py delete mode 100644 nemo_text_processing/inverse_text_normalization/ru/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/ru/taggers/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/ru/taggers/cardinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/ru/taggers/date.py delete mode 100644 nemo_text_processing/inverse_text_normalization/ru/taggers/decimals.py delete mode 100644 nemo_text_processing/inverse_text_normalization/ru/taggers/electronic.py delete mode 100644 nemo_text_processing/inverse_text_normalization/ru/taggers/measure.py delete mode 100644 nemo_text_processing/inverse_text_normalization/ru/taggers/money.py delete mode 100644 nemo_text_processing/inverse_text_normalization/ru/taggers/ordinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/ru/taggers/telephone.py delete mode 100644 nemo_text_processing/inverse_text_normalization/ru/taggers/time.py delete mode 100644 nemo_text_processing/inverse_text_normalization/ru/taggers/tokenize_and_classify.py delete mode 100644 nemo_text_processing/inverse_text_normalization/ru/taggers/whitelist.py delete mode 100644 nemo_text_processing/inverse_text_normalization/ru/verbalizers/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/ru/verbalizers/cardinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/ru/verbalizers/date.py delete mode 100644 nemo_text_processing/inverse_text_normalization/ru/verbalizers/decimal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/ru/verbalizers/electronic.py delete mode 100644 nemo_text_processing/inverse_text_normalization/ru/verbalizers/measure.py delete mode 100644 nemo_text_processing/inverse_text_normalization/ru/verbalizers/money.py delete mode 100644 nemo_text_processing/inverse_text_normalization/ru/verbalizers/ordinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/ru/verbalizers/telephone.py delete mode 100644 nemo_text_processing/inverse_text_normalization/ru/verbalizers/time.py delete mode 100644 nemo_text_processing/inverse_text_normalization/ru/verbalizers/verbalize.py delete mode 100644 nemo_text_processing/inverse_text_normalization/ru/verbalizers/verbalize_final.py delete mode 100644 nemo_text_processing/inverse_text_normalization/run_evaluate.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/currency.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/electronic/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/electronic/domain.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/electronic/server_name.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/electronic/symbols.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/magnitudes.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/math/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/math/symbols.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/measurements.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/months.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/numbers/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/numbers/digit.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/numbers/hundred.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/numbers/teen.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/numbers/thousands.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/numbers/ties.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/numbers/zero.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/ordinals/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/ordinals/digit.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/time/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/time/hours.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/time/hours_to.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/time/hours_to_night.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/time/minutes.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/time/minutes_to.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/time/time_suffix.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/time/time_zone.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/data/whitelist.tsv delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/graph_utils.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/taggers/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/taggers/cardinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/taggers/date.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/taggers/decimal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/taggers/electronic.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/taggers/fraction.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/taggers/measure.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/taggers/money.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/taggers/ordinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/taggers/punctuation.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/taggers/telephone.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/taggers/time.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/taggers/tokenize_and_classify.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/taggers/whitelist.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/taggers/word.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/utils.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/verbalizers/__init__.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/verbalizers/cardinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/verbalizers/date.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/verbalizers/decimal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/verbalizers/electronic.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/verbalizers/fraction.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/verbalizers/measure.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/verbalizers/money.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/verbalizers/ordinal.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/verbalizers/telephone.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/verbalizers/time.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/verbalizers/verbalize.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/verbalizers/verbalize_final.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/verbalizers/whitelist.py delete mode 100644 nemo_text_processing/inverse_text_normalization/vi/verbalizers/word.py delete mode 100644 nemo_text_processing/text_normalization/README.md delete mode 100644 nemo_text_processing/text_normalization/__init__.py delete mode 100644 nemo_text_processing/text_normalization/data_loader_utils.py delete mode 100644 nemo_text_processing/text_normalization/de/__init__.py delete mode 100644 nemo_text_processing/text_normalization/de/data/__init__.py delete mode 100644 nemo_text_processing/text_normalization/de/data/electronic/__init__.py delete mode 100644 nemo_text_processing/text_normalization/de/data/electronic/domain.tsv delete mode 100644 nemo_text_processing/text_normalization/de/data/electronic/server_name.tsv delete mode 100644 nemo_text_processing/text_normalization/de/data/electronic/symbols.tsv delete mode 100644 nemo_text_processing/text_normalization/de/data/fractions.tsv delete mode 100644 nemo_text_processing/text_normalization/de/data/measure/__init__.py delete mode 100644 nemo_text_processing/text_normalization/de/data/measure/measurements.tsv delete mode 100644 nemo_text_processing/text_normalization/de/data/measure/suppletive.tsv delete mode 100644 nemo_text_processing/text_normalization/de/data/money/__init__.py delete mode 100644 nemo_text_processing/text_normalization/de/data/money/currency.tsv delete mode 100644 nemo_text_processing/text_normalization/de/data/money/currency_minor_plural.tsv delete mode 100644 nemo_text_processing/text_normalization/de/data/money/currency_minor_singular.tsv delete mode 100644 nemo_text_processing/text_normalization/de/data/months/__init__.py delete mode 100644 nemo_text_processing/text_normalization/de/data/months/abbr_to_name.tsv delete mode 100644 nemo_text_processing/text_normalization/de/data/months/numbers.tsv delete mode 100644 nemo_text_processing/text_normalization/de/data/numbers/__init__.py delete mode 100644 nemo_text_processing/text_normalization/de/data/numbers/digit.tsv delete mode 100644 nemo_text_processing/text_normalization/de/data/numbers/ones.tsv delete mode 100644 nemo_text_processing/text_normalization/de/data/numbers/quantities.tsv delete mode 100644 nemo_text_processing/text_normalization/de/data/numbers/teen.tsv delete mode 100644 nemo_text_processing/text_normalization/de/data/numbers/ties.tsv delete mode 100644 nemo_text_processing/text_normalization/de/data/numbers/zero.tsv delete mode 100644 nemo_text_processing/text_normalization/de/data/ordinals/__init__.py delete mode 100644 nemo_text_processing/text_normalization/de/data/ordinals/digit.tsv delete mode 100644 nemo_text_processing/text_normalization/de/data/ordinals/thousands.tsv delete mode 100644 nemo_text_processing/text_normalization/de/data/ordinals/ties.tsv delete mode 100644 nemo_text_processing/text_normalization/de/data/time/__init__.py delete mode 100644 nemo_text_processing/text_normalization/de/data/time/hour_to.tsv delete mode 100644 nemo_text_processing/text_normalization/de/data/time/hour_to_night.tsv delete mode 100644 nemo_text_processing/text_normalization/de/data/time/minute_to.tsv delete mode 100644 nemo_text_processing/text_normalization/de/data/time/time_zone.tsv delete mode 100644 nemo_text_processing/text_normalization/de/data/whitelist.tsv delete mode 100644 nemo_text_processing/text_normalization/de/taggers/__init__.py delete mode 100644 nemo_text_processing/text_normalization/de/taggers/cardinal.py delete mode 100644 nemo_text_processing/text_normalization/de/taggers/date.py delete mode 100644 nemo_text_processing/text_normalization/de/taggers/decimal.py delete mode 100644 nemo_text_processing/text_normalization/de/taggers/electronic.py delete mode 100644 nemo_text_processing/text_normalization/de/taggers/fraction.py delete mode 100644 nemo_text_processing/text_normalization/de/taggers/measure.py delete mode 100644 nemo_text_processing/text_normalization/de/taggers/money.py delete mode 100644 nemo_text_processing/text_normalization/de/taggers/ordinal.py delete mode 100644 nemo_text_processing/text_normalization/de/taggers/telephone.py delete mode 100644 nemo_text_processing/text_normalization/de/taggers/time.py delete mode 100644 nemo_text_processing/text_normalization/de/taggers/tokenize_and_classify.py delete mode 100644 nemo_text_processing/text_normalization/de/taggers/whitelist.py delete mode 100644 nemo_text_processing/text_normalization/de/taggers/word.py delete mode 100644 nemo_text_processing/text_normalization/de/utils.py delete mode 100644 nemo_text_processing/text_normalization/de/verbalizers/__init__.py delete mode 100644 nemo_text_processing/text_normalization/de/verbalizers/cardinal.py delete mode 100644 nemo_text_processing/text_normalization/de/verbalizers/date.py delete mode 100644 nemo_text_processing/text_normalization/de/verbalizers/decimal.py delete mode 100644 nemo_text_processing/text_normalization/de/verbalizers/electronic.py delete mode 100644 nemo_text_processing/text_normalization/de/verbalizers/fraction.py delete mode 100644 nemo_text_processing/text_normalization/de/verbalizers/measure.py delete mode 100644 nemo_text_processing/text_normalization/de/verbalizers/money.py delete mode 100644 nemo_text_processing/text_normalization/de/verbalizers/ordinal.py delete mode 100644 nemo_text_processing/text_normalization/de/verbalizers/telephone.py delete mode 100644 nemo_text_processing/text_normalization/de/verbalizers/time.py delete mode 100644 nemo_text_processing/text_normalization/de/verbalizers/verbalize.py delete mode 100644 nemo_text_processing/text_normalization/de/verbalizers/verbalize_final.py delete mode 100644 nemo_text_processing/text_normalization/en/__init__.py delete mode 100644 nemo_text_processing/text_normalization/en/clean_eval_data.py delete mode 100644 nemo_text_processing/text_normalization/en/data/__init__.py delete mode 100644 nemo_text_processing/text_normalization/en/data/address/__init__.py delete mode 100644 nemo_text_processing/text_normalization/en/data/address/address_word.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/address/state.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/date/__init__.py delete mode 100644 nemo_text_processing/text_normalization/en/data/date/day.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/date/month_abbr.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/date/month_name.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/date/month_number.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/date/year_suffix.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/electronic/__init__.py delete mode 100644 nemo_text_processing/text_normalization/en/data/electronic/domain.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/electronic/symbol.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/measure/__init__.py delete mode 100644 nemo_text_processing/text_normalization/en/data/measure/math_operation.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/measure/unit.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/measure/unit_alternatives.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/money/__init__.py delete mode 100644 nemo_text_processing/text_normalization/en/data/money/currency_major.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/money/currency_minor_plural.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/money/currency_minor_singular.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/money/per_unit.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/number/__init__.py delete mode 100755 nemo_text_processing/text_normalization/en/data/number/cardinal_number_name.far delete mode 100644 nemo_text_processing/text_normalization/en/data/number/digit.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/number/fraction.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/number/hundred.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/number/quantity_abbr.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/number/teen.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/number/thousand.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/number/ty.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/number/zero.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/ordinal/__init__.py delete mode 100644 nemo_text_processing/text_normalization/en/data/ordinal/digit.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/ordinal/teen.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/roman/README.md delete mode 100644 nemo_text_processing/text_normalization/en/data/roman/__init__.py delete mode 100644 nemo_text_processing/text_normalization/en/data/roman/female.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/roman/key_word.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/roman/male.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/roman/roman_to_spoken.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/suppletive.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/telephone/__init__.py delete mode 100644 nemo_text_processing/text_normalization/en/data/telephone/ip_prompt.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/telephone/ssn_prompt.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/telephone/telephone_prompt.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/time/__init__.py delete mode 100644 nemo_text_processing/text_normalization/en/data/time/suffix.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/time/zone.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/whitelist/__init__.py delete mode 100644 nemo_text_processing/text_normalization/en/data/whitelist/alternatives.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/whitelist/alternatives_all_format.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/whitelist/asr.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/whitelist/ipa_symbols.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/whitelist/symbol.tsv delete mode 100644 nemo_text_processing/text_normalization/en/data/whitelist/tts.tsv delete mode 100644 nemo_text_processing/text_normalization/en/graph_utils.py delete mode 100644 nemo_text_processing/text_normalization/en/taggers/__init__.py delete mode 100644 nemo_text_processing/text_normalization/en/taggers/abbreviation.py delete mode 100644 nemo_text_processing/text_normalization/en/taggers/cardinal.py delete mode 100644 nemo_text_processing/text_normalization/en/taggers/date.py delete mode 100644 nemo_text_processing/text_normalization/en/taggers/decimal.py delete mode 100644 nemo_text_processing/text_normalization/en/taggers/electronic.py delete mode 100644 nemo_text_processing/text_normalization/en/taggers/fraction.py delete mode 100644 nemo_text_processing/text_normalization/en/taggers/measure.py delete mode 100644 nemo_text_processing/text_normalization/en/taggers/money.py delete mode 100644 nemo_text_processing/text_normalization/en/taggers/ordinal.py delete mode 100644 nemo_text_processing/text_normalization/en/taggers/punctuation.py delete mode 100644 nemo_text_processing/text_normalization/en/taggers/range.py delete mode 100644 nemo_text_processing/text_normalization/en/taggers/roman.py delete mode 100644 nemo_text_processing/text_normalization/en/taggers/serial.py delete mode 100644 nemo_text_processing/text_normalization/en/taggers/telephone.py delete mode 100644 nemo_text_processing/text_normalization/en/taggers/time.py delete mode 100644 nemo_text_processing/text_normalization/en/taggers/tokenize_and_classify.py delete mode 100644 nemo_text_processing/text_normalization/en/taggers/tokenize_and_classify_lm.py delete mode 100644 nemo_text_processing/text_normalization/en/taggers/tokenize_and_classify_with_audio.py delete mode 100644 nemo_text_processing/text_normalization/en/taggers/whitelist.py delete mode 100644 nemo_text_processing/text_normalization/en/taggers/word.py delete mode 100644 nemo_text_processing/text_normalization/en/utils.py delete mode 100644 nemo_text_processing/text_normalization/en/verbalizers/__init__.py delete mode 100644 nemo_text_processing/text_normalization/en/verbalizers/abbreviation.py delete mode 100644 nemo_text_processing/text_normalization/en/verbalizers/cardinal.py delete mode 100644 nemo_text_processing/text_normalization/en/verbalizers/date.py delete mode 100644 nemo_text_processing/text_normalization/en/verbalizers/decimal.py delete mode 100644 nemo_text_processing/text_normalization/en/verbalizers/electronic.py delete mode 100644 nemo_text_processing/text_normalization/en/verbalizers/fraction.py delete mode 100644 nemo_text_processing/text_normalization/en/verbalizers/measure.py delete mode 100644 nemo_text_processing/text_normalization/en/verbalizers/money.py delete mode 100644 nemo_text_processing/text_normalization/en/verbalizers/ordinal.py delete mode 100644 nemo_text_processing/text_normalization/en/verbalizers/post_processing.py delete mode 100644 nemo_text_processing/text_normalization/en/verbalizers/roman.py delete mode 100644 nemo_text_processing/text_normalization/en/verbalizers/telephone.py delete mode 100644 nemo_text_processing/text_normalization/en/verbalizers/time.py delete mode 100644 nemo_text_processing/text_normalization/en/verbalizers/verbalize.py delete mode 100644 nemo_text_processing/text_normalization/en/verbalizers/verbalize_final.py delete mode 100644 nemo_text_processing/text_normalization/en/verbalizers/whitelist.py delete mode 100644 nemo_text_processing/text_normalization/en/verbalizers/word.py delete mode 100644 nemo_text_processing/text_normalization/es/README.md delete mode 100644 nemo_text_processing/text_normalization/es/data/__init__.py delete mode 100644 nemo_text_processing/text_normalization/es/data/dates/__init__.py delete mode 100644 nemo_text_processing/text_normalization/es/data/dates/months.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/electronic/__init__.py delete mode 100644 nemo_text_processing/text_normalization/es/data/electronic/domain.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/electronic/server_name.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/electronic/symbols.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/fractions/__init__.py delete mode 100644 nemo_text_processing/text_normalization/es/data/fractions/ordinal_exceptions.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/fractions/powers_of_ten.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/fractions/powers_of_ten_fem.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/measures/__init__.py delete mode 100644 nemo_text_processing/text_normalization/es/data/measures/measurements.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/measures/measurements_plural_fem.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/measures/measurements_plural_masc.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/money/__init__.py delete mode 100644 nemo_text_processing/text_normalization/es/data/money/currency_major.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/money/currency_minor.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/money/currency_plural_fem.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/money/currency_plural_masc.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/numbers/__init__.py delete mode 100644 nemo_text_processing/text_normalization/es/data/numbers/digit.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/numbers/hundreds.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/numbers/quantities.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/numbers/teen.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/numbers/ties.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/numbers/twenties.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/numbers/zero.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/ordinals/__init__.py delete mode 100644 nemo_text_processing/text_normalization/es/data/ordinals/digit.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/ordinals/hundreds.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/ordinals/teen.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/ordinals/ties.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/ordinals/twenties.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/roman/__init__.py delete mode 100644 nemo_text_processing/text_normalization/es/data/roman/digit.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/roman/hundreds.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/roman/ties.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/time/__init__.py delete mode 100644 nemo_text_processing/text_normalization/es/data/time/afternoon_times.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/time/alt_minutes.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/time/evening_times.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/time/hour_to_12.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/time/hour_to_24.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/time/hour_to_night.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/time/minute_to.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/time/morning_times.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/time/time_suffix.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/time/time_zone.tsv delete mode 100644 nemo_text_processing/text_normalization/es/data/whitelist.tsv delete mode 100644 nemo_text_processing/text_normalization/es/graph_utils.py delete mode 100644 nemo_text_processing/text_normalization/es/taggers/__init__.py delete mode 100644 nemo_text_processing/text_normalization/es/taggers/cardinal.py delete mode 100644 nemo_text_processing/text_normalization/es/taggers/date.py delete mode 100644 nemo_text_processing/text_normalization/es/taggers/decimals.py delete mode 100644 nemo_text_processing/text_normalization/es/taggers/electronic.py delete mode 100644 nemo_text_processing/text_normalization/es/taggers/fraction.py delete mode 100644 nemo_text_processing/text_normalization/es/taggers/measure.py delete mode 100644 nemo_text_processing/text_normalization/es/taggers/money.py delete mode 100644 nemo_text_processing/text_normalization/es/taggers/ordinal.py delete mode 100644 nemo_text_processing/text_normalization/es/taggers/telephone.py delete mode 100644 nemo_text_processing/text_normalization/es/taggers/time.py delete mode 100644 nemo_text_processing/text_normalization/es/taggers/tokenize_and_classify.py delete mode 100644 nemo_text_processing/text_normalization/es/taggers/whitelist.py delete mode 100644 nemo_text_processing/text_normalization/es/taggers/word.py delete mode 100644 nemo_text_processing/text_normalization/es/utils.py delete mode 100644 nemo_text_processing/text_normalization/es/verbalizers/__init__.py delete mode 100644 nemo_text_processing/text_normalization/es/verbalizers/cardinal.py delete mode 100644 nemo_text_processing/text_normalization/es/verbalizers/date.py delete mode 100644 nemo_text_processing/text_normalization/es/verbalizers/decimals.py delete mode 100644 nemo_text_processing/text_normalization/es/verbalizers/electronic.py delete mode 100644 nemo_text_processing/text_normalization/es/verbalizers/fraction.py delete mode 100644 nemo_text_processing/text_normalization/es/verbalizers/measure.py delete mode 100644 nemo_text_processing/text_normalization/es/verbalizers/money.py delete mode 100644 nemo_text_processing/text_normalization/es/verbalizers/ordinal.py delete mode 100644 nemo_text_processing/text_normalization/es/verbalizers/telephone.py delete mode 100644 nemo_text_processing/text_normalization/es/verbalizers/time.py delete mode 100644 nemo_text_processing/text_normalization/es/verbalizers/verbalize.py delete mode 100644 nemo_text_processing/text_normalization/es/verbalizers/verbalize_final.py delete mode 100644 nemo_text_processing/text_normalization/normalize.py delete mode 100644 nemo_text_processing/text_normalization/normalize_with_audio.py delete mode 100644 nemo_text_processing/text_normalization/ru/__init__.py delete mode 100644 nemo_text_processing/text_normalization/ru/alphabet.py delete mode 100644 nemo_text_processing/text_normalization/ru/data/__init__.py delete mode 100644 nemo_text_processing/text_normalization/ru/data/currency/__init__.py delete mode 100644 nemo_text_processing/text_normalization/ru/data/currency/currency_plural.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/currency/currency_singular.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/electronic/__init__.py delete mode 100644 nemo_text_processing/text_normalization/ru/data/electronic/domain.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/electronic/server_name.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/electronic/symbols.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/latin_to_cyrillic.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/measurements.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/months/__init__.py delete mode 100644 nemo_text_processing/text_normalization/ru/data/months/abbr.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/months/abbr_to_name.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/months/numbers.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/numbers/1_cardinals_nominative.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/numbers/2_cardinals_genitive.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/numbers/3_cardinals_dative.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/numbers/4_cardinals_accusative.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/numbers/5_cardinals_instrumental.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/numbers/6_cardinals_prepositional.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/numbers/__init__.py delete mode 100644 nemo_text_processing/text_normalization/ru/data/numbers/cardinals_alternatives.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/numbers/cardinals_nominative_case.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/numbers/decimal_delimiter.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/numbers/decimal_endings.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/numbers/digits_nominative_case.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/numbers/ordinal_endings.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/numbers/ordinals.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/numbers/quantity.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/time/__init__.py delete mode 100644 nemo_text_processing/text_normalization/ru/data/time/increment_hour_cardinal.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/time/increment_hour_ordinal.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/time/minutes_to_hour.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/time/time_convert.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/data/utils/__init__.py delete mode 100644 nemo_text_processing/text_normalization/ru/data/utils/g.fst delete mode 100644 nemo_text_processing/text_normalization/ru/data/utils/universal_thousands_punct.far delete mode 100644 nemo_text_processing/text_normalization/ru/data/utils/util_arithmetic.far delete mode 100644 nemo_text_processing/text_normalization/ru/data/utils/util_byte.far delete mode 100644 nemo_text_processing/text_normalization/ru/data/whitelist.tsv delete mode 100644 nemo_text_processing/text_normalization/ru/taggers/__init__.py delete mode 100644 nemo_text_processing/text_normalization/ru/taggers/cardinal.py delete mode 100644 nemo_text_processing/text_normalization/ru/taggers/date.py delete mode 100644 nemo_text_processing/text_normalization/ru/taggers/decimals.py delete mode 100644 nemo_text_processing/text_normalization/ru/taggers/electronic.py delete mode 100644 nemo_text_processing/text_normalization/ru/taggers/measure.py delete mode 100644 nemo_text_processing/text_normalization/ru/taggers/money.py delete mode 100644 nemo_text_processing/text_normalization/ru/taggers/number_names.py delete mode 100644 nemo_text_processing/text_normalization/ru/taggers/ordinal.py delete mode 100644 nemo_text_processing/text_normalization/ru/taggers/telephone.py delete mode 100644 nemo_text_processing/text_normalization/ru/taggers/time.py delete mode 100644 nemo_text_processing/text_normalization/ru/taggers/tokenize_and_classify.py delete mode 100644 nemo_text_processing/text_normalization/ru/taggers/whitelist.py delete mode 100644 nemo_text_processing/text_normalization/ru/taggers/word.py delete mode 100644 nemo_text_processing/text_normalization/ru/utils.py delete mode 100644 nemo_text_processing/text_normalization/ru/verbalizers/__init__.py delete mode 100644 nemo_text_processing/text_normalization/ru/verbalizers/cardinal.py delete mode 100644 nemo_text_processing/text_normalization/ru/verbalizers/date.py delete mode 100644 nemo_text_processing/text_normalization/ru/verbalizers/decimal.py delete mode 100644 nemo_text_processing/text_normalization/ru/verbalizers/electronic.py delete mode 100644 nemo_text_processing/text_normalization/ru/verbalizers/measure.py delete mode 100644 nemo_text_processing/text_normalization/ru/verbalizers/money.py delete mode 100644 nemo_text_processing/text_normalization/ru/verbalizers/ordinal.py delete mode 100644 nemo_text_processing/text_normalization/ru/verbalizers/telephone.py delete mode 100644 nemo_text_processing/text_normalization/ru/verbalizers/time.py delete mode 100644 nemo_text_processing/text_normalization/ru/verbalizers/verbalize.py delete mode 100644 nemo_text_processing/text_normalization/ru/verbalizers/verbalize_final.py delete mode 100644 nemo_text_processing/text_normalization/run_evaluate.py delete mode 100644 nemo_text_processing/text_normalization/token_parser.py delete mode 100644 nemo_text_processing/text_normalization/zh/README.md delete mode 100644 nemo_text_processing/text_normalization/zh/__init__.py delete mode 100644 nemo_text_processing/text_normalization/zh/data/__init__.py delete mode 100644 nemo_text_processing/text_normalization/zh/data/char/__init__.py delete mode 100644 nemo_text_processing/text_normalization/zh/data/char/charset_extension.tsv delete mode 100644 nemo_text_processing/text_normalization/zh/data/char/charset_national_standard_2013_8105.tsv delete mode 100644 nemo_text_processing/text_normalization/zh/data/char/fullwidth_to_halfwidth.tsv delete mode 100644 nemo_text_processing/text_normalization/zh/data/char/oov_tags.tsv delete mode 100644 nemo_text_processing/text_normalization/zh/data/char/punctuations_zh.tsv delete mode 100644 nemo_text_processing/text_normalization/zh/data/char/upper_to_lower.tsv delete mode 100644 nemo_text_processing/text_normalization/zh/data/date/__init__.py delete mode 100644 nemo_text_processing/text_normalization/zh/data/date/year_suffix.tsv delete mode 100644 nemo_text_processing/text_normalization/zh/data/denylist/__init__.py delete mode 100644 nemo_text_processing/text_normalization/zh/data/denylist/denylist.tsv delete mode 100644 nemo_text_processing/text_normalization/zh/data/erhua/__init__.py delete mode 100644 nemo_text_processing/text_normalization/zh/data/erhua/whitelist.tsv delete mode 100644 nemo_text_processing/text_normalization/zh/data/math/__init__.py delete mode 100644 nemo_text_processing/text_normalization/zh/data/math/score.tsv delete mode 100644 nemo_text_processing/text_normalization/zh/data/math/symbol.tsv delete mode 100644 nemo_text_processing/text_normalization/zh/data/measure/__init__.py delete mode 100644 nemo_text_processing/text_normalization/zh/data/measure/units_en.tsv delete mode 100644 nemo_text_processing/text_normalization/zh/data/measure/units_zh.tsv delete mode 100644 nemo_text_processing/text_normalization/zh/data/money/__init__.py delete mode 100644 nemo_text_processing/text_normalization/zh/data/money/currency_code.tsv delete mode 100644 nemo_text_processing/text_normalization/zh/data/money/currency_symbol.tsv delete mode 100644 nemo_text_processing/text_normalization/zh/data/number/__init__.py delete mode 100644 nemo_text_processing/text_normalization/zh/data/number/digit.tsv delete mode 100644 nemo_text_processing/text_normalization/zh/data/number/digit_teen.tsv delete mode 100644 nemo_text_processing/text_normalization/zh/data/number/sign.tsv delete mode 100644 nemo_text_processing/text_normalization/zh/data/number/zero.tsv delete mode 100644 nemo_text_processing/text_normalization/zh/data/time/__init__.py delete mode 100644 nemo_text_processing/text_normalization/zh/data/time/digit.tsv delete mode 100644 nemo_text_processing/text_normalization/zh/data/time/hour.tsv delete mode 100644 nemo_text_processing/text_normalization/zh/data/time/suffix.tsv delete mode 100644 nemo_text_processing/text_normalization/zh/data/time/tens.tsv delete mode 100644 nemo_text_processing/text_normalization/zh/data/time/zero.tsv delete mode 100644 nemo_text_processing/text_normalization/zh/data/whitelist/__init__.py delete mode 100644 nemo_text_processing/text_normalization/zh/data/whitelist/default.tsv delete mode 100644 nemo_text_processing/text_normalization/zh/graph_utils.py delete mode 100644 nemo_text_processing/text_normalization/zh/taggers/__init__.py delete mode 100644 nemo_text_processing/text_normalization/zh/taggers/cardinal.py delete mode 100644 nemo_text_processing/text_normalization/zh/taggers/char.py delete mode 100644 nemo_text_processing/text_normalization/zh/taggers/date.py delete mode 100644 nemo_text_processing/text_normalization/zh/taggers/fraction.py delete mode 100644 nemo_text_processing/text_normalization/zh/taggers/math_symbol.py delete mode 100644 nemo_text_processing/text_normalization/zh/taggers/measure.py delete mode 100644 nemo_text_processing/text_normalization/zh/taggers/money.py delete mode 100644 nemo_text_processing/text_normalization/zh/taggers/preprocessor.py delete mode 100644 nemo_text_processing/text_normalization/zh/taggers/time.py delete mode 100644 nemo_text_processing/text_normalization/zh/taggers/tokenize_and_classify.py delete mode 100644 nemo_text_processing/text_normalization/zh/taggers/whitelist.py delete mode 100644 nemo_text_processing/text_normalization/zh/utils.py delete mode 100644 nemo_text_processing/text_normalization/zh/verbalizers/__init__.py delete mode 100644 nemo_text_processing/text_normalization/zh/verbalizers/cardinal.py delete mode 100644 nemo_text_processing/text_normalization/zh/verbalizers/char.py delete mode 100644 nemo_text_processing/text_normalization/zh/verbalizers/date.py delete mode 100644 nemo_text_processing/text_normalization/zh/verbalizers/fraction.py delete mode 100644 nemo_text_processing/text_normalization/zh/verbalizers/math_symbol.py delete mode 100644 nemo_text_processing/text_normalization/zh/verbalizers/measure.py delete mode 100644 nemo_text_processing/text_normalization/zh/verbalizers/money.py delete mode 100644 nemo_text_processing/text_normalization/zh/verbalizers/postprocessor.py delete mode 100644 nemo_text_processing/text_normalization/zh/verbalizers/time.py delete mode 100644 nemo_text_processing/text_normalization/zh/verbalizers/verbalize.py delete mode 100644 nemo_text_processing/text_normalization/zh/verbalizers/verbalize_final.py delete mode 100644 nemo_text_processing/text_normalization/zh/verbalizers/whitelist.py rename tests/{nemo_text_processing => collections/tts}/g2p/data/test_data_utils.py (93%) rename tests/{nemo_text_processing => collections/tts}/g2p/phoneme_dict/test_dict_de.txt (100%) rename tests/{nemo_text_processing => collections/tts}/g2p/phoneme_dict/test_dict_en.txt (100%) rename tests/{nemo_text_processing => collections/tts}/g2p/phoneme_dict/test_dict_es.txt (100%) rename tests/{nemo_text_processing => collections/tts}/g2p/test_modules.py (99%) delete mode 100644 tests/nemo_text_processing/__init__.py delete mode 100644 tests/nemo_text_processing/de/__init__.py delete mode 100644 tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_cardinal.txt delete mode 100644 tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_date.txt delete mode 100644 tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_decimal.txt delete mode 100644 tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_electronic.txt delete mode 100644 tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_fraction.txt delete mode 100644 tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_measure.txt delete mode 100644 tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_money.txt delete mode 100644 tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_ordinal.txt delete mode 100644 tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_telephone.txt delete mode 100644 tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_time.txt delete mode 100644 tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_whitelist.txt delete mode 100644 tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_word.txt delete mode 100644 tests/nemo_text_processing/de/data_text_normalization/test_cases_cardinal.txt delete mode 100644 tests/nemo_text_processing/de/data_text_normalization/test_cases_date.txt delete mode 100644 tests/nemo_text_processing/de/data_text_normalization/test_cases_decimal.txt delete mode 100644 tests/nemo_text_processing/de/data_text_normalization/test_cases_electronic.txt delete mode 100644 tests/nemo_text_processing/de/data_text_normalization/test_cases_fraction.txt delete mode 100644 tests/nemo_text_processing/de/data_text_normalization/test_cases_measure.txt delete mode 100644 tests/nemo_text_processing/de/data_text_normalization/test_cases_money.txt delete mode 100644 tests/nemo_text_processing/de/data_text_normalization/test_cases_normalize_with_audio.txt delete mode 100644 tests/nemo_text_processing/de/data_text_normalization/test_cases_ordinal.txt delete mode 100644 tests/nemo_text_processing/de/data_text_normalization/test_cases_telephone.txt delete mode 100644 tests/nemo_text_processing/de/data_text_normalization/test_cases_time.txt delete mode 100644 tests/nemo_text_processing/de/data_text_normalization/test_cases_whitelist.txt delete mode 100644 tests/nemo_text_processing/de/data_text_normalization/test_cases_word.txt delete mode 100644 tests/nemo_text_processing/de/test_cardinal.py delete mode 100644 tests/nemo_text_processing/de/test_date.py delete mode 100644 tests/nemo_text_processing/de/test_decimal.py delete mode 100644 tests/nemo_text_processing/de/test_electronic.py delete mode 100644 tests/nemo_text_processing/de/test_fraction.py delete mode 100644 tests/nemo_text_processing/de/test_measure.py delete mode 100644 tests/nemo_text_processing/de/test_money.py delete mode 100644 tests/nemo_text_processing/de/test_normalization_with_audio.py delete mode 100644 tests/nemo_text_processing/de/test_ordinal.py delete mode 100644 tests/nemo_text_processing/de/test_sparrowhawk_inverse_text_normalization.sh delete mode 100644 tests/nemo_text_processing/de/test_sparrowhawk_normalization.sh delete mode 100644 tests/nemo_text_processing/de/test_telephone.py delete mode 100644 tests/nemo_text_processing/de/test_time.py delete mode 100644 tests/nemo_text_processing/de/test_whitelist.py delete mode 100644 tests/nemo_text_processing/de/test_word.py delete mode 100644 tests/nemo_text_processing/en/__init__.py delete mode 100644 tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_cardinal.txt delete mode 100644 tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_date.txt delete mode 100644 tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_decimal.txt delete mode 100644 tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_electronic.txt delete mode 100644 tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_measure.txt delete mode 100644 tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_money.txt delete mode 100644 tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_ordinal.txt delete mode 100644 tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_telephone.txt delete mode 100644 tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_time.txt delete mode 100644 tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_whitelist.txt delete mode 100644 tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_word.txt delete mode 100644 tests/nemo_text_processing/en/data_text_normalization/test_cases_address.txt delete mode 100644 tests/nemo_text_processing/en/data_text_normalization/test_cases_cardinal.txt delete mode 100644 tests/nemo_text_processing/en/data_text_normalization/test_cases_date.txt delete mode 100644 tests/nemo_text_processing/en/data_text_normalization/test_cases_decimal.txt delete mode 100644 tests/nemo_text_processing/en/data_text_normalization/test_cases_electronic.txt delete mode 100644 tests/nemo_text_processing/en/data_text_normalization/test_cases_fraction.txt delete mode 100644 tests/nemo_text_processing/en/data_text_normalization/test_cases_math.txt delete mode 100644 tests/nemo_text_processing/en/data_text_normalization/test_cases_measure.txt delete mode 100644 tests/nemo_text_processing/en/data_text_normalization/test_cases_money.txt delete mode 100644 tests/nemo_text_processing/en/data_text_normalization/test_cases_normalize_with_audio.txt delete mode 100644 tests/nemo_text_processing/en/data_text_normalization/test_cases_ordinal.txt delete mode 100644 tests/nemo_text_processing/en/data_text_normalization/test_cases_punctuation.txt delete mode 100644 tests/nemo_text_processing/en/data_text_normalization/test_cases_punctuation_match_input.txt delete mode 100644 tests/nemo_text_processing/en/data_text_normalization/test_cases_range.txt delete mode 100644 tests/nemo_text_processing/en/data_text_normalization/test_cases_roman.txt delete mode 100644 tests/nemo_text_processing/en/data_text_normalization/test_cases_serial.txt delete mode 100644 tests/nemo_text_processing/en/data_text_normalization/test_cases_special_text.txt delete mode 100644 tests/nemo_text_processing/en/data_text_normalization/test_cases_telephone.txt delete mode 100644 tests/nemo_text_processing/en/data_text_normalization/test_cases_time.txt delete mode 100644 tests/nemo_text_processing/en/data_text_normalization/test_cases_whitelist.txt delete mode 100644 tests/nemo_text_processing/en/data_text_normalization/test_cases_word.txt delete mode 100644 tests/nemo_text_processing/en/test_address.py delete mode 100644 tests/nemo_text_processing/en/test_cardinal.py delete mode 100644 tests/nemo_text_processing/en/test_date.py delete mode 100644 tests/nemo_text_processing/en/test_decimal.py delete mode 100644 tests/nemo_text_processing/en/test_electronic.py delete mode 100644 tests/nemo_text_processing/en/test_fraction.py delete mode 100644 tests/nemo_text_processing/en/test_math.py delete mode 100644 tests/nemo_text_processing/en/test_measure.py delete mode 100644 tests/nemo_text_processing/en/test_money.py delete mode 100644 tests/nemo_text_processing/en/test_normalization_with_audio.py delete mode 100644 tests/nemo_text_processing/en/test_ordinal.py delete mode 100644 tests/nemo_text_processing/en/test_punctuation.py delete mode 100644 tests/nemo_text_processing/en/test_range.py delete mode 100644 tests/nemo_text_processing/en/test_roman.py delete mode 100644 tests/nemo_text_processing/en/test_serial.py delete mode 100644 tests/nemo_text_processing/en/test_sparrowhawk_inverse_text_normalization.sh delete mode 100644 tests/nemo_text_processing/en/test_sparrowhawk_normalization.sh delete mode 100644 tests/nemo_text_processing/en/test_special_text.py delete mode 100644 tests/nemo_text_processing/en/test_telephone.py delete mode 100644 tests/nemo_text_processing/en/test_text_split.py delete mode 100644 tests/nemo_text_processing/en/test_time.py delete mode 100644 tests/nemo_text_processing/en/test_whitelist.py delete mode 100644 tests/nemo_text_processing/en/test_word.py delete mode 100644 tests/nemo_text_processing/es/__init__.py delete mode 100644 tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_cardinal.txt delete mode 100644 tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_date.txt delete mode 100644 tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_decimal.txt delete mode 100644 tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_electronic.txt delete mode 100644 tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_measure.txt delete mode 100644 tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_money.txt delete mode 100644 tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_ordinal.txt delete mode 100644 tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_telephone.txt delete mode 100644 tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_time.txt delete mode 100644 tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_whitelist.txt delete mode 100644 tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_word.txt delete mode 100644 tests/nemo_text_processing/es/data_text_normalization/test_cases_cardinal.txt delete mode 100644 tests/nemo_text_processing/es/data_text_normalization/test_cases_date.txt delete mode 100644 tests/nemo_text_processing/es/data_text_normalization/test_cases_decimal.txt delete mode 100644 tests/nemo_text_processing/es/data_text_normalization/test_cases_electronic.txt delete mode 100644 tests/nemo_text_processing/es/data_text_normalization/test_cases_fraction.txt delete mode 100644 tests/nemo_text_processing/es/data_text_normalization/test_cases_measure.txt delete mode 100644 tests/nemo_text_processing/es/data_text_normalization/test_cases_money.txt delete mode 100644 tests/nemo_text_processing/es/data_text_normalization/test_cases_normalize_with_audio.txt delete mode 100644 tests/nemo_text_processing/es/data_text_normalization/test_cases_ordinal.txt delete mode 100644 tests/nemo_text_processing/es/data_text_normalization/test_cases_telephone.txt delete mode 100644 tests/nemo_text_processing/es/data_text_normalization/test_cases_time.txt delete mode 100644 tests/nemo_text_processing/es/data_text_normalization/test_cases_whitelist.txt delete mode 100644 tests/nemo_text_processing/es/data_text_normalization/test_cases_word.txt delete mode 100644 tests/nemo_text_processing/es/test_cardinal.py delete mode 100644 tests/nemo_text_processing/es/test_date.py delete mode 100644 tests/nemo_text_processing/es/test_decimal.py delete mode 100644 tests/nemo_text_processing/es/test_electronic.py delete mode 100644 tests/nemo_text_processing/es/test_fraction.py delete mode 100644 tests/nemo_text_processing/es/test_measure.py delete mode 100644 tests/nemo_text_processing/es/test_money.py delete mode 100644 tests/nemo_text_processing/es/test_normalization_with_audio.py delete mode 100644 tests/nemo_text_processing/es/test_ordinal.py delete mode 100644 tests/nemo_text_processing/es/test_sparrowhawk_inverse_text_normalization.sh delete mode 100644 tests/nemo_text_processing/es/test_sparrowhawk_normalization.sh delete mode 100644 tests/nemo_text_processing/es/test_telephone.py delete mode 100644 tests/nemo_text_processing/es/test_time.py delete mode 100644 tests/nemo_text_processing/es/test_whitelist.py delete mode 100644 tests/nemo_text_processing/es/test_word.py delete mode 100644 tests/nemo_text_processing/fr/__init__.py delete mode 100644 tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_cardinal.txt delete mode 100644 tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_date.txt delete mode 100644 tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_decimal.txt delete mode 100644 tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_electronic.txt delete mode 100644 tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_fraction.txt delete mode 100644 tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_measure.txt delete mode 100644 tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_money.txt delete mode 100644 tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_ordinal.txt delete mode 100644 tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_telephone.txt delete mode 100644 tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_time.txt delete mode 100644 tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_whitelist.txt delete mode 100644 tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_word.txt delete mode 100644 tests/nemo_text_processing/fr/test_cardinal.py delete mode 100644 tests/nemo_text_processing/fr/test_date.py delete mode 100644 tests/nemo_text_processing/fr/test_decimal.py delete mode 100644 tests/nemo_text_processing/fr/test_electronic.py delete mode 100644 tests/nemo_text_processing/fr/test_fraction.py delete mode 100644 tests/nemo_text_processing/fr/test_measure.py delete mode 100644 tests/nemo_text_processing/fr/test_money.py delete mode 100644 tests/nemo_text_processing/fr/test_ordinal.py delete mode 100644 tests/nemo_text_processing/fr/test_sparrowhawk_inverse_text_normalization.sh delete mode 100644 tests/nemo_text_processing/fr/test_telephone.py delete mode 100644 tests/nemo_text_processing/fr/test_time.py delete mode 100644 tests/nemo_text_processing/fr/test_whitelist.py delete mode 100644 tests/nemo_text_processing/fr/test_word.py delete mode 100644 tests/nemo_text_processing/pt/__init__.py delete mode 100755 tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_cardinal.txt delete mode 100644 tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_date.txt delete mode 100755 tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_decimal.txt delete mode 100644 tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_electronic.txt delete mode 100755 tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_measure.txt delete mode 100755 tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_money.txt delete mode 100755 tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_ordinal.txt delete mode 100755 tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_telephone.txt delete mode 100755 tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_time.txt delete mode 100755 tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_whitelist.txt delete mode 100755 tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_word.txt delete mode 100644 tests/nemo_text_processing/pt/test_cardinal.py delete mode 100644 tests/nemo_text_processing/pt/test_date.py delete mode 100644 tests/nemo_text_processing/pt/test_decimal.py delete mode 100644 tests/nemo_text_processing/pt/test_electronic.py delete mode 100644 tests/nemo_text_processing/pt/test_measure.py delete mode 100644 tests/nemo_text_processing/pt/test_money.py delete mode 100644 tests/nemo_text_processing/pt/test_ordinal.py delete mode 100755 tests/nemo_text_processing/pt/test_sparrowhawk_inverse_text_normalization.sh delete mode 100644 tests/nemo_text_processing/pt/test_telephone.py delete mode 100644 tests/nemo_text_processing/pt/test_time.py delete mode 100644 tests/nemo_text_processing/pt/test_whitelist.py delete mode 100644 tests/nemo_text_processing/pt/test_word.py delete mode 100644 tests/nemo_text_processing/ru/__init__.py delete mode 100644 tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_cardinal.txt delete mode 100644 tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_date.txt delete mode 100644 tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_decimal.txt delete mode 100644 tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_electronic.txt delete mode 100644 tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_measure.txt delete mode 100644 tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_money.txt delete mode 100644 tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_ordinal.txt delete mode 100644 tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_ordinal_hard.txt delete mode 100644 tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_telephone.txt delete mode 100644 tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_time.txt delete mode 100644 tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_whitelist.txt delete mode 100644 tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_word.txt delete mode 100644 tests/nemo_text_processing/ru/data_text_normalization/__init__.py delete mode 100644 tests/nemo_text_processing/ru/data_text_normalization/test_cases_cardinal.txt delete mode 100644 tests/nemo_text_processing/ru/data_text_normalization/test_cases_cardinal_normalize_with_audio.txt delete mode 100644 tests/nemo_text_processing/ru/data_text_normalization/test_cases_date.txt delete mode 100644 tests/nemo_text_processing/ru/data_text_normalization/test_cases_decimal.txt delete mode 100644 tests/nemo_text_processing/ru/data_text_normalization/test_cases_electronic.txt delete mode 100644 tests/nemo_text_processing/ru/data_text_normalization/test_cases_measure.txt delete mode 100644 tests/nemo_text_processing/ru/data_text_normalization/test_cases_money.txt delete mode 100644 tests/nemo_text_processing/ru/data_text_normalization/test_cases_ordinal.txt delete mode 100644 tests/nemo_text_processing/ru/data_text_normalization/test_cases_telephone.txt delete mode 100644 tests/nemo_text_processing/ru/data_text_normalization/test_cases_time.txt delete mode 100644 tests/nemo_text_processing/ru/data_text_normalization/test_cases_whitelist.txt delete mode 100644 tests/nemo_text_processing/ru/data_text_normalization/test_cases_word.txt delete mode 100644 tests/nemo_text_processing/ru/test_ru_inverse_normalization.py delete mode 100644 tests/nemo_text_processing/ru/test_ru_normalization.py delete mode 100644 tests/nemo_text_processing/ru/test_sparrowhawk_inverse_text_normalization.sh delete mode 100644 tests/nemo_text_processing/utils.py delete mode 100644 tests/nemo_text_processing/vi/__init__.py delete mode 100644 tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_cardinal.txt delete mode 100644 tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_date.txt delete mode 100644 tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_decimal.txt delete mode 100644 tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_electronic.txt delete mode 100644 tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_fraction.txt delete mode 100644 tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_measure.txt delete mode 100644 tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_money.txt delete mode 100644 tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_ordinal.txt delete mode 100644 tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_telephone.txt delete mode 100644 tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_time.txt delete mode 100644 tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_whitelist.txt delete mode 100644 tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_word.txt delete mode 100644 tests/nemo_text_processing/vi/test_cardinal.py delete mode 100644 tests/nemo_text_processing/vi/test_date.py delete mode 100644 tests/nemo_text_processing/vi/test_decimal.py delete mode 100644 tests/nemo_text_processing/vi/test_electronic.py delete mode 100644 tests/nemo_text_processing/vi/test_fraction.py delete mode 100644 tests/nemo_text_processing/vi/test_measure.py delete mode 100644 tests/nemo_text_processing/vi/test_money.py delete mode 100644 tests/nemo_text_processing/vi/test_ordinal.py delete mode 100644 tests/nemo_text_processing/vi/test_sparrowhawk_inverse_text_normalization.sh delete mode 100644 tests/nemo_text_processing/vi/test_telephone.py delete mode 100644 tests/nemo_text_processing/vi/test_time.py delete mode 100644 tests/nemo_text_processing/vi/test_whitelist.py delete mode 100644 tests/nemo_text_processing/vi/test_word.py delete mode 100644 tests/nemo_text_processing/zh/__init__.py delete mode 100644 tests/nemo_text_processing/zh/data_text_normalization/test_cases_char.txt delete mode 100644 tests/nemo_text_processing/zh/data_text_normalization/test_cases_date.txt delete mode 100644 tests/nemo_text_processing/zh/data_text_normalization/test_cases_fraction.txt delete mode 100644 tests/nemo_text_processing/zh/data_text_normalization/test_cases_math.txt delete mode 100644 tests/nemo_text_processing/zh/data_text_normalization/test_cases_measure.txt delete mode 100644 tests/nemo_text_processing/zh/data_text_normalization/test_cases_money.txt delete mode 100644 tests/nemo_text_processing/zh/data_text_normalization/test_cases_preprocess.txt delete mode 100644 tests/nemo_text_processing/zh/data_text_normalization/test_cases_time.txt delete mode 100644 tests/nemo_text_processing/zh/test_char.py delete mode 100644 tests/nemo_text_processing/zh/test_date.py delete mode 100644 tests/nemo_text_processing/zh/test_fraction.py delete mode 100644 tests/nemo_text_processing/zh/test_math.py delete mode 100644 tests/nemo_text_processing/zh/test_measure.py delete mode 100644 tests/nemo_text_processing/zh/test_money.py delete mode 100644 tests/nemo_text_processing/zh/test_preprocess.py delete mode 100644 tests/nemo_text_processing/zh/test_sparrowhawk_normalization.sh delete mode 100644 tests/nemo_text_processing/zh/test_time.py delete mode 100644 tools/text_processing_deployment/Dockerfile delete mode 100644 tools/text_processing_deployment/README.rst delete mode 100644 tools/text_processing_deployment/docker/build.sh delete mode 100644 tools/text_processing_deployment/docker/launch.sh delete mode 100644 tools/text_processing_deployment/export_grammars.sh delete mode 100644 tools/text_processing_deployment/pynini_export.py delete mode 100644 tutorials/text_processing/ITN_with_Thutmose_Tagger.ipynb delete mode 100755 tutorials/text_processing/Text_(Inverse)_Normalization.ipynb delete mode 100644 tutorials/text_processing/WFST_Tutorial.ipynb delete mode 100644 tutorials/text_processing/images/audio_based_tn.png delete mode 100644 tutorials/text_processing/images/cent.PNG delete mode 100644 tutorials/text_processing/images/cent_to_100.PNG delete mode 100644 tutorials/text_processing/images/cent_vingt_bad.PNG delete mode 100644 tutorials/text_processing/images/cent_vingt_good.PNG delete mode 100644 tutorials/text_processing/images/cent_vingt_to_120.PNG delete mode 100644 tutorials/text_processing/images/deployment.png delete mode 100644 tutorials/text_processing/images/dix_to_digits.PNG delete mode 100644 tutorials/text_processing/images/dix_to_digits_with_insert.PNG delete mode 100644 tutorials/text_processing/images/pipeline.png delete mode 100644 tutorials/text_processing/images/romanization.PNG delete mode 100644 tutorials/text_processing/images/task_overview.png delete mode 100644 tutorials/text_processing/images/thutmose_tagger_alignment_bottom.png delete mode 100644 tutorials/text_processing/images/thutmose_tagger_alignment_top.png delete mode 100644 tutorials/text_processing/images/thutmose_tagger_architecture.png delete mode 100644 tutorials/text_processing/images/thutmose_tagger_final_alignment.png delete mode 100644 tutorials/text_processing/images/thutmose_tagger_tag_vocabulary.png diff --git a/Dockerfile b/Dockerfile index d796ef055558..03ec004e268c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -54,10 +54,6 @@ WORKDIR /tmp/nemo COPY requirements . RUN for f in $(ls requirements*.txt); do pip3 install --disable-pip-version-check --no-cache-dir -r $f; done -# install pynini -COPY nemo_text_processing/install_pynini.sh /tmp/nemo/ -RUN /bin/bash /tmp/nemo/install_pynini.sh - # install k2, skip if installation fails COPY scripts /tmp/nemo/scripts/ RUN /bin/bash /tmp/nemo/scripts/speech_recognition/k2/setup.sh || exit 0 @@ -81,8 +77,7 @@ RUN --mount=from=nemo-src,target=/tmp/nemo cd /tmp/nemo && pip install ".[all]" # Check install RUN python -c "import nemo.collections.nlp as nemo_nlp" && \ - python -c "import nemo.collections.tts as nemo_tts" && \ - python -c "import nemo_text_processing.text_normalization as text_normalization" + python -c "import nemo.collections.tts as nemo_tts" # TODO: Update to newer numba 0.56.0RC1 for 22.03 container if possible # install pinned numba version diff --git a/Jenkinsfile b/Jenkinsfile index f36839598941..bd8cbd78e906 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -102,92 +102,6 @@ pipeline { } } - - stage('L0: TN/ITN Tests CPU') { - when { - anyOf { - branch 'main' - changeRequest target: 'main' - } - } - failFast true - parallel { - stage('En TN grammars') { - steps { - sh 'CUDA_VISIBLE_DEVICES="" python nemo_text_processing/text_normalization/normalize.py --text="1" --cache_dir /home/TestData/nlp/text_norm/ci/grammars/11-16-22' - } - } - stage('En ITN grammars') { - steps { - sh 'CUDA_VISIBLE_DEVICES="" python nemo_text_processing/inverse_text_normalization/inverse_normalize.py --language en --text="twenty" --cache_dir /home/TestData/nlp/text_norm/ci/grammars/11-16-22' - } - } - stage('Test En non-deterministic TN & Run all En TN/ITN tests (restore grammars from cache)') { - steps { - sh 'CUDA_VISIBLE_DEVICES="" python nemo_text_processing/text_normalization/normalize_with_audio.py --text "\$.01" --n_tagged 2 --cache_dir /home/TestData/nlp/text_norm/ci/grammars/11-16-22' - sh 'CUDA_VISIBLE_DEVICES="" pytest tests/nemo_text_processing/en/ -m "not pleasefixme" --cpu --tn_cache_dir /home/TestData/nlp/text_norm/ci/grammars/11-16-22' - } - } - stage('Test En Hybrid TN') { - steps { - sh 'CUDA_VISIBLE_DEVICES="" python nemo_text_processing/hybrid/wfst_lm_rescoring.py --data /home/TestData/nlp/text_norm/hybrid_tn/test.txt --regenerate_pkl --cache_dir /home/TestData/nlp/text_norm/ci/grammars/11-16-22 | grep "all_correct: True" || exit 1' - } - } - } - } - - stage('L2: NeMo text processing') { - when { - anyOf { - branch 'main' - changeRequest target: 'main' - } - } - failFast true - parallel { - stage('L2: Eng TN') { - steps { - sh 'cd tools/text_processing_deployment && python pynini_export.py --output=/home/TestData/nlp/text_norm/output/ --grammars=tn_grammars --cache_dir /home/TestData/nlp/text_norm/ci/grammars/11-16-22 --language=en && ls -R /home/TestData/nlp/text_norm/output/ && echo ".far files created "|| exit 1' - sh 'cd nemo_text_processing/text_normalization/ && python normalize.py --input_file=/home/TestData/nlp/text_norm/ci/test.txt --input_case="lower_cased" --language=en --output_file=/home/TestData/nlp/text_norm/output/test.pynini.txt --verbose' - sh 'cat /home/TestData/nlp/text_norm/output/test.pynini.txt' - sh 'cmp --silent /home/TestData/nlp/text_norm/output/test.pynini.txt /home/TestData/nlp/text_norm/ci/test_goal_py_05-25.txt || exit 1' - sh 'rm -rf /home/TestData/nlp/text_norm/output/*' - } - } - - stage('L2: Eng ITN export') { - steps { - sh 'cd tools/text_processing_deployment && python pynini_export.py --output=/home/TestData/nlp/text_denorm/output/ --grammars=itn_grammars --cache_dir /home/TestData/nlp/text_norm/ci/grammars/11-16-22 --language=en && ls -R /home/TestData/nlp/text_denorm/output/ && echo ".far files created "|| exit 1' - sh 'cd nemo_text_processing/inverse_text_normalization/ && python inverse_normalize.py --input_file=/home/TestData/nlp/text_denorm/ci/test.txt --language=en --output_file=/home/TestData/nlp/text_denorm/output/test.pynini.txt --verbose' - sh 'cmp --silent /home/TestData/nlp/text_denorm/output/test.pynini.txt /home/TestData/nlp/text_denorm/ci/test_goal_py.txt || exit 1' - sh 'rm -rf /home/TestData/nlp/text_denorm/output/*' - } - } - stage('L2: TN with Audio (audio and raw text)') { - steps { - sh 'cd nemo_text_processing/text_normalization && \ - python normalize_with_audio.py --language=en --cache_dir /home/TestData/nlp/text_norm/ci/grammars/11-16-22 --text "The total amounts to \\$4.76." \ - --audio_data /home/TestData/nlp/text_norm/audio_based/audio.wav | tail -n2 | head -n1 > /tmp/out_raw.txt 2>&1 && \ - cmp --silent /tmp/out_raw.txt /home/TestData/nlp/text_norm/audio_based/result.txt || exit 1' - } - } - stage('L2: TN with Audio (audio and text file)') { - steps { - sh 'cd nemo_text_processing/text_normalization && \ - python normalize_with_audio.py --language=en --cache_dir /home/TestData/nlp/text_norm/ci/grammars/11-16-22 --text /home/TestData/nlp/text_norm/audio_based/text.txt \ - --audio_data /home/TestData/nlp/text_norm/audio_based/audio.wav | tail -n2 | head -n1 > /tmp/out_file.txt 2>&1 && \ - cmp --silent /tmp/out_file.txt /home/TestData/nlp/text_norm/audio_based/result.txt || exit 1' - } - } - stage('L2: TN with Audio (manifest)') { - steps { - sh 'cd nemo_text_processing/text_normalization && \ - python normalize_with_audio.py --language=en --audio_data /home/TestData/nlp/text_norm/audio_based/manifest.json --n_tagged=120 --cache_dir /home/TestData/nlp/text_norm/ci/grammars/11-16-22' - } - } - } - } - stage('L2: ASR dev run') { when { anyOf { @@ -1073,7 +987,7 @@ pipeline { parallel { stage('G2P Conformer training, evaluation and inference') { steps { - sh 'cd examples/text_processing/g2p && \ + sh 'cd examples/tts/g2p && \ TIME=`date +"%Y-%m-%d-%T"` && OUTPUT_DIR_CONFORMER=output_ctc_${TIME} && \ python g2p_train_and_evaluate.py \ train_manifest=/home/TestData/g2p/g2p.json \ @@ -1097,7 +1011,7 @@ pipeline { } stage('ByT5G2P training, evaluation and inference') { steps { - sh 'TRANSFORMERS_OFFLINE=0 && cd examples/text_processing/g2p && \ + sh 'TRANSFORMERS_OFFLINE=0 && cd examples/tts/g2p && \ TIME=`date +"%Y-%m-%d-%T"` && OUTPUT_DIR_T5=output_byt5_${TIME} && \ python g2p_train_and_evaluate.py \ train_manifest=/home/TestData/g2p/g2p.json \ @@ -1119,7 +1033,7 @@ pipeline { } stage('HeteronymClassificationModel training, evaluation and inference') { steps { - sh 'cd examples/text_processing/g2p && \ + sh 'cd examples/tts/g2p && \ TIME=`date +"%Y-%m-%d-%T"` && OUTPUT_DIR=output_${TIME} && \ python heteronym_classification_train_and_evaluate.py \ train_manifest=/home/TestData/g2p/manifest.json \ diff --git a/examples/tts/aligner_heteronym_disambiguation.py b/examples/tts/aligner_heteronym_disambiguation.py index fe5d89747a2b..ad6b1ac13eee 100644 --- a/examples/tts/aligner_heteronym_disambiguation.py +++ b/examples/tts/aligner_heteronym_disambiguation.py @@ -26,7 +26,7 @@ """ -G2P disambiguation using an Aligner model's input embedding distances. +G2P_paper disambiguation using an Aligner model's input embedding distances. Does not handle OOV and leaves them as graphemes. @@ -46,7 +46,7 @@ def get_args(): """Retrieve arguments for disambiguation. """ - parser = argparse.ArgumentParser("G2P disambiguation using Aligner input embedding distances.") + parser = argparse.ArgumentParser("G2P_paper disambiguation using Aligner input embedding distances.") # TODO(jocelynh): Make this required=False with default download from NGC once ckpt uploaded parser.add_argument('--model', required=True, type=str, help="Path to Aligner model checkpoint (.nemo file).") parser.add_argument( @@ -108,7 +108,7 @@ def disambiguate_candidates(aligner, text, spec, spec_len, confidence, device, h Note: This could be sped up if multiple words' candidates were batched, but this is conceptually easier. """ - # Grab original G2P result + # Grab original G2P_paper result aligner_g2p = aligner.tokenizer.g2p base_g2p = aligner_g2p(text) @@ -122,7 +122,7 @@ def disambiguate_candidates(aligner, text, spec, spec_len, confidence, device, h has_heteronym = False for word in words: - # Retrieve the length of the word in the default G2P conversion + # Retrieve the length of the word in the default G2P_paper conversion g2p_default_len = len(aligner_g2p(word)) # Check if word needs to be disambiguated @@ -134,7 +134,7 @@ def disambiguate_candidates(aligner, text, spec, spec_len, confidence, device, h candidate_prons_and_lengths = [] for pron in aligner_g2p.phoneme_dict[word]: - # Replace graphemes in the base G2P result with the current variant + # Replace graphemes in the base G2P_paper result with the current variant candidate = base_g2p[:word_start_idx] + pron + base_g2p[word_start_idx + g2p_default_len :] candidate_tokens = aligner.tokenizer.encode_from_g2p(candidate) @@ -247,7 +247,7 @@ def disambiguate_dataset( count = 0 for line in f_in: - # Retrieve entry and base G2P conversion for full text + # Retrieve entry and base G2P_paper conversion for full text entry = json.loads(line) # Set punct_post_process=True in order to preserve words with apostrophes text = aligner.normalizer.normalize(entry['text'], punct_post_process=True) diff --git a/examples/text_processing/g2p/README.md b/examples/tts/g2p/README.md similarity index 100% rename from examples/text_processing/g2p/README.md rename to examples/tts/g2p/README.md diff --git a/examples/text_processing/g2p/conf/g2p_conformer_ctc.yaml b/examples/tts/g2p/conf/g2p_conformer_ctc.yaml similarity index 95% rename from examples/text_processing/g2p/conf/g2p_conformer_ctc.yaml rename to examples/tts/g2p/conf/g2p_conformer_ctc.yaml index a20259b197c2..aa94405747fb 100644 --- a/examples/text_processing/g2p/conf/g2p_conformer_ctc.yaml +++ b/examples/tts/g2p/conf/g2p_conformer_ctc.yaml @@ -1,4 +1,4 @@ -name: G2P-Conformer-CTC +name: G2P_paper-Conformer-CTC # Dataset info train_manifest: ??? @@ -69,7 +69,7 @@ model: train_ds: manifest_filepath: ${train_manifest} dataset: - _target_: "nemo_text_processing.g2p.data.ctc_g2p.CTCG2PBPEDataset" + _target_: "nemo.tts.g2p.data.ctc_g2p.CTCG2PBPEDataset" phoneme_field: "text" # name of the field in manifest_filepath for ground truth phonemes grapheme_field: "text_graphemes" # name of the field in manifest_filepath for input grapheme text dataloader_params: @@ -81,7 +81,7 @@ model: validation_ds: manifest_filepath: ${validation_manifest} dataset: - _target_: "nemo_text_processing.g2p.data.ctc_g2p.CTCG2PBPEDataset" + _target_: "nemo.tts.g2p.data.ctc_g2p.CTCG2PBPEDataset" phoneme_field: "text" # name of the field in manifest_filepath for ground truth phonemes grapheme_field: "text_graphemes" # name of the field in manifest_filepath for input grapheme text dataloader_params: @@ -93,7 +93,7 @@ model: test_ds: manifest_filepath: ${test_manifest} dataset: - _target_: "nemo_text_processing.g2p.data.ctc_g2p.CTCG2PBPEDataset" + _target_: "nemo.collections.tts.g2p.data.ctc_g2p.CTCG2PBPEDataset" phoneme_field: "text" # name of the field in manifest_filepath for ground truth phonemes grapheme_field: "text_graphemes" # name of the field in manifest_filepath for input grapheme text dataloader_params: diff --git a/examples/text_processing/g2p/conf/heteronym_classification.yaml b/examples/tts/g2p/conf/heteronym_classification.yaml similarity index 89% rename from examples/text_processing/g2p/conf/heteronym_classification.yaml rename to examples/tts/g2p/conf/heteronym_classification.yaml index ef6b65a69cf0..89c2899230de 100644 --- a/examples/text_processing/g2p/conf/heteronym_classification.yaml +++ b/examples/tts/g2p/conf/heteronym_classification.yaml @@ -35,7 +35,7 @@ model: train_ds: dataset: - _target_: "nemo_text_processing.g2p.data.heteronym_classification_data.HeteronymClassificationDataset" + _target_: "nemo.tts.g2p.data.heteronym_classification_data.HeteronymClassificationDataset" manifest: ${train_manifest} grapheme_field: "text_graphemes" # name of the field in manifest for input grapheme text dataloader_params: @@ -46,7 +46,7 @@ model: validation_ds: dataset: - _target_: "nemo_text_processing.g2p.data.heteronym_classification_data.HeteronymClassificationDataset" + _target_: "nemo.tts.g2p.data.heteronym_classification_data.HeteronymClassificationDataset" manifest: ${validation_manifest} grapheme_field: "text_graphemes" # name of the field in manifest for input grapheme text dataloader_params: @@ -57,7 +57,7 @@ model: test_ds: dataset: - _target_: "nemo_text_processing.g2p.data.heteronym_classification_data.HeteronymClassificationDataset" + _target_: "nemo.tts.g2p.data.heteronym_classification_data.HeteronymClassificationDataset" manifest: ${test_manifest} grapheme_field: "text_graphemes" # name of the field in manifest for input grapheme text dataloader_params: diff --git a/examples/text_processing/g2p/conf/t5_g2p.yaml b/examples/tts/g2p/conf/t5_g2p.yaml similarity index 92% rename from examples/text_processing/g2p/conf/t5_g2p.yaml rename to examples/tts/g2p/conf/t5_g2p.yaml index 0ee46d1e61ac..24eca43ce12f 100644 --- a/examples/text_processing/g2p/conf/t5_g2p.yaml +++ b/examples/tts/g2p/conf/t5_g2p.yaml @@ -17,7 +17,7 @@ model: train_ds: manifest_filepath: ${train_manifest} dataset: - _target_: "nemo_text_processing.g2p.data.t5_g2p.T5G2PDataset" + _target_: "nemo.tts.g2p.data.t5_g2p.T5G2PDataset" phoneme_field: "text" # name of the field in manifest_filepath for ground truth phonemes grapheme_field: "text_graphemes" # name of the field in manifest_filepath for input grapheme text dataloader_params: @@ -29,7 +29,7 @@ model: validation_ds: manifest_filepath: ${validation_manifest} dataset: - _target_: "nemo_text_processing.g2p.data.t5_g2p.T5G2PDataset" + _target_: "nemo.tts.g2p.data.t5_g2p.T5G2PDataset" phoneme_field: "text" # name of the field in manifest_filepath for ground truth phonemes grapheme_field: "text_graphemes" # name of the field in manifest_filepath for input grapheme text dataloader_params: @@ -41,7 +41,7 @@ model: test_ds: manifest_filepath: ${test_manifest} dataset: - _target_: "nemo_text_processing.g2p.data.t5_g2p.T5G2PDataset" + _target_: "nemo.tts.g2p.data.t5_g2p.T5G2PDataset" phoneme_field: "text" # name of the field in manifest_filepath for ground truth phonemes grapheme_field: "text_graphemes" # name of the field in manifest_filepath for input grapheme text dataloader_params: diff --git a/examples/text_processing/g2p/g2p_inference.py b/examples/tts/g2p/g2p_inference.py similarity index 98% rename from examples/text_processing/g2p/g2p_inference.py rename to examples/tts/g2p/g2p_inference.py index fe73ddbcbfde..ec32c29c4a95 100644 --- a/examples/text_processing/g2p/g2p_inference.py +++ b/examples/tts/g2p/g2p_inference.py @@ -18,7 +18,7 @@ import pytorch_lightning as pl import torch -from nemo_text_processing.g2p.models.g2p_model import G2PModel +from nemo.collections.tts.g2p.models import G2PModel from omegaconf import OmegaConf from utils import get_metrics diff --git a/examples/text_processing/g2p/g2p_train_and_evaluate.py b/examples/tts/g2p/g2p_train_and_evaluate.py similarity index 97% rename from examples/text_processing/g2p/g2p_train_and_evaluate.py rename to examples/tts/g2p/g2p_train_and_evaluate.py index 395b82c3f073..1770bd28f87d 100644 --- a/examples/text_processing/g2p/g2p_train_and_evaluate.py +++ b/examples/tts/g2p/g2p_train_and_evaluate.py @@ -16,7 +16,7 @@ import pytorch_lightning as pl import torch -from nemo_text_processing.g2p.models.g2p_model import G2PModel +from nemo.collections.tts.g2p.models import G2PModel from utils import get_model from nemo.collections.common.callbacks import LogEpochTimeCallback @@ -41,7 +41,7 @@ Example of the config file: NeMo/examples/text_processing/g2p/conf/t5_g2p.yaml -# Training Conformer-G2P Model and evaluation at the end of training: +# Training Conformer-G2P_paper Model and evaluation at the end of training: python examples/text_processing/g2p/g2p_train_and_evaluate.py \ # (Optional: --config-path= --config-name=) \ model.train_ds.manifest_filepath="" \ diff --git a/examples/text_processing/g2p/heteronym_classification_inference.py b/examples/tts/g2p/heteronym_classification_inference.py similarity index 99% rename from examples/text_processing/g2p/heteronym_classification_inference.py rename to examples/tts/g2p/heteronym_classification_inference.py index f587a12c3317..a4621422a8f4 100644 --- a/examples/text_processing/g2p/heteronym_classification_inference.py +++ b/examples/tts/g2p/heteronym_classification_inference.py @@ -20,7 +20,7 @@ import pytorch_lightning as pl import torch -from nemo_text_processing.g2p.models.heteronym_classification import HeteronymClassificationModel +from nemo.collections.tts.g2p.models.heteronym_classification import HeteronymClassificationModel from omegaconf import OmegaConf from nemo.core.config import hydra_runner diff --git a/examples/text_processing/g2p/heteronym_classification_train_and_evaluate.py b/examples/tts/g2p/heteronym_classification_train_and_evaluate.py similarity index 98% rename from examples/text_processing/g2p/heteronym_classification_train_and_evaluate.py rename to examples/tts/g2p/heteronym_classification_train_and_evaluate.py index e6a3cd17ade6..012e7a894958 100644 --- a/examples/text_processing/g2p/heteronym_classification_train_and_evaluate.py +++ b/examples/tts/g2p/heteronym_classification_train_and_evaluate.py @@ -16,7 +16,7 @@ import pytorch_lightning as pl import torch -from nemo_text_processing.g2p.models.heteronym_classification import HeteronymClassificationModel +from nemo.collections.tts.g2p.models.heteronym_classification import HeteronymClassificationModel from nemo.collections.common.callbacks import LogEpochTimeCallback from nemo.core.config import hydra_runner diff --git a/examples/text_processing/g2p/utils.py b/examples/tts/g2p/utils.py similarity index 96% rename from examples/text_processing/g2p/utils.py rename to examples/tts/g2p/utils.py index 1f022e49cab3..c91be04f2ae3 100644 --- a/examples/text_processing/g2p/utils.py +++ b/examples/tts/g2p/utils.py @@ -14,8 +14,8 @@ import json -from nemo_text_processing.g2p.models.ctc_g2p import CTCG2PModel -from nemo_text_processing.g2p.models.t5_g2p import T5G2PModel +from nemo.collections.tts.g2p.models import CTCG2PModel +from nemo.collections.tts.g2p.models.t5_g2p import T5G2PModel from nemo.collections.asr.metrics.wer import word_error_rate from nemo.utils import logging diff --git a/nemo/collections/common/tokenizers/text_to_speech/tokenizer_wrapper.py b/nemo/collections/common/tokenizers/text_to_speech/tokenizer_wrapper.py index be73e80a1b3c..539c9d85a829 100644 --- a/nemo/collections/common/tokenizers/text_to_speech/tokenizer_wrapper.py +++ b/nemo/collections/common/tokenizers/text_to_speech/tokenizer_wrapper.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from nemo_text_processing.g2p.modules import EnglishG2p +from nemo.collections.tts.g2p.modules import EnglishG2p from nemo.collections.common.tokenizers import TokenizerSpec from nemo.collections.common.tokenizers.text_to_speech.tts_tokenizers import EnglishPhonemesTokenizer diff --git a/nemo/collections/common/tokenizers/text_to_speech/tts_tokenizers.py b/nemo/collections/common/tokenizers/text_to_speech/tts_tokenizers.py index 1b478653d62a..ac9cb45a2724 100644 --- a/nemo/collections/common/tokenizers/text_to_speech/tts_tokenizers.py +++ b/nemo/collections/common/tokenizers/text_to_speech/tts_tokenizers.py @@ -19,7 +19,7 @@ from contextlib import contextmanager from typing import List, Optional -from nemo_text_processing.g2p.data.data_utils import ( +from nemo.collections.tts.g2p.data.data_utils import ( any_locale_text_preprocessing, chinese_text_preprocessing, english_text_preprocessing, @@ -447,11 +447,11 @@ def encode(self, text): def encode_from_g2p(self, g2p_text: List[str], raw_text: Optional[str] = None): """ - Encodes text that has already been run through G2P. - Called for encoding to tokens after text preprocessing and G2P. + Encodes text that has already been run through G2P_paper. + Called for encoding to tokens after text preprocessing and G2P_paper. Args: - g2p_text: G2P's output, could be a mixture of phonemes and graphemes, + g2p_text: G2P_paper's output, could be a mixture of phonemes and graphemes, e.g. "see OOV" -> ['S', 'IY1', ' ', 'O', 'O', 'V'] raw_text: original raw input """ @@ -534,11 +534,11 @@ def __init__( """ if not hasattr(g2p, "symbols"): logging.error( - f"Please make sure the G2P module passed into the IPATokenizer has a `symbols` attribute. " + f"Please make sure the G2P_paper module passed into the IPATokenizer has a `symbols` attribute. " f"This is required in order to build the tokenizer vocabulary.\n" f"Expected e.g. IPAG2P, found {type(g2p)}" ) - raise ValueError("G2P modules passed into the IPATokenizer must have `symbols` defined.") + raise ValueError("G2P_paper modules passed into the IPATokenizer must have `symbols` defined.") if locale is not None: validate_locale(locale) @@ -596,14 +596,14 @@ def encode(self, text: str) -> List[int]: def encode_from_g2p(self, g2p_text: List[str], raw_text: Optional[str] = None) -> List[int]: """ - Tokenize the `g2p_text` that has been already run through G2P. Each item in the `g2p_text` would be encoded as + Tokenize the `g2p_text` that has been already run through G2P_paper. Each item in the `g2p_text` would be encoded as one of the integer IDs predefined in `self._token2id`. Note that this function should be called after `self.text_preprocessing_func` and `self.g2p` functions Args: - g2p_text (List[str]): a sequence of tokens from G2P's output. It could be a sequence of phonemes, a sequence + g2p_text (List[str]): a sequence of tokens from G2P_paper's output. It could be a sequence of phonemes, a sequence of graphemes, or a mixture of both. For example, `['ˈ', 's', 'i', ' ', '#O', '#O', '#V']`, which is the - G2P's output of the text "see OOV", where '#' is prepended to each grapheme in order to distinguish + G2P_paper's output of the text "see OOV", where '#' is prepended to each grapheme in order to distinguish graphemes from phonemes if there are overlaps in between. The prefix '#' can be customized in `nemo_text_processing.g2p.modules.IPAG2P.grapheme_prefix`. raw_text (str): the original text after calling `self.text_preprocessing_func`. It is optional. It is only @@ -732,11 +732,11 @@ def encode(self, text): def encode_from_g2p(self, g2p_text: List[str], raw_text: Optional[str] = None): """ - Encodes text that has already been run through G2P. - Called for encoding to tokens after text preprocessing and G2P. + Encodes text that has already been run through G2P_paper. + Called for encoding to tokens after text preprocessing and G2P_paper. Args: - g2p_text: G2P's output, could be a mixture of Chinese phonemes and English letters. + g2p_text: G2P_paper's output, could be a mixture of Chinese phonemes and English letters. raw_text: original raw input """ ps, space, tokens = [], self.tokens[self.space], set(self.tokens) diff --git a/nemo_text_processing/text_normalization/es/__init__.py b/nemo/collections/tts/g2p/data/__init__.py similarity index 85% rename from nemo_text_processing/text_normalization/es/__init__.py rename to nemo/collections/tts/g2p/data/__init__.py index bd1fc7d433ef..5857cda3f17b 100644 --- a/nemo_text_processing/text_normalization/es/__init__.py +++ b/nemo/collections/tts/g2p/data/__init__.py @@ -12,4 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -LOCALIZATION = "eu" # Set to am for alternate formatting +from nemo.collections.tts.g2p.data.t5_g2p import T5G2PDataset + +__all__ = ["T5G2PDataset", "CTCG2PBPEDataset"] diff --git a/nemo_text_processing/g2p/data/ctc_g2p.py b/nemo/collections/tts/g2p/data/ctc_g2p.py similarity index 99% rename from nemo_text_processing/g2p/data/ctc_g2p.py rename to nemo/collections/tts/g2p/data/ctc_g2p.py index 792a92a10fa6..6b0052db6996 100644 --- a/nemo_text_processing/g2p/data/ctc_g2p.py +++ b/nemo/collections/tts/g2p/data/ctc_g2p.py @@ -37,7 +37,7 @@ def __init__( with_labels: bool = True, ): """ - Creates a dataset to train a CTC-based G2P models. + Creates a dataset to train a CTC-based G2P_paper models. Args: manifest_filepath: path to a .json manifest that contains "phoneme_field" and "grapheme_field" diff --git a/nemo_text_processing/g2p/data/data_utils.py b/nemo/collections/tts/g2p/data/data_utils.py similarity index 100% rename from nemo_text_processing/g2p/data/data_utils.py rename to nemo/collections/tts/g2p/data/data_utils.py diff --git a/nemo_text_processing/g2p/data/heteronym_classification_data.py b/nemo/collections/tts/g2p/data/heteronym_classification_data.py similarity index 100% rename from nemo_text_processing/g2p/data/heteronym_classification_data.py rename to nemo/collections/tts/g2p/data/heteronym_classification_data.py diff --git a/nemo_text_processing/g2p/data/t5_g2p.py b/nemo/collections/tts/g2p/data/t5_g2p.py similarity index 98% rename from nemo_text_processing/g2p/data/t5_g2p.py rename to nemo/collections/tts/g2p/data/t5_g2p.py index 0e4c5f2697b6..200d057fb0ab 100644 --- a/nemo_text_processing/g2p/data/t5_g2p.py +++ b/nemo/collections/tts/g2p/data/t5_g2p.py @@ -40,7 +40,7 @@ def __init__( with_labels: bool = True, ): """ - Dataset to train T5-based G2P generative model. + Dataset to train T5-based G2P_paper generative model. Args: manifest_filepath: path to a .json manifest that contains "phoneme_field" and "grapheme_field" diff --git a/nemo_text_processing/g2p/models/__init__.py b/nemo/collections/tts/g2p/models/__init__.py similarity index 84% rename from nemo_text_processing/g2p/models/__init__.py rename to nemo/collections/tts/g2p/models/__init__.py index 5f35864a4fd9..46c89fc49a6e 100644 --- a/nemo_text_processing/g2p/models/__init__.py +++ b/nemo/collections/tts/g2p/models/__init__.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from nemo_text_processing.g2p.models.ctc_g2p import CTCG2PModel -from nemo_text_processing.g2p.models.t5_g2p import T5G2PModel +from nemo.collections.tts.g2p.models.t5_g2p import T5G2PModel +from nemo.collections.tts.g2p.models.ctc_g2p import CTCG2PModel __all__ = ["T5G2PModel", "CTCG2PModel"] diff --git a/nemo_text_processing/g2p/models/ctc_g2p.py b/nemo/collections/tts/g2p/models/ctc_g2p.py similarity index 99% rename from nemo_text_processing/g2p/models/ctc_g2p.py rename to nemo/collections/tts/g2p/models/ctc_g2p.py index a456942c1ad8..2a7ad7965c02 100644 --- a/nemo_text_processing/g2p/models/ctc_g2p.py +++ b/nemo/collections/tts/g2p/models/ctc_g2p.py @@ -19,8 +19,8 @@ import torch from hydra.utils import instantiate -from nemo_text_processing.g2p.data.ctc_g2p import CTCG2PBPEDataset -from nemo_text_processing.g2p.models.g2p_model import G2PModel +from nemo.collections.tts.g2p.data.ctc_g2p import CTCG2PBPEDataset +from nemo.collections.tts.g2p.models.g2p_model import G2PModel from omegaconf import DictConfig, ListConfig, OmegaConf, open_dict from pytorch_lightning import Trainer from torch import nn diff --git a/nemo_text_processing/g2p/models/g2p_model.py b/nemo/collections/tts/g2p/models/g2p_model.py similarity index 100% rename from nemo_text_processing/g2p/models/g2p_model.py rename to nemo/collections/tts/g2p/models/g2p_model.py diff --git a/nemo_text_processing/g2p/models/heteronym_classification.py b/nemo/collections/tts/g2p/models/heteronym_classification.py similarity index 99% rename from nemo_text_processing/g2p/models/heteronym_classification.py rename to nemo/collections/tts/g2p/models/heteronym_classification.py index 2cb19ac8bd6e..ddfac56ef93a 100644 --- a/nemo_text_processing/g2p/models/heteronym_classification.py +++ b/nemo/collections/tts/g2p/models/heteronym_classification.py @@ -19,8 +19,8 @@ import torch from hydra.utils import instantiate -from nemo_text_processing.g2p.data.data_utils import get_heteronym_spans, get_wordid_to_phonemes, read_wordids -from nemo_text_processing.g2p.data.heteronym_classification_data import HeteronymClassificationDataset +from nemo.collections.tts.g2p.data.data_utils import get_heteronym_spans, get_wordid_to_phonemes, read_wordids +from nemo.collections.tts.g2p.data.heteronym_classification_data import HeteronymClassificationDataset from omegaconf import DictConfig from pytorch_lightning import Trainer diff --git a/nemo_text_processing/g2p/models/t5_g2p.py b/nemo/collections/tts/g2p/models/t5_g2p.py similarity index 98% rename from nemo_text_processing/g2p/models/t5_g2p.py rename to nemo/collections/tts/g2p/models/t5_g2p.py index ee919ffa6643..bc1ba7e88ab9 100644 --- a/nemo_text_processing/g2p/models/t5_g2p.py +++ b/nemo/collections/tts/g2p/models/t5_g2p.py @@ -17,8 +17,8 @@ import torch from hydra.utils import instantiate -from nemo_text_processing.g2p.data.t5_g2p import T5G2PDataset -from nemo_text_processing.g2p.models.g2p_model import G2PModel +from nemo.collections.tts.g2p.data.t5_g2p import T5G2PDataset +from nemo.collections.tts.g2p.models.g2p_model import G2PModel from omegaconf import DictConfig, OmegaConf from pytorch_lightning import Trainer from transformers import AutoTokenizer, T5ForConditionalGeneration diff --git a/nemo_text_processing/g2p/modules.py b/nemo/collections/tts/g2p/modules.py similarity index 98% rename from nemo_text_processing/g2p/modules.py rename to nemo/collections/tts/g2p/modules.py index 91141ac729e1..6108098cfa74 100644 --- a/nemo_text_processing/g2p/modules.py +++ b/nemo/collections/tts/g2p/modules.py @@ -23,7 +23,7 @@ import nltk import torch -from nemo_text_processing.g2p.data.data_utils import ( +from nemo.collections.tts.g2p.data.data_utils import ( GRAPHEME_CASE_MIXED, GRAPHEME_CASE_UPPER, LATIN_CHARS_ALL, @@ -81,7 +81,7 @@ def setup_heteronym_model( """ try: - from nemo_text_processing.g2p.models.heteronym_classification import HeteronymClassificationModel + from nemo.collections.tts.g2p.models.heteronym_classification import HeteronymClassificationModel self.heteronym_model = heteronym_model self.heteronym_model.set_wordid_to_phonemes(wordid_to_phonemes_file) @@ -102,7 +102,7 @@ def __init__( phoneme_probability: Optional[float] = None, mapping_file: Optional[str] = None, ): - """English G2P module. This module converts words from grapheme to phoneme representation using phoneme_dict in CMU dict format. + """English G2P_paper module. This module converts words from grapheme to phoneme representation using phoneme_dict in CMU dict format. Optionally, it can ignore words which are heteronyms, ambiguous or marked as unchangeable by word_tokenize_func (see code for details). Ignored words are left unchanged or passed through apply_to_oov_word for handling. Args: @@ -310,7 +310,7 @@ def __init__( mapping_file: Optional[str] = None, ) -> None: """ - Generic IPA G2P module. This module converts words from graphemes to International Phonetic Alphabet + Generic IPA G2P_paper module. This module converts words from graphemes to International Phonetic Alphabet representations. Optionally, it can ignore heteronyms, ambiguous words, or words marked as unchangeable by `word_tokenize_func` (see code for details). Ignored words are left unchanged or passed through `apply_to_oov_word` for handling. @@ -683,7 +683,7 @@ def __init__( mapping_file: Optional[str] = None, word_segmenter: Optional[str] = None, ): - """Chinese G2P module. This module first converts Chinese characters into pinyin sequences using pypinyin, then pinyin sequences would + """Chinese G2P_paper module. This module first converts Chinese characters into pinyin sequences using pypinyin, then pinyin sequences would be further converted into phoneme sequences using pinyin_dict_nv_22.10.txt dict file. For Chinese and English bilingual sentences, the English words would be converted into letters. Args: diff --git a/nemo/collections/tts/models/aligner.py b/nemo/collections/tts/models/aligner.py index 66705d516e0d..ce1eeba309ef 100644 --- a/nemo/collections/tts/models/aligner.py +++ b/nemo/collections/tts/models/aligner.py @@ -82,11 +82,13 @@ def _setup_normalizer(self, cfg): ) try: + import nemo_text_processing + self.normalizer = instantiate(cfg.text_normalizer, **normalizer_kwargs) except Exception as e: logging.error(e) raise ImportError( - "`pynini` not installed, please install via NeMo/nemo_text_processing/pynini_install.sh" + "`nemo_text_processing` not installed, see https://github.com/NVIDIA/NeMo-text-processing for more details" ) self.text_normalizer_call = self.normalizer.normalize @@ -96,6 +98,12 @@ def _setup_normalizer(self, cfg): def _setup_tokenizer(self, cfg): text_tokenizer_kwargs = {} if "g2p" in cfg.text_tokenizer: + # for backward compatibility + if self._is_model_being_restored() and cfg.text_tokenizer.g2p.get('_target_', None): + cfg.text_tokenizer.g2p['_target_'] = cfg.text_tokenizer.g2p['_target_'].replace( + "nemo_text_processing.g2p", "nemo.collections.tts.g2p" + ) + g2p_kwargs = {} if "phoneme_dict" in cfg.text_tokenizer.g2p: diff --git a/nemo/collections/tts/models/fastpitch.py b/nemo/collections/tts/models/fastpitch.py index 67d55cb70e88..2e3593e76115 100644 --- a/nemo/collections/tts/models/fastpitch.py +++ b/nemo/collections/tts/models/fastpitch.py @@ -170,13 +170,14 @@ def _setup_normalizer(self, cfg): normalizer_kwargs["whitelist"] = self.register_artifact( 'text_normalizer.whitelist', cfg.text_normalizer.whitelist ) - try: + import nemo_text_processing + self.normalizer = instantiate(cfg.text_normalizer, **normalizer_kwargs) except Exception as e: logging.error(e) raise ImportError( - "`pynini` not installed, please install via NeMo/nemo_text_processing/pynini_install.sh" + "`nemo_text_processing` not installed, see https://github.com/NVIDIA/NeMo-text-processing for more details" ) self.text_normalizer_call = self.normalizer.normalize @@ -187,6 +188,13 @@ def _setup_tokenizer(self, cfg): text_tokenizer_kwargs = {} if "g2p" in cfg.text_tokenizer: + + # for backward compatibility + if self._is_model_being_restored() and cfg.text_tokenizer.g2p.get('_target_', None): + cfg.text_tokenizer.g2p['_target_'] = cfg.text_tokenizer.g2p['_target_'].replace( + "nemo_text_processing.g2p", "nemo.collections.tts.g2p" + ) + g2p_kwargs = {} if "phoneme_dict" in cfg.text_tokenizer.g2p: @@ -199,6 +207,7 @@ def _setup_tokenizer(self, cfg): 'text_tokenizer.g2p.heteronyms', cfg.text_tokenizer.g2p.heteronyms, ) + # for backward compatability text_tokenizer_kwargs["g2p"] = instantiate(cfg.text_tokenizer.g2p, **g2p_kwargs) # TODO @xueyang: rename the instance of tokenizer because vocab is misleading. diff --git a/nemo/collections/tts/models/mixer_tts.py b/nemo/collections/tts/models/mixer_tts.py index fffaf82d5a06..5f59e93554c5 100644 --- a/nemo/collections/tts/models/mixer_tts.py +++ b/nemo/collections/tts/models/mixer_tts.py @@ -132,11 +132,13 @@ def _setup_normalizer(self, cfg): ) try: + import nemo_text_processing + self.normalizer = instantiate(cfg.text_normalizer, **normalizer_kwargs) except Exception as e: logging.error(e) raise ImportError( - "`pynini` not installed, please install via NeMo/nemo_text_processing/pynini_install.sh" + "`nemo_text_processing` not installed, see https://github.com/NVIDIA/NeMo-text-processing for more details" ) self.text_normalizer_call = self.normalizer.normalize @@ -146,6 +148,12 @@ def _setup_normalizer(self, cfg): def _setup_tokenizer(self, cfg): text_tokenizer_kwargs = {} if "g2p" in cfg.text_tokenizer: + # for backward compatibility + if self._is_model_being_restored() and cfg.text_tokenizer.g2p.get('_target_', None): + cfg.text_tokenizer.g2p['_target_'] = cfg.text_tokenizer.g2p['_target_'].replace( + "nemo_text_processing.g2p", "nemo.collections.tts.g2p" + ) + g2p_kwargs = {} if "phoneme_dict" in cfg.text_tokenizer.g2p: diff --git a/nemo/collections/tts/models/radtts.py b/nemo/collections/tts/models/radtts.py index e303c924f4b2..ea08346c8b9f 100644 --- a/nemo/collections/tts/models/radtts.py +++ b/nemo/collections/tts/models/radtts.py @@ -326,6 +326,12 @@ def parser(self): def _setup_tokenizer(self, cfg): text_tokenizer_kwargs = {} if "g2p" in cfg.text_tokenizer: + # for backward compatibility + if self._is_model_being_restored() and cfg.text_tokenizer.g2p.get('_target_', None): + cfg.text_tokenizer.g2p['_target_'] = cfg.text_tokenizer.g2p['_target_'].replace( + "nemo_text_processing.g2p", "nemo.collections.tts.g2p" + ) + g2p_kwargs = {} if "phoneme_dict" in cfg.text_tokenizer.g2p: @@ -363,7 +369,16 @@ def _setup_normalizer(self, cfg): 'text_normalizer.whitelist', cfg.text_normalizer.whitelist ) - self.normalizer = instantiate(cfg.text_normalizer, **normalizer_kwargs) + try: + import nemo_text_processing + + self.normalizer = instantiate(cfg.text_normalizer, **normalizer_kwargs) + self.text_normalizer_call = self.normalizer.normalize + except Exception as e: + logging.error(e) + raise ImportError( + "`nemo_text_processing` not installed, see https://github.com/NVIDIA/NeMo-text-processing for more details" + ) self.text_normalizer_call = self.normalizer.normalize if "text_normalizer_call_kwargs" in cfg: self.text_normalizer_call_kwargs = cfg.text_normalizer_call_kwargs diff --git a/nemo/collections/tts/models/tacotron2.py b/nemo/collections/tts/models/tacotron2.py index 311677ba7eb6..20fee29a7c31 100644 --- a/nemo/collections/tts/models/tacotron2.py +++ b/nemo/collections/tts/models/tacotron2.py @@ -307,11 +307,13 @@ def _setup_normalizer(self, cfg): ) try: + import nemo_text_processing + self.normalizer = instantiate(cfg.text_normalizer, **normalizer_kwargs) except Exception as e: logging.error(e) raise ImportError( - "`pynini` not installed, please install via NeMo/nemo_text_processing/pynini_install.sh" + "`nemo_text_processing` not installed, see https://github.com/NVIDIA/NeMo-text-processing for more details" ) self.text_normalizer_call = self.normalizer.normalize @@ -321,6 +323,12 @@ def _setup_normalizer(self, cfg): def _setup_tokenizer(self, cfg): text_tokenizer_kwargs = {} if "g2p" in cfg.text_tokenizer and cfg.text_tokenizer.g2p is not None: + # for backward compatibility + if self._is_model_being_restored() and cfg.text_tokenizer.g2p.get('_target_', None): + cfg.text_tokenizer.g2p['_target_'] = cfg.text_tokenizer.g2p['_target_'].replace( + "nemo_text_processing.g2p", "nemo.collections.tts.g2p" + ) + g2p_kwargs = {} if "phoneme_dict" in cfg.text_tokenizer.g2p: diff --git a/nemo/collections/tts/models/vits.py b/nemo/collections/tts/models/vits.py index a18c762803e2..832197195742 100644 --- a/nemo/collections/tts/models/vits.py +++ b/nemo/collections/tts/models/vits.py @@ -97,14 +97,28 @@ def _setup_normalizer(self, cfg): 'text_normalizer.whitelist', cfg.text_normalizer.whitelist ) - self.normalizer = instantiate(cfg.text_normalizer, **normalizer_kwargs) - self.text_normalizer_call = self.normalizer.normalize + try: + import nemo_text_processing + + self.normalizer = instantiate(cfg.text_normalizer, **normalizer_kwargs) + self.text_normalizer_call = self.normalizer.normalize + except Exception as e: + logging.error(e) + raise ImportError( + "`nemo_text_processing` not installed, see https://github.com/NVIDIA/NeMo-text-processing for more details" + ) if "text_normalizer_call_kwargs" in cfg: self.text_normalizer_call_kwargs = cfg.text_normalizer_call_kwargs def _setup_tokenizer(self, cfg): text_tokenizer_kwargs = {} if "g2p" in cfg.text_tokenizer and cfg.text_tokenizer.g2p is not None: + # for backward compatibility + if self._is_model_being_restored() and cfg.text_tokenizer.g2p.get('_target_', None): + cfg.text_tokenizer.g2p['_target_'] = cfg.text_tokenizer.g2p['_target_'].replace( + "nemo_text_processing.g2p", "nemo.collections.tts.g2p" + ) + g2p_kwargs = {} if "phoneme_dict" in cfg.text_tokenizer.g2p: diff --git a/nemo/collections/tts/torch/g2ps.py b/nemo/collections/tts/torch/g2ps.py index d66af6f82da7..e6347a0657a9 100644 --- a/nemo/collections/tts/torch/g2ps.py +++ b/nemo/collections/tts/torch/g2ps.py @@ -14,4 +14,3 @@ # TODO @xueyang: deprecate this file since no other places import modules from here anymore. However, # all checkpoints uploaded in ngc used this path. So it requires to update all ngc checkpoints g2p path as well. -from nemo_text_processing.g2p.modules import IPAG2P, BaseG2p, EnglishG2p diff --git a/nemo_text_processing/README.md b/nemo_text_processing/README.md deleted file mode 100644 index 181d6ddc8e3b..000000000000 --- a/nemo_text_processing/README.md +++ /dev/null @@ -1,8 +0,0 @@ -**nemo_text_processing** -========================== - -Introduction ------------- - -NeMo's `nemo_text_processing` is a Python package that is installed with the `nemo_toolkit`. -See [documentation](https://docs.nvidia.com/deeplearning/nemo/user-guide/docs/en/stable/nlp/text_normalization/wfst/wfst_text_normalization.html) for details. \ No newline at end of file diff --git a/nemo_text_processing/__init__.py b/nemo_text_processing/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/fst_alignment/README.md b/nemo_text_processing/fst_alignment/README.md deleted file mode 100644 index 99ae457db5c7..000000000000 --- a/nemo_text_processing/fst_alignment/README.md +++ /dev/null @@ -1,27 +0,0 @@ -This folder contains Python and C++ scripts that generate input output mappings from a specified FST graph and input string. -The required input FST graph needs to be provided as OpenFst FAR file. - -Requirements: -- Python: Pynini -- C++: Thrax, OpenFst - -Example Usage: - - -```python -python alignment.py --fst=fst.far --text="2615 Forest Av, 1 Aug 2016" --rule="tokenize_and_classify" --start=22 --end=26 - -Output: \ -inp string: |2615 Forest Av, 1 Aug 2016| \ -out string: |twenty six fifteen Forest Avenue , the first of august twenty sixteen| \ -inp indices: [22:26] -out indices: [55:69] \ -in: |2016| out: |twenty sixteen| -``` - - - -Disclaimer: - -The heuristic algorithm relies on monotonous alignment and can fail in certain situations, -e.g. when word pieces are reordered by the fst. \ No newline at end of file diff --git a/nemo_text_processing/fst_alignment/alignment.cpp b/nemo_text_processing/fst_alignment/alignment.cpp deleted file mode 100644 index aba2c3bab328..000000000000 --- a/nemo_text_processing/fst_alignment/alignment.cpp +++ /dev/null @@ -1,194 +0,0 @@ -//Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. -//Licensed under the Apache License, Version 2.0 (the "License"); -//you may not use this file except in compliance with the License. -//You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -//Unless required by applicable law or agreed to in writing, software -//distributed under the License is distributed on an "AS IS" BASIS, -//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -//See the License for the specific language governing permissions and -//limitations under the License. - - -#include -#include -#include -#include -#include - -using fst::StdArcLookAheadFst; -using thrax::GrmManager; -using fst::StdArc; -using fst::StdFst; -using namespace std; -typedef StdArcLookAheadFst LookaheadFst; - - -// This file takes 1. FST path 2. entire string 3. start position of substring 4. end (exclusive) position of substring -// and prints 1. mapped output string 2. start and end indices of mapped substring - -// Usage: - -// g++ -std=gnu++11 -I/include/ alignment.cpp -lfst -lthrax -ldl -L/lib -// ./a.out "tokenize_and_classify" "2615 Forest Av, 1 Aug 2016" 22 26 - -// Output: -// inp string: |2615 Forest Av, 1 Aug 2016| -// out string: |twenty six fifteen Forest Avenue , the first of august twenty sixteen| -// inp indices: [22:26] -// out indices: [55:69] -// in: |2016| out: |twenty sixteen| - -// Disclaimer: The heuristic algorithm relies on monotonous alignment and can fail in certain situations, -// e.g. when word pieces are reordered by the fst, e.g. - -// ./a.out "tokenize_and_classify" "$1" 0 1 -// inp string: |$1| -// out string: |one dollar| -// inp indices: [0:1] out indices: [0:3] -// in: |$| out: |one| - -// to prevent compiler error, not actually called -namespace fst { - #include - #include - #include - #include - - bool IsSTList(const std::string &source) { - std::ifstream strm(source, std::ios_base::in | std::ios_base::binary); - if (!strm) return false; - int32_t magic_number = 0; - ReadType(strm, &magic_number); - return magic_number == kSTListMagicNumber; - } - - bool IsSTTable(const std::string &source) { - std::ifstream strm(source); - if (!strm.good()) return false; - - int32_t magic_number = 0; - ReadType(strm, &magic_number); - return magic_number == kSTTableMagicNumber; - } -} - -char EPS = '\0'; -char WS= ' '; - - -int _get_aligned_index(const vector> &alignment, int index){ - int aligned_index = 0; - - int idx = 0; - while (idx < index){ - if (get<0>(alignment[aligned_index]) != EPS) {idx += 1;} - aligned_index += 1; - } - while (get<0>(alignment[aligned_index]) == EPS){ - aligned_index += 1; - } - return aligned_index; -} - -int _get_original_index(const vector> &alignment, int aligned_index){ - int og_index = 0; - int idx = 0; - while (idx < aligned_index) { - if (get<1>(alignment[idx]) != EPS) {og_index += 1;} - idx += 1; - } - return og_index; -} - - -tuple indexed_map_to_output(const vector> &alignment, int start, int end) { - - int aligned_start = _get_aligned_index(alignment, start); - int aligned_end = _get_aligned_index(alignment, end-1); - - string output_str = ""; - string input_str = ""; - for (const auto &i: alignment){ - output_str += get<1>(i); - input_str += get<0>(i); - } - - - while ((aligned_start -1 > 0) && (get<0>(alignment[aligned_start-1]) == EPS) && (isalpha(get<1>(alignment[aligned_start-1])) || (get<1>(alignment[aligned_start-1]) == EPS))) {aligned_start -= 1;} - - while (((aligned_end + 1) < alignment.size()) && (get<0>(alignment[aligned_end + 1]) == EPS) && (isalpha(get<1>(alignment[aligned_end + 1])) || (get<1>(alignment[aligned_end + 1]) == EPS))) {aligned_end += 1;} - - while (((aligned_end + 1) < alignment.size()) && (isalpha(get<1>(alignment[aligned_end + 1])) || get<1>(alignment[aligned_end + 1]) == EPS)) {aligned_end += 1;} - - int output_og_start_index = _get_original_index(alignment, aligned_start); - int output_og_end_index = _get_original_index(alignment, aligned_end+1); - - return make_tuple(output_og_start_index, output_og_end_index); - - -} - -int main(int argc, char * argv[]) { - if (argc != 6) - { - printf("\n Please provide 4 input arguments, 1st is the path to fst, 2nd the fst rule name, " - "3rd the full string in quotes, 4th is start index of word, 5th is exclusive end index of word."); - return 1; - } - unique_ptr grm_; - grm_.reset(new GrmManager); - string grm_file=argv[1]; - string grm_rule=argv[2]; - grm_->LoadArchive(grm_file); - LookaheadFst fst_(*(grm_->GetFst(grm_rule))); - - - string input = argv[3]; - typedef fst::StringCompiler Compiler; - typedef StdArc::StateId StateId; - typedef fst::StringPrinter Printer; - Compiler compiler; - thrax::GrmManager::MutableTransducer input_fst; - compiler(input, &input_fst); - - fst::ComposeFst output(input_fst,fst_); - thrax::GrmManager::MutableTransducer shortest_path; - fst::ShortestPath(output, &shortest_path); - - auto siter = shortest_path.Start(); - vector> alignment; - - while (shortest_path.Final(siter) == StdArc::Weight::Zero()) { - fst::ArcIterator aiter(shortest_path, siter); - if (aiter.Done()) { - cerr << "Unexpected format: Does not reach final state" << endl; - return 1; - } - const auto &arc = aiter.Value(); - alignment.push_back({(char)arc.ilabel, (char)arc.olabel }); - siter = arc.nextstate; - aiter.Next(); - if (!aiter.Done()) { - cerr << "Unexpected format: State has multiple outgoing arcs" << endl; - return 1; - } - } - - string output_str = ""; - int idx = 0; - for (const auto &i: alignment){ - if (get<1>(i) == EPS) continue; - output_str += get<1>(i); - } - - int start_index = atoi(argv[4]); - int end_index = atoi(argv[5]); - - tuple out_indices = indexed_map_to_output(alignment, start_index, end_index); - cout << "inp string: |" << argv[3] << "|" << endl; - cout << "out string: |" << output_str << "|" << endl; - cout << "inp indices: [" << start_index << ":" << end_index << "]" << endl; - cout << "out indices: [" << get<0>(out_indices) << ":" << get<1>(out_indices) << "]" << endl; - cout << "in: |" << input.substr(start_index, end_index - start_index) << "| out: |" << output_str.substr(get<0>(out_indices), (get<1>(out_indices)-get<0>(out_indices))) << "|" << endl; -} \ No newline at end of file diff --git a/nemo_text_processing/fst_alignment/alignment.py b/nemo_text_processing/fst_alignment/alignment.py deleted file mode 100644 index dbf2897d8fa0..000000000000 --- a/nemo_text_processing/fst_alignment/alignment.py +++ /dev/null @@ -1,254 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -from argparse import ArgumentParser -from typing import List - -import pynini -from pynini import Far - -from nemo.utils import logging - - -""" -This files takes 1. Far file containing a fst graph created by TN or ITN 2. entire string. -Optionally: 3. start position of substring 4. end (exclusive) position of substring -and returns input output mapping of all words in the string bounded by whitespace. -If start and end position specified returns -1. mapped output string 2. start and end indices of mapped substring - -Usage: - -python alignment.py --fst= --text=\"2615 Forest Av, 1 Aug 2016\" --rule=\"tokenize_and_classify\" --start=22 --end=26 - -Output: -inp string: |2615 Forest Av, 1 Aug 2016| -out string: |twenty six fifteen Forest Avenue , the first of august twenty sixteen| -inp indices: [22:26] -out indices: [55:69] -in: |2016| out: |twenty sixteen| - - -python alignment.py --fst= --text=\"2615 Forest Av, 1 Aug 2016\" --rule=\"tokenize_and_classify\" - -Output: -inp string: |2615 Forest Av, 1 Aug 2016| -out string: |twenty six fifteen Forest Avenue , the first of august twenty sixteen| -inp indices: [0:4] out indices: [0:18] -in: |2615| out: |twenty six fifteen| -inp indices: [5:11] out indices: [19:25] -in: |Forest| out: |Forest| -inp indices: [12:15] out indices: [26:34] -in: |Av,| out: |Avenue ,| -inp indices: [16:17] out indices: [39:44] -in: |1| out: |first| -inp indices: [18:21] out indices: [48:54] -in: |Aug| out: |august| -inp indices: [22:26] out indices: [55:69] -in: |2016| out: |twenty sixteen| - - -Disclaimer: The heuristic algorithm relies on monotonous alignment and can fail in certain situations, -e.g. when word pieces are reordered by the fst: - - -python alignment.py --fst= --text=\"$1\" --rule=\"tokenize_and_classify\" --start=0 --end=1 -inp string: |$1| -out string: |one dollar| -inp indices: [0:1] out indices: [0:3] -in: |$| out: |one| -""" - - -def parse_args(): - args = ArgumentParser("map substring to output with FST") - args.add_argument("--fst", help="FAR file containing FST", type=str, required=True) - args.add_argument( - "--rule", - help="rule name in FAR file containing FST", - type=str, - default='tokenize_and_classify', - required=False, - ) - args.add_argument( - "--text", - help="input string", - type=str, - default="2615 Forest Av, 90601 CA, Santa Clara. 10kg, 12/16/2018, $123.25. 1 Aug 2016.", - ) - args.add_argument("--start", help="start index of substring to be mapped", type=int, required=False) - args.add_argument("--end", help="end index of substring to be mapped", type=int, required=False) - return args.parse_args() - - -EPS = "" -WHITE_SPACE = "\u23B5" - - -def get_word_segments(text: str) -> List[List[int]]: - """ - Returns word segments from given text based on white space in form of list of index spans. - """ - spans = [] - cur_span = [0] - for idx, ch in enumerate(text): - if len(cur_span) == 0 and ch != " ": - cur_span.append(idx) - elif ch == " ": - cur_span.append(idx) - assert len(cur_span) == 2 - spans.append(cur_span) - cur_span = [] - elif idx == len(text) - 1: - idx += 1 - cur_span.append(idx) - assert len(cur_span) == 2 - spans.append(cur_span) - return spans - - -def create_symbol_table() -> pynini.SymbolTable: - """ - Creates and returns Pynini SymbolTable used to label alignment with ascii instead of integers - """ - table = pynini.SymbolTable() - for num in range(34, 200): # ascii alphanum + letter range - table.add_symbol(chr(num), num) - table.add_symbol(EPS, 0) - table.add_symbol(WHITE_SPACE, 32) - return table - - -def get_string_alignment(fst: pynini.Fst, input_text: str, symbol_table: pynini.SymbolTable): - """ - create alignment of input text based on shortest path in FST. Symbols used for alignment are from symbol_table - - Returns: - output: list of tuples, each mapping input character to output - """ - lattice = pynini.shortestpath(input_text @ fst) - paths = lattice.paths(input_token_type=symbol_table, output_token_type=symbol_table) - - ilabels = paths.ilabels() - olabels = paths.olabels() - logging.debug(paths.istring()) - logging.debug(paths.ostring()) - output = list(zip([symbol_table.find(x) for x in ilabels], [symbol_table.find(x) for x in olabels])) - paths.next() - assert paths.done() - output_str = "".join(map(remove, [x[1] for x in output])) - return output, output_str - - -def _get_aligned_index(alignment: List[tuple], index: int): - """ - Given index in contracted input string computes corresponding index in alignment (which has EPS) - """ - aligned_index = 0 - idx = 0 - - while idx < index: - if alignment[aligned_index][0] != EPS: - idx += 1 - aligned_index += 1 - while alignment[aligned_index][0] == EPS: - aligned_index += 1 - return aligned_index - - -def _get_original_index(alignment, aligned_index): - """ - Given index in aligned output, returns corresponding index in contracted output string - """ - - og_index = 0 - idx = 0 - while idx < aligned_index: - if alignment[idx][1] != EPS: - og_index += 1 - idx += 1 - return og_index - - -remove = lambda x: "" if x == EPS else " " if x == WHITE_SPACE else x - - -def indexed_map_to_output(alignment: List[tuple], start: int, end: int): - """ - Given input start and end index of contracted substring return corresponding output start and end index - - Args: - alignment: alignment generated by FST with shortestpath, is longer than original string since including eps transitions - start: inclusive start position in input string - end: exclusive end position in input string - - Returns: - output_og_start_index: inclusive start position in output string - output_og_end_index: exclusive end position in output string - """ - # get aligned start and end of input substring - aligned_start = _get_aligned_index(alignment, start) - aligned_end = _get_aligned_index(alignment, end - 1) # inclusive - - logging.debug(f"0: |{list(map(remove, [x[0] for x in alignment[aligned_start:aligned_end+1]]))}|") - - # extend aligned_start to left - while ( - aligned_start - 1 > 0 - and alignment[aligned_start - 1][0] == EPS - and (alignment[aligned_start - 1][1].isalpha() or alignment[aligned_start - 1][1] == EPS) - ): - aligned_start -= 1 - - while ( - aligned_end + 1 < len(alignment) - and alignment[aligned_end + 1][0] == EPS - and (alignment[aligned_end + 1][1].isalpha() or alignment[aligned_end + 1][1] == EPS) - ): - aligned_end += 1 - - while (aligned_end + 1) < len(alignment) and ( - alignment[aligned_end + 1][1].isalpha() or alignment[aligned_end + 1][1] == EPS - ): - aligned_end += 1 - - output_og_start_index = _get_original_index(alignment=alignment, aligned_index=aligned_start) - output_og_end_index = _get_original_index(alignment=alignment, aligned_index=aligned_end + 1) - return output_og_start_index, output_og_end_index - - -if __name__ == '__main__': - logging.setLevel(logging.INFO) - args = parse_args() - fst = Far(args.fst, mode='r') - try: - fst = fst[args.rule] - except: - raise ValueError(f"{args.rule} not found. Please specify valid --rule argument.") - input_text = args.text - - table = create_symbol_table() - alignment, output_text = get_string_alignment(fst=fst, input_text=input_text, symbol_table=table) - print(f"inp string: |{args.text}|") - print(f"out string: |{output_text}|") - - if args.start is None: - indices = get_word_segments(input_text) - else: - indices = [(args.start, args.end)] - for x in indices: - start, end = indexed_map_to_output(start=x[0], end=x[1], alignment=alignment) - print(f"inp indices: [{x[0]}:{x[1]}] out indices: [{start}:{end}]") - print(f"in: |{input_text[x[0]:x[1]]}| out: |{output_text[start:end]}|") diff --git a/nemo_text_processing/g2p/__init__.py b/nemo_text_processing/g2p/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/g2p/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/g2p/data/__init__.py b/nemo_text_processing/g2p/data/__init__.py deleted file mode 100644 index d218266f08a0..000000000000 --- a/nemo_text_processing/g2p/data/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from nemo_text_processing.g2p.data.ctc_g2p import CTCG2PBPEDataset -from nemo_text_processing.g2p.data.t5_g2p import T5G2PDataset - -__all__ = ["T5G2PDataset", "CTCG2PBPEDataset"] diff --git a/nemo_text_processing/hybrid/model_utils.py b/nemo_text_processing/hybrid/model_utils.py deleted file mode 100644 index 174ed6737704..000000000000 --- a/nemo_text_processing/hybrid/model_utils.py +++ /dev/null @@ -1,158 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import math -import re -from typing import List, Union - -import torch -from tqdm import tqdm - -from nemo.collections.common.parts import MLMScorer -from nemo.utils import logging - - -def init_models(model_name_list: str): - """ - returns dictionary of Masked Language Models by their HuggingFace name. - """ - model_names = model_name_list.split(",") - models = {} - for model_name in model_names: - device = 'cuda' if torch.cuda.is_available() else 'cpu' - models[model_name] = MLMScorer(model_name=model_name, device=device) - return models - - -def get_score(texts: Union[List[str], str], model: MLMScorer): - """Computes MLM score for list of text using model""" - try: - if isinstance(texts, str): - texts = [texts] - score = -1 * sum(model.score_sentences(texts)) / len(texts) - except Exception as e: - print(e) - print(f"Scoring error: {texts}") - score = math.inf - return score - - -def get_masked_score(text, model, do_lower=True): - """text is normalized prediction which contains <> around semiotic tokens. - If multiple tokens are present, multiple variants of the text are created where all but one ambiguous semiotic tokens are masked - to avoid unwanted reinforcement of neighboring semiotic tokens.""" - text = text.lower() if do_lower else text - spans = re.findall("<\s.+?\s>", text) - if len(spans) > 0: - text_with_mask = [] - - for match in re.finditer("<\s.+?\s>", text): - new_text = ( - text[: match.span()[0]] + match.group().replace("< ", "").replace(" >", "") + text[match.span()[1] :] - ) - new_text = re.sub("<\s.+?\s>", model.MASK_LABEL, new_text) - text_with_mask.append(new_text) - text = text_with_mask - - return get_score(text, model) - - -def _get_ambiguous_positions(sentences: List[str]): - """returns None or index list of ambigous semiotic tokens for list of sentences. - E.g. if sentences = ["< street > < three > A", "< saint > < three > A"], it returns [1, 0] since only - the first semiotic span / is ambiguous.""" - l_sets = [set([x]) for x in re.findall("<\s.+?\s>", sentences[0])] - for sentence in sentences[1:]: - spans = re.findall("<\s.+?\s>", sentence) - if len(spans) != len(l_sets): - return None - for i in range(len(spans)): - l_sets[i].add(spans[i]) - - ambiguous = [] - for span in l_sets: - ambiguous.append(len(span) > 1) - return ambiguous - - -def score_options(sentences: List[str], context_len, model, do_lower=True): - """return list of scores for each sentence in list where model is used for MLM Scoring.""" - scores = [] - if context_len is not None: - diffs = [find_diff(s, context_len) for s in sentences] - if len(set([len(d) for d in diffs])) == 1: - sentences = diffs - - ambiguous_positions = None - if sentences and isinstance(sentences[0], str): - ambiguous_positions = _get_ambiguous_positions(sentences) - - for sent in tqdm(sentences): - if isinstance(sent, list): # in case of set context len - option_scores = [get_masked_score(s, model, do_lower) for s in sent] - logging.debug(sent) - logging.debug(option_scores) - logging.debug("=" * 50) - if any(math.isnan(x) for x in option_scores): - av_score = math.inf - else: - av_score = round(sum(option_scores) / len(option_scores), 4) - scores.append(av_score) - elif isinstance(sent, str): # in case of full context - if ambiguous_positions: - matches = list(re.finditer("<\s.+?\s>", sent)) - for match, pos in zip(matches[::-1], ambiguous_positions[::-1]): - if not pos: - sent = ( - sent[: match.span()[0]] - + match.group().replace("< ", "").replace(" >", "") - + sent[match.span()[1] :] - ) - scores.append(round(get_masked_score(sent, model, do_lower=do_lower))) - else: - raise ValueError() - return scores - - -def find_diff(text, context_len=3): - """Finds parts of text normalized by WFST and returns them in list with a context of context_len""" - diffs = [] - pattern_start = "< " - pattern_end = " >" - - def __clean(s): - return s.replace(pattern_start, "").replace(pattern_end, "").replace(" ", " ") - - index_start = 0 - while pattern_start in text[index_start:]: - index_start = index_start + text[index_start:].index(pattern_start) - offset = index_start - if pattern_end in text[offset:]: - index_end = offset + text[offset:].index(pattern_end) + len(pattern_end) - center = __clean(text[index_start:index_end]) - - left_context = " ".join(__clean(text[:index_start]).split()[-context_len:]) - if len(left_context) > 0 and text[:index_start][-1].isspace(): - left_context = left_context + " " - right_context = " ".join(__clean(text[index_end:]).split()[:context_len]) - if len(right_context) > 0 and text[index_end][0].isspace(): - right_context = " " + right_context - diffs.append(left_context + center + right_context) - index_end += 1 - index_start = index_end + 1 - else: - break - if len(diffs) == 0: - diffs = [text] - return diffs diff --git a/nemo_text_processing/hybrid/utils.py b/nemo_text_processing/hybrid/utils.py deleted file mode 100644 index 84c892679354..000000000000 --- a/nemo_text_processing/hybrid/utils.py +++ /dev/null @@ -1,696 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import copy -import difflib -import json -import re -import string -from typing import List, Optional, Tuple, Union - -import pandas as pd -import pynini -from nemo_text_processing.inverse_text_normalization.en.taggers.cardinal import CardinalFst -from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer -from pynini.lib.rewrite import top_rewrite -from tqdm import tqdm - -from nemo.utils import logging - -DELIMITER = '~~' - -cardinal_graph = CardinalFst().graph_no_exception -cardinal_graph = ( - pynini.closure(pynini.union("In ", "in ")) + cardinal_graph + pynini.closure(pynini.accep(" ") + cardinal_graph) -) - -inverse_normalizer = InverseNormalizer() - - -def load_data(input_fs: List[str]): - """ - loads data from list of abs file paths - Returns: - inputs: List[str] list of abs file paths - targets: List[List[str]] list of targets, can contain multiple options for each target - sentences: List[List[str]] list of sentence options - labels: List[List[int]] list of labels (1,0) - """ - inputs = [] - sentences = [] - cur_sentences = [] - labels = [] - cur_labels = [] - for input_f in input_fs: - if input_f.endswith(".json"): - with open(input_f, "r") as f: - for line in f: - line = json.loads(line) - try: - inputs.append(line['text'].strip()) - sentences.append([line['gt_normalized'].strip()]) - labels.append([1]) - except Exception as e: - print(e) - raise ValueError(f"Check format for line {line}") - else: - with open(input_f, "r") as f: - for line in f: - if line != "\n": - try: - sent, label = line.strip().split(DELIMITER) - except Exception as e: - if line.startswith("#"): - continue - print(e) - raise ValueError(f"Check format for line {line}") - if label == "RAW": - inputs.append(sent) - elif label == "1": - cur_sentences.append(sent) - cur_labels.append(1) - elif label == "0": - cur_sentences.append(sent) - cur_labels.append(0) - else: - sentences.append(cur_sentences) - cur_sentences = [] - labels.append(cur_labels) - cur_labels = [] - - if len(cur_sentences) > 0: - sentences.append(cur_sentences) - labels.append(cur_labels) - assert len(inputs) == len(sentences) - targets = [[x for i, x in enumerate(sents) if ls[i]] for (sents, ls) in zip(sentences, labels)] - return inputs, targets, sentences, labels - - -def remove_whitelist_boudaries(x): - # remove raw whitelist - x = re.sub(r"\|raw_start\|[^|]+\|raw_end\|", "", x) - # remove norm text boundaries - x = x.replace("|norm_start|", "").replace("|norm_end|", "") - return x - - -def _clean_pre_norm_libritts(inputs: List[str], targets: List[List[str]]): - """ - standardizes format of inputs and targets before being normalized, so more rules apply. - This is specific for libritts. - """ - for i in range(len(targets)): - for j in range(len(targets[i])): - targets[i][j] = clean_libri_tts(targets[i][j]) - - for i in range(len(inputs)): - for target in targets[i]: - diffs = get_diff(a=inputs[i].lower(), b=target.lower()) - for diff in diffs[::-1]: - in_diff = inputs[i][diff[0][0] : diff[0][1]].lower() - tg_diff = target[diff[1][0] : diff[1][1]].lower() - replacement = inputs[i][: diff[0][0]] + tg_diff + inputs[i][diff[0][1] :] - if (in_diff == "s" and tg_diff == "z") or (in_diff == "z" and tg_diff == "s"): - inputs[i] = replacement - elif (in_diff == "re" and tg_diff == "er") or (in_diff == "er" and tg_diff == "re"): - inputs[i] = replacement - elif (in_diff == "me" and tg_diff == "") or (in_diff == "" and tg_diff == "me"): - inputs[i] = replacement - elif (in_diff == "ue" and tg_diff == "") or (in_diff == "" and tg_diff == "ue"): - inputs[i] = replacement - return inputs, targets - - -def _clean_pre_norm_google(inputs: List[str], targets: List[List[str]]): - """ - standardizes format of inputs and targets before being normalized, so more rules apply. - This is specific for google dataset. - """ - for i in range(len(inputs)): - - inputs[i] = re.sub(r"\$\s([0-9]{1,})", r"$\1", inputs[i]) - inputs[i] = re.sub(r"\bmr ", r"Mr. ", inputs[i]) - inputs[i] = re.sub(r"\bdr ", r"Dr. ", inputs[i]) - inputs[i] = re.sub(r"\bdr$", r"Dr.", inputs[i]) - inputs[i] = re.sub(r"\bmrs ", r"Mrs. ", inputs[i]) - inputs[i] = re.sub(r"\bjr ", r"Jr. ", inputs[i]) - inputs[i] = re.sub(r"\bjr$", r"Jr.", inputs[i]) - inputs[i] = re.sub(r"\dsr ", r"Sr. ", inputs[i]) - inputs[i] = re.sub(r"\dsr$", r"Sr.", inputs[i]) - for target in targets[i]: - diffs = get_diff(a=inputs[i].lower(), b=target.lower()) - for diff in diffs[::-1]: - in_diff = inputs[i][diff[0][0] : diff[0][1]].lower() - tg_diff = target[diff[1][0] : diff[1][1]].lower() - replacement = inputs[i][: diff[0][0]] + tg_diff + inputs[i][diff[0][1] :] - if (in_diff == "s" and tg_diff == "z") or (in_diff == "z" and tg_diff == "s"): - inputs[i] = replacement - elif (in_diff == "re" and tg_diff == "er") or (in_diff == "er" and tg_diff == "re"): - inputs[i] = replacement - elif (in_diff == "me" and tg_diff == "") or (in_diff == "" and tg_diff == "me"): - inputs[i] = replacement - elif (in_diff == "" and tg_diff == "u") or (in_diff == "u" and tg_diff == ""): - inputs[i] = replacement - elif (in_diff == "ue" and tg_diff == "") or (in_diff == "" and tg_diff == "ue"): - inputs[i] = replacement - elif re.sub(r"\.", "", in_diff) == re.sub(r"( |\.)", "", tg_diff): - inputs[i] = replacement - - return inputs, targets - - -def clean_pre_norm(inputs: List[str], targets: List[List[str]], dataset: Optional[str] = None): - """ - standardizes format of inputs and targets before being normalized, so more rules apply. - """ - # deep copy - pre_inputs = copy.deepcopy(inputs) - pre_targets = copy.deepcopy(targets) - - # --- data specific pre cleaning --- - if dataset == "libritts": - pre_inputs, pre_targets = _clean_pre_norm_libritts(inputs=pre_inputs, targets=pre_targets) - elif dataset == "google": - pre_inputs, pre_targets = _clean_pre_norm_google(inputs=pre_inputs, targets=pre_targets) - else: - pass - - # --- general pre cleaning --- - for i in range(len(pre_inputs)): - pre_inputs[i] = re.sub("librivox.org", "librivox dot org", pre_inputs[i]) - pre_inputs[i] = re.sub( - rf"([0-9]?[0-9](\.|:)[0-9][0-9]\s?)(a|A|p|P)(\.?)\s(M|m)(\.?)", rf"\1\3\4\5\6", pre_inputs[i] - ) - # pre_inputs[i] =re.sub(rf"\b(S|s)t\.", rf"saint", pre_inputs[i]) - return pre_inputs, pre_targets - - -def _clean_post_norm_libritts(inputs: List[str], targets: List[List[str]], norm_texts): - return targets, norm_texts - - -def _clean_post_norm_google(inputs: List[str], targets: List[List[str]], norm_texts): - """ - standardizes format of inputs and targets, and predicted normalizations for easier evaluation. - This is specific for google dataset. - """ - for i in range(len(targets)): - for target in targets[i]: - for j, norm in enumerate(norm_texts[i][0]): - diffs = get_diff(a=norm.lower(), b=target.lower()) - for diff in diffs[::-1]: - norm_diff = norm[diff[0][0] : diff[0][1]].lower() - tg_diff = target[diff[1][0] : diff[1][1]].lower() - replacement = norm[: diff[0][0]] + tg_diff + norm[diff[0][1] :] - if norm_diff == re.sub(r" ", "", tg_diff): - norm_texts[i][0][j] = replacement - - return targets, norm_texts - - -def _clean_post_general(str) -> str: - """ - standardizes format of inputs and targets, and predicted normalizations for easier evaluation. - """ - str = re.sub(rf" oh ", " zero ", str) - str = re.sub(rf" oh$", " zero", str) - str = re.sub(rf"^oh ", "zero ", str) - # str = re.sub(rf" o ", " zero ", str) - str = re.sub(rf"\sO\b", "zero", str) - str = re.sub(rf" o$", " zero", str) - str = re.sub(rf"^o ", "zero ", str) - str = re.sub(rf"'o ", "'zero ", str) - str = str.replace("mountain", "mount") - return str - - -def _clean_targets(str) -> str: - """Clean ground truth options.""" - str = re.sub(rf" o ", " zero ", str) - return str - - -def adjust_pred(pred: str, gt: str, dataset: str, delim_present=True): - """Standardize prediction format to make evaluation easier""" - orig_pred = pred - orig_gt = gt - if delim_present and not re.search(rf"< (.*?) >", pred): - return pred - pred = re.sub(rf"< ", "", pred) - pred = re.sub(rf" >", "", pred) - pred = pred.lower().strip() - gt = gt.lower().strip() - can_be_adjusted = False - - if dataset in ["google", "libritts"] and pred != gt: - if is_date(pred=pred, gt=gt, cardinal_graph=cardinal_graph): - pred = gt - elif contains_month(pred, gt): - pred = re.sub(r",", "", pred) - gt = re.sub(r",", "", gt) - pred = re.sub(r" zero ", " o ", pred) - gt = re.sub(r" zero ", " o ", gt) - gt = re.sub(rf" +", " ", gt) - pred = re.sub(rf" +", " ", pred) - - if pred != gt: - gt_itn = inverse_normalizer.normalize(gt, verbose=False) - pred_itn = inverse_normalizer.normalize(pred, verbose=False) - if len(gt_itn) == len(pred_itn) and set(gt_itn) == set(pred_itn): - can_be_adjusted = True - pred = gt - elif " of " in gt: - gt = re.sub(r"(^the | of)", "", gt) - idx = gt.index(" ") - idx2 = (gt[idx + 1 :].index(" ") if " " in gt[idx + 1 :] else len(gt[idx + 1 :])) + idx + 1 - gt = gt[idx + 1 : idx2] + " " + gt[:idx] + gt[idx2:] - if dataset == "libritts" and pred != gt: - if "dollar" in gt: - gt = re.sub(rf"\band\b", "", gt) - pred = re.sub(rf"\band\b", "", pred) - if re.search(r"\bus dollar", pred) and not re.search(r"\bus dollar", gt): - pred = re.sub(rf"\bus dollar", "dollar", pred) - else: - gt = re.sub(rf"(\bthe\b|\.)", "", gt) - pred = re.sub(rf"\bone\b", "a", pred) - gt = re.sub(rf"\bmr\b", "mister", gt) - gt = re.sub(rf"\bmrs\b", "misses", gt) - gt = re.sub(rf"\bdr\b", "doctor", gt) - gt = re.sub(rf"\bco\b", "company", gt) - if gt != pd and dataset in ["google", "libritts"]: - if gt.replace("/", "").replace(" ", " ") == pred.replace("slash", "").replace(" ", " "): - pred = gt - elif gt in ["s", "z"] and pred in ["s", "z"]: - pred = gt - elif gt == "hash tag" and pred == "hash": - pred = "hash tag" - elif gt[:-2] == pred[:-2] and gt[-2:] in ["er", "re"] and pred[-2:] in ["er", "re"]: - pred = gt - # elif gt.replace("-", " ").replace(" ", " ") == pred.replace("minus", "").replace(" ", " "): - # pred = gt - elif gt.replace("to", "").replace("-", "") == pred.replace("to", "").replace("-", ""): - pred = gt - - gt = re.sub(rf" +", " ", gt) - pred = re.sub(rf"(\.)", "", pred) - pred = re.sub(rf" +", " ", pred) - if gt == pred: - can_be_adjusted = True - if can_be_adjusted: - if delim_present: - res = f" < {orig_gt} > " - else: - res = orig_gt - return res - else: - return orig_pred - - -def clean_post_norm( - inputs: List[str], - targets: List[List[str]], - norm_texts, - dataset: Optional[str] = None, - delim_present: Optional[bool] = True, -): - """ - Args: - inputs (List[str]): inputs - targets (List[List[str]]): targets - norm_texts (List[(List[str], List[float])]): List of normalization options, weights - dataset (Optional[str], optional): _description_. Defaults to None. - delim_present (Optional[str], optional): The flag indicates whether normalization output contain delimiters "<>". - Set to False for NN baseline. - """ - # deep copy - post_norm_texts = copy.deepcopy(norm_texts) - post_targets = copy.deepcopy(targets) - - # --- data specific pre cleaning --- - if dataset == "libritts": - post_targets, post_norm_texts = _clean_post_norm_libritts( - inputs=inputs, targets=post_targets, norm_texts=post_norm_texts - ) - elif dataset == "google": - post_targets, post_norm_texts = _clean_post_norm_google( - inputs=inputs, targets=post_targets, norm_texts=post_norm_texts - ) - - else: - pass - - # --- general pre cleaning --- - - for i in range(len(targets)): - for j, x in enumerate(post_targets[i]): - post_targets[i][j] = _clean_post_general(x) - for j, x in enumerate(post_norm_texts[i][0]): - if x.count("< ") != x.count(" >"): - x = x.replace("<", "< ").replace(">", " >").replace(" ", " ") - post_norm_texts[i][0][j] = _clean_post_general(x) - if dataset in ["libritts", "google"]: - for i, _targets in enumerate(post_targets): - for jj, option in enumerate(post_norm_texts[i][0]): - for _, _target in enumerate(_targets): - - if not delim_present: - # nn doesn't have punctuation marks that leads for diff_pred_gt mismatch - _target = remove_punctuation(_target, remove_spaces=False, do_lower=True) - option = remove_punctuation(option, remove_spaces=False, do_lower=True) - - diffs = diff_pred_gt(pred=option, gt=_target) - for diff in diffs[::-1]: - if diff[0][1] - diff[0][0] == 0 and diff[1][1] - diff[1][0] == 0: - continue - pred = option[diff[0][0] : diff[0][1]] - gt = _target[diff[1][0] : diff[1][1]] - logging.debug(f"pred: |{pred}|\tgt: |{gt}|") - new_pred = adjust_pred(pred=pred, gt=gt, dataset=dataset, delim_present=delim_present) - new_pred = ( - post_norm_texts[i][0][jj][: diff[0][0]] - + new_pred - + post_norm_texts[i][0][jj][diff[0][1] :] - ) - logging.debug(f"|{post_norm_texts[i][0][jj]}| -> |{new_pred}|") - post_norm_texts[i][0][jj] = new_pred - return post_targets, post_norm_texts - - -def clean_libri_tts(target: str): - """ - Replace abbreviations in LibriTTS dataset - """ - - # Normalized text in LibriTTS by Google which contains abbreviations from `libri_sometimes_converts_abbrs` sometimes wasn't converted. - libri_sometimes_converts_abbrs = {"St.": "saint", "Rev.": "reverend"} - - # Normalized text in LibriTTS by Google which contains abbreviations from `libri_wo_changes_abbrs` wasn't converted. - libri_wo_changes_abbrs = {"vs.": "versus"} - - google_abbr2expand = { - "mr": "mister", - "Mr": "Mister", - "mrs": "misses", - "Mrs": "Misses", - "dr": "doctor", - "Dr": "Doctor", - "drs": "doctors", - "Drs": "Doctors", - "lt": "lieutenant", - "Lt": "Lieutenant", - "sgt": "sergeant", - "Sgt": "Sergeant", - "st": "saint", - "St": "Saint", - "jr": "junior", - "Jr": "Junior", - "maj": "major", - "Maj": "Major", - "hon": "honorable", - "Hon": "Honorable", - "gov": "governor", - "Gov": "Governor", - "capt": "captain", - "Capt": "Captain", - "esq": "esquire", - "Esq": "Esquire", - "gen": "general", - "Gen": "General", - "ltd": "limited", - "Ltd": "Limited", - "rev": "reverend", - "Rev": "Reverend", - "col": "colonel", - "Col": "Colonel", - "and co": "and Company", - "and Co": "and Company", - "mt": "mount", - "Mt": "Mount", - "ft": "fort", - "Ft": "Fort", - "tenn": "tennessee", - "Tenn": "Tennessee", - "vs": "versus", - "Vs": "Versus", - "&": "and", - "§": "section", - "#": "hash", - "=": "equals", - } - - # let's normalize `libri_only_remove_dot_abbrs` abbreviations, because google doesn't do it well - for abbr in google_abbr2expand.keys(): - if abbr in target: - # replace abbr in google text via regex and using \b to match only whole words, keep original 1 and 2 groups - target = re.sub(rf'(^|\s|\W){abbr}($|\s)', rf"\1{google_abbr2expand[abbr]}\2", target) - - # let's normalize `libri_sometimes_converts_abbrs` abbreviations manually, google sometimes forgets to expand them - for abbr, t in libri_sometimes_converts_abbrs.items(): - target = target.replace(abbr, t) - - # let's normalize `libri_wo_changes_abbrs` abbreviations manually, google doesn't change, but they should be - for abbr, t in libri_wo_changes_abbrs.items(): - target = target.replace(abbr, t) - - return target - - -def remove_punctuation(text: str, remove_spaces=True, do_lower=True, lang="en", exclude=None): - """Removes punctuation (and optionally spaces) in text for better evaluation""" - all_punct_marks = string.punctuation - - if exclude is not None: - for p in exclude: - all_punct_marks = all_punct_marks.replace(p, "") - text = re.sub("[" + all_punct_marks + "]", " ", text) - - if lang == "en": - # remove things like \x94 and \x93 - text = re.sub(r"[^\x00-\x7f]", r" ", text) - - text = re.sub(r" +", " ", text) - if remove_spaces: - text = text.replace(" ", "").replace("\u00A0", "").strip() - - if do_lower: - text = text.lower() - return text.strip() - - -def get_alternative_label(pred: str, targets: List[str]) -> bool: - """Returns true if prediction matches target options""" - - def _relax_diff(text): - text = text.replace("us dollars", "dollars") - text = text.replace("etcetera", "").replace("etc", "") - text = text.replace("one half ounce", "").replace("half an ounce", "") - text = text.replace("television", "").replace("t v ", " ").replace("tv", "") - text = text.replace("hundred", "") - text = text.replace("forty two", "").replace("four two", "") - text = text.replace("re", "").replace("er", "") - text = text.replace("ou", "").replace("o", "") - text = text.replace(" ", " ").strip() - return text - - acceptable = False - pred = remove_punctuation(pred, remove_spaces=False, do_lower=True) - for target in targets: - target = _clean_post_general(remove_punctuation(target, remove_spaces=False, do_lower=True)) - target = _clean_targets(remove_punctuation(target, remove_spaces=False, do_lower=True)) - if _relax_diff(target) == _relax_diff(pred): - acceptable = True - break - return acceptable - - -def get_labels(targets: List[str], norm_texts_weights: List[Tuple[str, str]], lang="en",) -> List[List[str]]: - """ - Assign labels to generated normalization options (1 - for ground truth, 0 - other options) - Args: - targets: ground truth normalization sentences - norm_texts_weights: List of tuples: (normalization options, weights of normalization options) - returns: - List of labels [1, 0] for every normalization option - """ - print("Assign labels to generated normalization options...") - labels = [] - for i, cur_targets in tqdm(enumerate(targets)): - curr_labels = [] - cur_targets = [_clean_targets(t) for t in cur_targets] - for norm_option in norm_texts_weights[i][0]: - norm_option = _clean_targets(norm_option) - norm_option = remove_whitelist_boudaries(norm_option) - - if is_correct(pred=norm_option, targets=cur_targets, lang=lang): - curr_labels.append(1) - elif get_alternative_label(pred=norm_option, targets=cur_targets): - curr_labels.append(1) - else: - curr_labels.append(0) - labels.append(curr_labels) - return labels - - -def contains_month(pred, gt): - """Check is the pred/gt contain month in the span""" - months = [ - "january", - "february", - "march", - "april", - "may", - "june", - "july", - "august", - "september", - "october", - "november", - "december", - ] - - for mon in months: - if mon in gt and mon in pred: - return True - return False - - -def is_date(pred, gt, cardinal_graph): - """Returns True is pred and gt are date format modifications and are equal.""" - is_date_case = False - - # for cases "1890" -> "one thousand eight hundred ninety" vs "eighteen ninety" - if "thousand" in pred and "hundred" in pred and pred.strip().split()[-2:] == gt.strip().split()[-2:]: - is_date_case = True - elif "thousand" in gt and "hundred" in gt and gt.strip().split()[-2:] == pred.strip().split()[-2:]: - is_date_case = True - else: - try: - if top_rewrite(gt.replace(" oh ", " zero ").replace(" o ", " zero "), cardinal_graph).replace( - " ", "" - ) == top_rewrite(pred.replace(" oh ", " zero ").replace(" o ", " zero "), cardinal_graph).replace(" ", ""): - is_date_case = True - except: - pass - - return is_date_case - - -def is_correct(pred: str, targets: Union[List[str], str], lang: str) -> bool: - """ - returns True if prediction matches targets for language lang. - """ - if isinstance(targets, List): - targets = [remove_punctuation(x, remove_spaces=True, do_lower=True, lang=lang) for x in targets] - else: - targets = [remove_punctuation(targets, remove_spaces=True, do_lower=True, lang=lang)] - - pred = remove_punctuation(pred, remove_spaces=True, do_lower=True) - return pred in targets - - -def print_df(df): - """ - prints data frame - """ - with pd.option_context( - "display.max_rows", None, "display.max_columns", None, "display.width", 1000, "display.max_colwidth", 400, - ): - print(df) - - -def get_diff(a: str, b: str): - """returns list of different substrings between and b - - Returns: - list of Tuple(pred start and end, gt start and end) subsections - """ - s = difflib.SequenceMatcher(None, a, b, autojunk=False) - - # s contains a list of triples. Each triple is of the form (i, j, n), and means that a[i:i+n] == b[j:j+n]. - # The triples are monotonically increasing in i and in j. - s = s.get_matching_blocks() - s = [x for x in s if x[2] != 1] - # get not matching blocks - matches = [[0, 0, 0]] + s - unmatches_l = [] - unmatches_r = [] - for l, r in zip(matches[:-1], matches[1:]): - unmatches_l.append([l[0] + l[2], r[0]]) - unmatches_r.append([l[1] + l[2], r[1]]) - - result = list(zip(unmatches_l, unmatches_r)) - - for item in list(zip(unmatches_l, unmatches_r)): - logging.debug(f"a: {a[item[0][0]:item[0][1]]}") - logging.debug(f"b: {b[item[1][0]:item[1][1]]}") - logging.debug("=" * 20) - return result[1:] - - -def diff_pred_gt(pred: str, gt: str): - """returns list of different substrings between prediction and gt - relies on that prediction uses '< ' ' >' - - Args: - pred (str): prediction - gt (str): ground truth - - Returns: - list of Tuple(pred start and end, gt start and end) subsections - - e.g. pred="< Edward third >., king Our own . loss had been < two thousand two hundred >" - gt ="Edward III., king Our own loss had been twenty two hundred" - --> [([0, 16], [0, 10]), ([32, 34], [26, 26]), ([48, 76], [40, 58])] - """ - s = difflib.SequenceMatcher(None, pred, gt, autojunk=False) - - # s contains a list of triples. Each triple is of the form (i, j, n), and means that a[i:i+n] == b[j:j+n]. - # The triples are monotonically increasing in i and in j. - s = s.get_matching_blocks() - - left = list(re.finditer("< ", pred)) - left = [x.start() for x in left] - right = list(re.finditer(" >", pred)) - right = [x.end() for x in right] - left = [-1] + left + [len(pred)] - right = [0] + right + [len(pred)] - - matches = [] - assert len(left) == len(right) - idx = 1 - for i, seq in enumerate(s): - if i == len(s) - 1 and seq[2] == 0: - break - while idx < len(left) - 1 and (seq[0] >= right[idx]): - idx += 1 - - if right[idx - 1] <= seq[0] < left[idx] and (seq[0] + seq[2]) <= left[idx]: - matches.append(seq) - - # get not matching blocks - matches = [[0, 0, 0]] + matches + [[len(pred), len(gt), 0]] - unmatches_l = [] - unmatches_r = [] - for l, r in zip(matches[:-1], matches[1:]): - unmatches_l.append([l[0] + l[2], r[0]]) - unmatches_r.append([l[1] + l[2], r[1]]) - - result = list(zip(unmatches_l, unmatches_r)) - - for item in list(zip(unmatches_l, unmatches_r)): - logging.debug(f"pred: {pred[item[0][0]:item[0][1]]}") - logging.debug(f"gt : {gt[item[1][0]:item[1][1]]}") - logging.debug("=" * 20) - return result diff --git a/nemo_text_processing/hybrid/wfst_lm_rescoring.py b/nemo_text_processing/hybrid/wfst_lm_rescoring.py deleted file mode 100644 index 72b3b59bb62a..000000000000 --- a/nemo_text_processing/hybrid/wfst_lm_rescoring.py +++ /dev/null @@ -1,335 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import argparse -import os -import pickle -import re -import shutil -from typing import Dict, List - -import model_utils -import pandas as pd -import utils -from joblib import Parallel, delayed -from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio -from tqdm import tqdm - -from nemo.utils import logging - -parser = argparse.ArgumentParser(description="Re-scoring") -parser.add_argument("--lang", default="en", type=str, choices=["en"]) -parser.add_argument("--n_tagged", default=100, type=int, help="Number WFST options") -parser.add_argument("--context_len", default=-1, type=int, help="Context length, -1 to use full context") -parser.add_argument("--threshold", default=0.2, type=float, help="delta threshold value") -parser.add_argument("--overwrite_cache", action="store_true", help="overwrite cache") -parser.add_argument("--model_name", type=str, default="bert-base-uncased") -parser.add_argument("--cache_dir", default='cache', type=str, help="use cache dir") -parser.add_argument( - "--data", - default="text_normalization_dataset_files/EngConf.txt", - help="For En only. Path to a file for evaluation.", -) -parser.add_argument("--n_jobs", default=-2, type=int, help="The maximum number of concurrently running jobs") -parser.add_argument( - "--models", default="mlm_bert-base-uncased", type=str, help="Comma separated string of model names" -) -parser.add_argument( - "--regenerate_pkl", - action="store_true", - help="Set to True to re-create pickle file with WFST normalization options", -) -parser.add_argument("--batch_size", default=200, type=int, help="Batch size for parallel processing") - - -def rank(sentences: List[str], labels: List[int], models: Dict[str, 'Model'], context_len=None, do_lower=True): - """ - computes scores for each sentences using all provided models and returns summary in data frame - """ - df = pd.DataFrame({"sent": sentences, "labels": labels}) - for model_name, model in models.items(): - scores = model_utils.score_options( - sentences=sentences, context_len=context_len, model=model, do_lower=do_lower - ) - df[model_name] = scores - return df - - -def threshold_weights(norm_texts_weights, delta: float = 0.2): - """ - norm_texts_weights: list of [ List[normalized options of input], list[weights] ] - delta: delta to add to minimum weight in options to compose upper limit for threshhold - - returns: - filter list of same format as input - """ - # threshold value is factor applied to lowest/first weight of all normalization options for every input - res = [] - for i, options_weights in enumerate(norm_texts_weights): - thresh = options_weights[1][0] + delta # minimum weight plus delta - item = [x for x in zip(*options_weights)] - # filters out all options for every input that is larger than threshold - res.append(list(filter(lambda x: x[1] < thresh, item))) - - return [list(map(list, zip(*item))) for item in res] - - -def _get_unchanged_count(text): - """ - returns number of unchanged words in text - """ - exclude = '#$%&<>' - - # remove normalized whitelist - text = re.sub(r"\|norm_start\|[^|]+\|norm_end\|", "", text) - # remove raw text boundaries - text = text.replace("|raw_start|", "").replace("|raw_end|", "") - - start_pattern = "<" - end_pattern = ">" - - text = utils.remove_punctuation(text, remove_spaces=False, do_lower=False, exclude=exclude) - text_clean = "" - for ch in text: - if ch.isalpha() or ch.isspace() or ch in [start_pattern, end_pattern]: - text_clean += ch - else: - text_clean += " " + ch + " " - - text = text_clean - unchanged_count = 0 - skip = False - - for word in text.split(): - if start_pattern == word: - skip = True - elif end_pattern == word: - skip = False - elif not skip: - unchanged_count += 1 - return unchanged_count - - -def _get_replacement_count(text): - """ - returns number of token replacements - """ - start_pattern = "<" - end_pattern = ">" - return min(text.count(start_pattern), text.count(end_pattern)) - - -def threshold(norm_texts_weights, unchanged=True, replacement=True): - """ - Reduces the number of WFST options based for LM rescoring. - - Args: - :param norm_texts_weights: WFST options with associated weight - :param unchanged: set to True to filter out examples based on number of words left unchanged - (punct is not taken into account) - :param replacement: set to True to filter out examples based on number of replacements made - (Given A and B are WFST options, if the number of unchanged for A and B are the same, - the option with a smaller number of replacements is preferable (i.e., larger span)). - - :return: WFST options with associated weight (reduced) - """ - - def __apply(norm_texts_weights, f, use_min=True): - inputs_filtered = [] - for example in norm_texts_weights: - texts = example[0] - counts = [f(t) for t in texts] - [logging.debug(f"{c} -- {t}") for t, c in zip(texts, counts)] - target_count = min(counts) if use_min else max(counts) - filtered_texts = [] - filtered_weights = [] - for i, c in enumerate(counts): - if c == target_count: - filtered_texts.append(example[0][i]) - filtered_weights.append(example[1][i]) - inputs_filtered.append([filtered_texts, filtered_weights]) - return inputs_filtered - - logging.debug("BASIC THRESHOLDING INPUT:") - [logging.debug(x) for x in norm_texts_weights[0][0]] - if unchanged: - norm_texts_weights = __apply(norm_texts_weights, _get_unchanged_count) - logging.debug("AFTER UNCHANGED FILTER:") - [logging.debug(x) for x in norm_texts_weights[0][0]] - - if replacement: - norm_texts_weights = __apply(norm_texts_weights, _get_replacement_count) - logging.debug("AFTER REPLACEMENT FILTER:") - [logging.debug(x) for x in norm_texts_weights[0][0]] - - return norm_texts_weights - - -def main(): - args = parser.parse_args() - - logging.setLevel(logging.INFO) - lang = args.lang - input_f = args.data - - if args.data == "text_normalization_dataset_files/LibriTTS.json": - args.dataset = "libritts" - elif args.data == "text_normalization_dataset_files/GoogleTN.json": - args.dataset = "google" - else: - args.dataset = None - if not os.path.exists(args.data): - raise FileNotFoundError(f"{args.data} file not found") - - print("Create Masked Language Model...") - models = model_utils.init_models(model_name_list=args.model_name) - input_fs = input_f.split(",") - print("LOAD DATA...") - inputs, targets, _, _ = utils.load_data(input_fs) - pre_inputs, pre_targets = utils.clean_pre_norm(dataset=args.dataset, inputs=inputs, targets=targets) - - print("INIT WFST...") - normalizer = NormalizerWithAudio( - input_case="cased", lang=lang, cache_dir=args.cache_dir, lm=True, overwrite_cache=args.overwrite_cache - ) - - print("APPLYING NORMALIZATION RULES...") - p_file = ( - f"norm_texts_weights_{args.n_tagged}_{os.path.basename(args.data)}_{args.context_len}_{args.threshold}.pkl" - ) - - if not os.path.exists(p_file) or args.regenerate_pkl: - print(f"Creating WFST and saving to {p_file}") - - def __process_batch(batch_idx, batch, dir_name): - normalized = [] - for x in tqdm(batch): - ns, ws = normalizer.normalize(x, n_tagged=args.n_tagged, punct_post_process=False) - ns = [re.sub(r"<(.+?)>", r"< \1 >", x) for x in ns] - normalized.append((ns, ws)) - with open(f"{dir_name}/{batch_idx}.p", "wb") as handle: - pickle.dump(normalized, handle, protocol=pickle.HIGHEST_PROTOCOL) - - print(f"Batch -- {batch_idx} -- is complete") - return batch_idx - - # to save intermediate results to a file - batch = min(len(pre_inputs), args.batch_size) - - tmp_dir = f"/tmp/{os.path.basename(args.data)}" - if os.path.exists(tmp_dir): - shutil.rmtree(tmp_dir) - os.makedirs(tmp_dir, exist_ok=True) - - batch_ids = Parallel(n_jobs=args.n_jobs)( - delayed(__process_batch)(idx, pre_inputs[i : i + batch], tmp_dir) - for idx, i in enumerate(range(0, len(pre_inputs), batch)) - ) - - # aggregate all intermediate results - norm_texts_weights = [] - for batch_id in batch_ids: - batch_f = f"{tmp_dir}/{batch_id}.p" - norm_texts_weights.extend(pickle.load(open(batch_f, "rb"))) - - with open(p_file, "wb") as handle: - pickle.dump(norm_texts_weights, handle, protocol=pickle.HIGHEST_PROTOCOL) - else: - print(f"Loading WFST from {p_file}") - norm_texts_weights = pickle.load(open(p_file, "rb")) - - print("THRESHOLDING...") - # apply weights threshold to reduce number of options - - if args.threshold > 0: - norm_texts_weights = threshold_weights(norm_texts_weights, delta=args.threshold) - logging.debug("AFTER WEIGHTS THRESHOLDING:") - [logging.debug(x) for x in norm_texts_weights[0][0]] - - # reduce number of options by selecting options with the smallest number of unchanged words - norm_texts_weights = threshold(norm_texts_weights) - - print("POST PROCESSING...") - post_targets, post_norm_texts_weights = utils.clean_post_norm( - dataset=args.dataset, inputs=pre_inputs, targets=pre_targets, norm_texts=norm_texts_weights - ) - - print("GETTING LABELS...") - labels = utils.get_labels(targets=post_targets, norm_texts_weights=post_norm_texts_weights) - - examples_with_no_labels_among_wfst = [i for i, x in enumerate(labels) if 1 not in x] - - print("GATHERING STATS...") - model_stats = {m: 0 for m in models} - gt_in_options = 0 - for i, example in tqdm(enumerate(zip(post_norm_texts_weights, labels))): - data, curr_labels = example - assert len(data[0]) == len(curr_labels) - df = rank( - sentences=data[0], - labels=curr_labels, - models=models, - context_len=args.context_len if args.context_len is not None and args.context_len >= 0 else None, - do_lower=True, - ) - df['sent'] = df['sent'].apply(lambda x: utils.remove_whitelist_boudaries(x)) - df["weights"] = data[1] - - do_print = False - - for model in models: - # one hot vector for predictions, 1 for the best score option - df[f"{model}_pred"] = (df[model] == min(df[model])).astype(int) - # add constrain when multiple correct labels per example - pred_is_correct = min(sum((df["labels"] == df[f"{model}_pred"]) & df["labels"] == 1), 1) - - if not pred_is_correct or logging.getEffectiveLevel() <= logging.DEBUG: - do_print = True - - if do_print: - print(f"{model} prediction is correct: {pred_is_correct == 1}") - model_stats[model] += pred_is_correct - gt_in_options += 1 in curr_labels - - if do_print: - print(f"INPUT: {pre_inputs[i]}") - print(f"GT : {post_targets[i]}\n") - utils.print_df(df) - print("-" * 80 + "\n") - - if gt_in_options != len(post_norm_texts_weights): - print("WFST options for some examples don't contain the ground truth:") - for i in examples_with_no_labels_among_wfst: - print(f"INPUT: {pre_inputs[i]}") - print(f"GT : {post_targets[i]}\n") - print(f"WFST:") - for x in post_norm_texts_weights[i]: - print(x) - print("=" * 40) - - all_correct = True - for model, correct in model_stats.items(): - print( - f"{model} -- correct: {correct}/{len(post_norm_texts_weights)} or ({round(correct/len(post_norm_texts_weights) * 100, 2)}%)" - ) - all_correct = all_correct and (correct == len(post_norm_texts_weights)) - - print(f"examples_with_no_labels_among_wfst: {len(examples_with_no_labels_among_wfst)}") - return all_correct - - -if __name__ == "__main__": - all_correct = main() - print(f"all_correct: {all_correct}") diff --git a/nemo_text_processing/install_pynini.sh b/nemo_text_processing/install_pynini.sh deleted file mode 100644 index 301df87c223d..000000000000 --- a/nemo_text_processing/install_pynini.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -pip install pynini==2.1.5 diff --git a/nemo_text_processing/inverse_text_normalization/README.md b/nemo_text_processing/inverse_text_normalization/README.md deleted file mode 100644 index 8bf5a0fcf929..000000000000 --- a/nemo_text_processing/inverse_text_normalization/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Inverse Text Normalization - -Inverse Text Normalization is part of NeMo's `nemo_text_processing` - a Python package that is installed with the `nemo_toolkit`. -It converts text from spoken form into written form, e.g. "one hundred twenty three" -> "123". - -See [NeMo documentation](https://docs.nvidia.com/deeplearning/nemo/user-guide/docs/en/stable/nlp/text_normalization/wfst/wfst_text_normalization.html) for details. - -Tutorial with overview of the package capabilities: [Text_(Inverse)_Normalization.ipynb](https://colab.research.google.com/github/NVIDIA/NeMo/blob/stable/tutorials/text_processing/Text_(Inverse)_Normalization.ipynb) - -Tutorial on how to customize the underlying gramamrs: [WFST_Tutorial.ipynb](https://colab.research.google.com/github/NVIDIA/NeMo/blob/stable/tutorials/text_processing/WFST_Tutorial.ipynb) - diff --git a/nemo_text_processing/inverse_text_normalization/__init__.py b/nemo_text_processing/inverse_text_normalization/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/de/__init__.py b/nemo_text_processing/inverse_text_normalization/de/__init__.py deleted file mode 100644 index a1ed7385c85c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/de/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from nemo_text_processing.inverse_text_normalization.en.taggers.tokenize_and_classify import ClassifyFst -from nemo_text_processing.inverse_text_normalization.en.verbalizers.verbalize import VerbalizeFst -from nemo_text_processing.inverse_text_normalization.en.verbalizers.verbalize_final import VerbalizeFinalFst diff --git a/nemo_text_processing/inverse_text_normalization/de/taggers/__init__.py b/nemo_text_processing/inverse_text_normalization/de/taggers/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/inverse_text_normalization/de/taggers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/de/taggers/cardinal.py b/nemo_text_processing/inverse_text_normalization/de/taggers/cardinal.py deleted file mode 100644 index e0b066173814..000000000000 --- a/nemo_text_processing/inverse_text_normalization/de/taggers/cardinal.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_SIGMA, GraphFst -from pynini.lib import pynutil - - -class CardinalFst(GraphFst): - """ - Finite state transducer for classifying cardinals. Numbers below ten are not converted. - Allows both compound numeral strings or separated by whitespace. - "und" (en: "and") can be inserted between "hundert" and following number or "tausend" and following single or double digit number. - - e.g. minus drei und zwanzig -> cardinal { negative: "-" integer: "23" } } - e.g. minus dreiundzwanzig -> cardinal { integer: "23" } } - e.g. dreizehn -> cardinal { integer: "13" } } - e.g. ein hundert -> cardinal { integer: "100" } } - e.g. einhundert -> cardinal { integer: "100" } } - e.g. ein tausend -> cardinal { integer: "1000" } } - e.g. eintausend -> cardinal { integer: "1000" } } - e.g. ein tausend zwanzig -> cardinal { integer: "1020" } } - - Args: - tn_cardinal_tagger: TN cardinal tagger - """ - - def __init__(self, tn_cardinal_tagger: GraphFst, deterministic: bool = True): - super().__init__(name="cardinal", kind="classify", deterministic=deterministic) - - # add_space_between_chars = pynini.cdrewrite(pynini.closure(insert_space, 0, 1), NEMO_CHAR, NEMO_CHAR, NEMO_SIGMA) - optional_delete_space = pynini.closure(NEMO_SIGMA | pynutil.delete(" ")) - - graph = (tn_cardinal_tagger.graph @ optional_delete_space).invert().optimize() - self.graph_hundred_component_at_least_one_none_zero_digit = ( - (tn_cardinal_tagger.graph_hundred_component_at_least_one_none_zero_digit @ optional_delete_space) - .invert() - .optimize() - ) - - self.graph_ties = (tn_cardinal_tagger.two_digit_non_zero @ optional_delete_space).invert().optimize() - # this is to make sure if there is an ambiguity with decimal, decimal is chosen, e.g. 1000000 vs. 1 million - graph = pynutil.add_weight(graph, weight=0.001) - self.graph_no_exception = graph - self.digit = pynini.arcmap(tn_cardinal_tagger.digit, map_type="rmweight").invert().optimize() - graph_exception = pynini.project(self.digit, 'input') - self.graph = (pynini.project(graph, "input") - graph_exception.arcsort()) @ graph - - self.optional_minus_graph = pynini.closure( - pynutil.insert("negative: ") + pynini.cross("minus ", "\"-\" "), 0, 1 - ) - - final_graph = self.optional_minus_graph + pynutil.insert("integer: \"") + self.graph + pynutil.insert("\"") - - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/de/taggers/date.py b/nemo_text_processing/inverse_text_normalization/de/taggers/date.py deleted file mode 100644 index b65be41e4e1b..000000000000 --- a/nemo_text_processing/inverse_text_normalization/de/taggers/date.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_DIGIT, - NEMO_NOT_QUOTE, - NEMO_SIGMA, - GraphFst, - convert_space, -) -from pynini.lib import pynutil - - -class DateFst(GraphFst): - """ - Finite state transducer for classifying date, in the form of (day) month (year) or year - e.g. vierundzwanzigster juli zwei tausend dreizehn -> tokens { name: "24. Jul. 2013" } - e.g. neunzehnachtzig -> tokens { name: "1980" } - e.g. vierzehnter januar -> tokens { name: "14. Jan." } - e.g. zweiter dritter -> tokens { name: "02.03." } - e.g. januar neunzehnachtzig -> tokens { name: "Jan. 1980" } - e.g. zwanzigzwanzig -> tokens { name: "2020" } - - Args: - itn_cardinal_tagger: ITN cardinal tagger - tn_date_tagger: TN date tagger - tn_date_verbalizer: TN date verbalizer - """ - - def __init__( - self, - itn_cardinal_tagger: GraphFst, - tn_date_tagger: GraphFst, - tn_date_verbalizer: GraphFst, - deterministic: bool = True, - ): - super().__init__(name="date", kind="classify", deterministic=deterministic) - - add_leading_zero_to_double_digit = (NEMO_DIGIT + NEMO_DIGIT) | (pynutil.insert("0") + NEMO_DIGIT) - optional_delete_space = pynini.closure(NEMO_SIGMA | pynutil.delete(" ", weight=0.0001)) - tagger = tn_date_verbalizer.graph.invert().optimize() - - delete_day_marker = ( - pynutil.delete("day: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - ) @ itn_cardinal_tagger.graph_no_exception - - month_as_number = pynutil.delete("month: \"") + itn_cardinal_tagger.graph_no_exception + pynutil.delete("\"") - month_as_string = pynutil.delete("month: \"") + tn_date_tagger.month_abbr.invert() + pynutil.delete("\"") - - convert_year = (tn_date_tagger.year @ optional_delete_space).invert().optimize() - delete_year_marker = ( - pynutil.delete("year: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - ) @ convert_year - - # day. month as string (year) - verbalizer = ( - pynini.closure(delete_day_marker + pynutil.insert(".") + pynini.accep(" "), 0, 1) - + month_as_string - + pynini.closure(pynini.accep(" ") + delete_year_marker, 0, 1) - ) - - # day. month as number (year) - verbalizer |= ( - delete_day_marker @ add_leading_zero_to_double_digit - + pynutil.insert(".") - + pynutil.delete(" ") - + month_as_number @ add_leading_zero_to_double_digit - + pynutil.insert(".") - + pynini.closure(pynutil.delete(" ") + delete_year_marker, 0, 1) - ) - - # year - verbalizer |= delete_year_marker - - final_graph = tagger @ verbalizer - - graph = pynutil.insert("name: \"") + convert_space(final_graph) + pynutil.insert("\"") - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/de/taggers/decimal.py b/nemo_text_processing/inverse_text_normalization/de/taggers/decimal.py deleted file mode 100644 index e6d562d4b43e..000000000000 --- a/nemo_text_processing/inverse_text_normalization/de/taggers/decimal.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.de.taggers.decimal import get_quantity, quantities -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_SIGMA, GraphFst -from pynini.lib import pynutil - - -class DecimalFst(GraphFst): - """ - Finite state transducer for classifying decimal - e.g. minus elf komma zwei null null sechs billionen -> decimal { negative: "true" integer_part: "11" fractional_part: "2006" quantity: "billionen" } - e.g. eine billion -> decimal { integer_part: "1" quantity: "billion" } - Args: - itn_cardinal_tagger: ITN Cardinal tagger - tn_decimal_tagger: TN decimal tagger - """ - - def __init__(self, itn_cardinal_tagger: GraphFst, tn_decimal_tagger: GraphFst, deterministic: bool = True): - super().__init__(name="decimal", kind="classify", deterministic=deterministic) - - self.graph = tn_decimal_tagger.graph.invert().optimize() - - delete_point = pynutil.delete(" komma") - - allow_spelling = pynini.cdrewrite(pynini.cross("eine ", "eins ") + quantities, "[BOS]", "[EOS]", NEMO_SIGMA) - - graph_fractional = pynutil.insert("fractional_part: \"") + self.graph + pynutil.insert("\"") - graph_integer = ( - pynutil.insert("integer_part: \"") + itn_cardinal_tagger.graph_no_exception + pynutil.insert("\"") - ) - final_graph_wo_sign = graph_integer + delete_point + pynini.accep(" ") + graph_fractional - - self.final_graph_wo_negative = ( - allow_spelling - @ ( - final_graph_wo_sign - | get_quantity( - final_graph_wo_sign, itn_cardinal_tagger.graph_hundred_component_at_least_one_none_zero_digit - ) - ).optimize() - ) - - final_graph = itn_cardinal_tagger.optional_minus_graph + self.final_graph_wo_negative - final_graph += pynutil.insert(" preserve_order: true") - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/de/taggers/electronic.py b/nemo_text_processing/inverse_text_normalization/de/taggers/electronic.py deleted file mode 100644 index 849d6c8435ef..000000000000 --- a/nemo_text_processing/inverse_text_normalization/de/taggers/electronic.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst -from pynini.lib import pynutil - - -class ElectronicFst(GraphFst): - """ - Finite state transducer for classifying electronic: email addresses, etc. - e.g. c d f eins at a b c punkt e d u -> tokens { name: "cdf1.abc.edu" } - - Args: - tn_electronic_tagger: TN eletronic tagger - tn_electronic_verbalizer: TN eletronic verbalizer - """ - - def __init__(self, tn_electronic_tagger: GraphFst, tn_electronic_verbalizer: GraphFst, deterministic: bool = True): - super().__init__(name="electronic", kind="classify", deterministic=deterministic) - - tagger = pynini.invert(tn_electronic_verbalizer.graph).optimize() - verbalizer = pynini.invert(tn_electronic_tagger.graph).optimize() - final_graph = tagger @ verbalizer - - graph = pynutil.insert("name: \"") + final_graph + pynutil.insert("\"") - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/de/taggers/fraction.py b/nemo_text_processing/inverse_text_normalization/de/taggers/fraction.py deleted file mode 100644 index 6960a666343a..000000000000 --- a/nemo_text_processing/inverse_text_normalization/de/taggers/fraction.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_NOT_QUOTE, - GraphFst, - convert_space, - delete_space, -) -from pynini.lib import pynutil - - -class FractionFst(GraphFst): - """ - Finite state transducer for classifying fraction - e.g. ein halb -> tokens { name: "1/2" } - e.g. ein ein halb -> tokens { name: "1 1/2" } - e.g. drei zwei ein hundertstel -> tokens { name: "3 2/100" } - - Args: - itn_cardinal_tagger: ITN cardinal tagger - tn_fraction_verbalizer: TN fraction verbalizer - """ - - def __init__(self, itn_cardinal_tagger: GraphFst, tn_fraction_verbalizer: GraphFst, deterministic: bool = True): - super().__init__(name="fraction", kind="classify", deterministic=deterministic) - tagger = tn_fraction_verbalizer.graph.invert().optimize() - - delete_optional_sign = pynini.closure(pynutil.delete("negative: ") + pynini.cross("\"true\" ", "-"), 0, 1) - delete_integer_marker = ( - pynutil.delete("integer_part: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - ) @ itn_cardinal_tagger.graph_no_exception - - delete_numerator_marker = ( - pynutil.delete("numerator: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - ) @ itn_cardinal_tagger.graph_no_exception - - delete_denominator_marker = ( - pynutil.insert('/') - + (pynutil.delete("denominator: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"")) - @ itn_cardinal_tagger.graph_no_exception - ) - - graph = ( - pynini.closure(delete_integer_marker + pynini.accep(" "), 0, 1) - + delete_numerator_marker - + delete_space - + delete_denominator_marker - ).optimize() - verbalizer = delete_optional_sign + graph - - self.graph = tagger @ verbalizer - - graph = pynutil.insert("name: \"") + convert_space(self.graph) + pynutil.insert("\"") - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/de/taggers/measure.py b/nemo_text_processing/inverse_text_normalization/de/taggers/measure.py deleted file mode 100644 index 86eff3ff29da..000000000000 --- a/nemo_text_processing/inverse_text_normalization/de/taggers/measure.py +++ /dev/null @@ -1,103 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.de.taggers.measure import singular_to_plural, unit_singular -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_SIGMA, - GraphFst, - convert_space, - delete_extra_space, - delete_space, -) -from pynini.lib import pynutil - - -class MeasureFst(GraphFst): - """ - Finite state transducer for classifying measure. Allows for plural form for unit. - e.g. minus elf kilogramm -> measure { cardinal { negative: "true" integer: "11" } units: "kg" } - e.g. drei stunden -> measure { cardinal { integer: "3" } units: "h" } - e.g. ein halb kilogramm -> measure { decimal { integer_part: "1/2" } units: "kg" } - e.g. eins komma zwei kilogramm -> measure { decimal { integer_part: "1" fractional_part: "2" } units: "kg" } - - Args: - itn_cardinal_tagger: ITN Cardinal tagger - itn_decimal_tagger: ITN Decimal tagger - itn_fraction_tagger: ITN Fraction tagger - """ - - def __init__( - self, - itn_cardinal_tagger: GraphFst, - itn_decimal_tagger: GraphFst, - itn_fraction_tagger: GraphFst, - deterministic: bool = True, - ): - super().__init__(name="measure", kind="classify", deterministic=deterministic) - - cardinal_graph = ( - pynini.cdrewrite(pynini.cross(pynini.union("ein", "eine"), "eins"), "[BOS]", "[EOS]", NEMO_SIGMA) - @ itn_cardinal_tagger.graph_no_exception - ) - - graph_unit_singular = pynini.invert(unit_singular) # singular -> abbr - unit = (pynini.invert(singular_to_plural()) @ graph_unit_singular) | graph_unit_singular # plural -> abbr - unit = convert_space(unit) - graph_unit_singular = convert_space(graph_unit_singular) - - optional_graph_negative = pynini.closure( - pynutil.insert("negative: ") + pynini.cross("minus", "\"true\"") + delete_extra_space, 0, 1 - ) - - unit_misc = pynutil.insert("/") + pynutil.delete("pro") + delete_space + graph_unit_singular - - unit = ( - pynutil.insert("units: \"") - + (unit | unit_misc | pynutil.add_weight(unit + delete_space + unit_misc, 0.01)) - + pynutil.insert("\"") - ) - - subgraph_decimal = ( - pynutil.insert("decimal { ") - + optional_graph_negative - + itn_decimal_tagger.final_graph_wo_negative - + pynutil.insert(" }") - + delete_extra_space - + unit - ) - - subgraph_fraction = ( - pynutil.insert("decimal { ") - + optional_graph_negative - + pynutil.insert("integer_part: \"") - + itn_fraction_tagger.graph - + pynutil.insert("\" }") - + delete_extra_space - + unit - ) - - subgraph_cardinal = ( - pynutil.insert("cardinal { ") - + optional_graph_negative - + pynutil.insert("integer: \"") - + cardinal_graph - + pynutil.insert("\"") - + pynutil.insert(" }") - + delete_extra_space - + unit - ) - final_graph = subgraph_cardinal | subgraph_decimal | subgraph_fraction - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/de/taggers/money.py b/nemo_text_processing/inverse_text_normalization/de/taggers/money.py deleted file mode 100644 index dad06de1c1c5..000000000000 --- a/nemo_text_processing/inverse_text_normalization/de/taggers/money.py +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.de.taggers.money import maj_singular, min_plural, min_singular -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_DIGIT, - NEMO_SIGMA, - GraphFst, - convert_space, - delete_extra_space, - delete_space, - insert_space, -) -from pynini.lib import pynutil - - -class MoneyFst(GraphFst): - """ - Finite state transducer for classifying money - e.g. elf euro und vier cent -> money { integer_part: "11" fractional_part: 04 currency: "€" } - - Args: - itn_cardinal_tagger: ITN Cardinal Tagger - itn_decimal_tagger: ITN Decimal Tagger - """ - - def __init__(self, itn_cardinal_tagger: GraphFst, itn_decimal_tagger: GraphFst, deterministic: bool = True): - super().__init__(name="money", kind="classify", deterministic=deterministic) - cardinal_graph = ( - pynini.cdrewrite(pynini.cross(pynini.union("ein", "eine"), "eins"), "[BOS]", "[EOS]", NEMO_SIGMA) - @ itn_cardinal_tagger.graph_no_exception - ) - graph_decimal_final = itn_decimal_tagger.final_graph_wo_negative - - graph_unit = pynini.invert(maj_singular) - graph_unit = pynutil.insert("currency: \"") + convert_space(graph_unit) + pynutil.insert("\"") - - add_leading_zero_to_double_digit = (NEMO_DIGIT + NEMO_DIGIT) | (pynutil.insert("0") + NEMO_DIGIT) - min_unit = pynini.project(min_singular | min_plural, "output") - # elf euro (und) vier cent, vier cent - cents_standalone = ( - pynutil.insert("fractional_part: \"") - + cardinal_graph @ add_leading_zero_to_double_digit - + delete_space - + pynutil.delete(min_unit) - + pynutil.insert("\"") - ) - - optional_cents_standalone = pynini.closure( - delete_space - + pynini.closure(pynutil.delete("und") + delete_space, 0, 1) - + insert_space - + cents_standalone, - 0, - 1, - ) - # elf euro vierzig, only after integer - optional_cents_suffix = pynini.closure( - delete_extra_space - + pynutil.insert("fractional_part: \"") - + pynutil.add_weight(cardinal_graph @ add_leading_zero_to_double_digit, -0.7) - + pynutil.insert("\""), - 0, - 1, - ) - - graph_integer = ( - pynutil.insert("integer_part: \"") - + cardinal_graph - + pynutil.insert("\"") - + delete_extra_space - + graph_unit - + (optional_cents_standalone | optional_cents_suffix) - ) - graph_decimal = graph_decimal_final + delete_extra_space + graph_unit - graph_decimal |= pynutil.insert("currency: \"€\" integer_part: \"0\" ") + cents_standalone - final_graph = graph_integer | graph_decimal - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/de/taggers/ordinal.py b/nemo_text_processing/inverse_text_normalization/de/taggers/ordinal.py deleted file mode 100644 index 300dd901032c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/de/taggers/ordinal.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst -from pynini.lib import pynutil - - -class OrdinalFst(GraphFst): - """ - Finite state transducer for classifying ordinal - e.g. dreizehnter -> tokens { name: "13." } - - Args: - itn_cardinal_tagger: ITN Cardinal Tagger - tn_ordinal_verbalizer: TN Ordinal Verbalizer - """ - - def __init__(self, itn_cardinal_tagger: GraphFst, tn_ordinal_verbalizer: GraphFst, deterministic: bool = True): - super().__init__(name="ordinal", kind="classify", deterministic=deterministic) - - tagger = tn_ordinal_verbalizer.graph.invert().optimize() - - graph = ( - pynutil.delete("integer: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - ) @ itn_cardinal_tagger.graph - - final_graph = tagger @ graph + pynutil.insert(".") - - graph = pynutil.insert("name: \"") + final_graph + pynutil.insert("\"") - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/de/taggers/telephone.py b/nemo_text_processing/inverse_text_normalization/de/taggers/telephone.py deleted file mode 100644 index ddc3391d4b95..000000000000 --- a/nemo_text_processing/inverse_text_normalization/de/taggers/telephone.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst, convert_space, insert_space -from pynini.lib import pynutil - - -class TelephoneFst(GraphFst): - """ - Finite state transducer for classifying telephone numbers, e.g. - null vier eins eins eins zwei drei vier eins zwei drei vier -> tokens { name: "(0411) 1234-1234" } - - Args: - tn_cardinal_tagger: TN Cardinal Tagger - """ - - def __init__(self, tn_cardinal_tagger: GraphFst, deterministic: bool = True): - super().__init__(name="telephone", kind="classify", deterministic=deterministic) - separator = pynini.accep(" ") # between components - digit = pynini.union(*list(map(str, range(1, 10)))) @ tn_cardinal_tagger.two_digit_non_zero - zero = pynini.cross("0", "null") - - number_part = ( - pynutil.delete("(") - + zero - + insert_space - + pynini.closure(digit + insert_space, 2, 2) - + digit - + pynutil.delete(")") - + separator - + pynini.closure(digit + insert_space, 3, 3) - + digit - + pynutil.delete("-") - + insert_space - + pynini.closure(digit + insert_space, 3, 3) - + digit - ) - graph = convert_space(pynini.invert(number_part)) - final_graph = pynutil.insert("name: \"") + graph + pynutil.insert("\"") - - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/de/taggers/time.py b/nemo_text_processing/inverse_text_normalization/de/taggers/time.py deleted file mode 100644 index c747202b923d..000000000000 --- a/nemo_text_processing/inverse_text_normalization/de/taggers/time.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_SIGMA, GraphFst -from pynini.lib import pynutil - - -class TimeFst(GraphFst): - """ - Finite state transducer for classifying time - e.g. acht uhr e s t-> time { hours: "8" zone: "e s t" } - e.g. dreizehn uhr -> time { hours: "13" } - e.g. dreizehn uhr zehn -> time { hours: "13" minutes: "10" } - e.g. viertel vor zwölf -> time { minutes: "45" hours: "11" } - e.g. viertel nach zwölf -> time { minutes: "15" hours: "12" } - e.g. halb zwölf -> time { minutes: "30" hours: "11" } - e.g. drei vor zwölf -> time { minutes: "57" hours: "11" } - e.g. drei nach zwölf -> time { minutes: "3" hours: "12" } - e.g. drei uhr zehn minuten zehn sekunden -> time { hours: "3" hours: "10" sekunden: "10"} - - Args: - tn_time_verbalizer: TN time verbalizer - """ - - def __init__(self, tn_time_verbalizer: GraphFst, deterministic: bool = True): - super().__init__(name="time", kind="classify", deterministic=deterministic) - # lazy way to make sure compounds work - optional_delete_space = pynini.closure(NEMO_SIGMA | pynutil.delete(" ", weight=0.0001)) - graph = (tn_time_verbalizer.graph @ optional_delete_space).invert().optimize() - self.fst = self.add_tokens(graph).optimize() diff --git a/nemo_text_processing/inverse_text_normalization/de/taggers/tokenize_and_classify.py b/nemo_text_processing/inverse_text_normalization/de/taggers/tokenize_and_classify.py deleted file mode 100644 index 487fcd71effd..000000000000 --- a/nemo_text_processing/inverse_text_normalization/de/taggers/tokenize_and_classify.py +++ /dev/null @@ -1,141 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -import pynini -from nemo_text_processing.inverse_text_normalization.de.taggers.cardinal import CardinalFst -from nemo_text_processing.inverse_text_normalization.de.taggers.date import DateFst -from nemo_text_processing.inverse_text_normalization.de.taggers.decimal import DecimalFst -from nemo_text_processing.inverse_text_normalization.de.taggers.electronic import ElectronicFst -from nemo_text_processing.inverse_text_normalization.de.taggers.fraction import FractionFst -from nemo_text_processing.inverse_text_normalization.de.taggers.measure import MeasureFst -from nemo_text_processing.inverse_text_normalization.de.taggers.money import MoneyFst -from nemo_text_processing.inverse_text_normalization.de.taggers.ordinal import OrdinalFst -from nemo_text_processing.inverse_text_normalization.de.taggers.telephone import TelephoneFst -from nemo_text_processing.inverse_text_normalization.de.taggers.time import TimeFst -from nemo_text_processing.inverse_text_normalization.de.taggers.whitelist import WhiteListFst -from nemo_text_processing.inverse_text_normalization.en.taggers.punctuation import PunctuationFst -from nemo_text_processing.inverse_text_normalization.en.taggers.word import WordFst -from nemo_text_processing.text_normalization.de.taggers.cardinal import CardinalFst as TNCardinalTagger -from nemo_text_processing.text_normalization.de.taggers.date import DateFst as TNDateTagger -from nemo_text_processing.text_normalization.de.taggers.decimal import DecimalFst as TNDecimalTagger -from nemo_text_processing.text_normalization.de.taggers.electronic import ElectronicFst as TNElectronicTagger -from nemo_text_processing.text_normalization.de.taggers.whitelist import WhiteListFst as TNWhitelistTagger -from nemo_text_processing.text_normalization.de.verbalizers.date import DateFst as TNDateVerbalizer -from nemo_text_processing.text_normalization.de.verbalizers.electronic import ElectronicFst as TNElectronicVerbalizer -from nemo_text_processing.text_normalization.de.verbalizers.fraction import FractionFst as TNFractionVerbalizer -from nemo_text_processing.text_normalization.de.verbalizers.ordinal import OrdinalFst as TNOrdinalVerbalizer -from nemo_text_processing.text_normalization.de.verbalizers.time import TimeFst as TNTimeVerbalizer -from nemo_text_processing.text_normalization.en.graph_utils import ( - GraphFst, - delete_extra_space, - delete_space, - generator_main, -) -from pynini.lib import pynutil - -from nemo.utils import logging - - -class ClassifyFst(GraphFst): - """ - Final class that composes all other classification grammars. This class can process an entire sentence, that is lower cased. - For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - - Args: - cache_dir: path to a dir with .far grammar file. Set to None to avoid using cache. - overwrite_cache: set to True to overwrite .far files - """ - - def __init__(self, cache_dir: str = None, overwrite_cache: bool = False, deterministic: bool = True): - super().__init__(name="tokenize_and_classify", kind="classify", deterministic=deterministic) - - far_file = None - if cache_dir is not None and cache_dir != 'None': - os.makedirs(cache_dir, exist_ok=True) - far_file = os.path.join(cache_dir, "_de_itn.far") - if not overwrite_cache and far_file and os.path.exists(far_file): - self.fst = pynini.Far(far_file, mode="r")["tokenize_and_classify"] - logging.info(f"ClassifyFst.fst was restored from {far_file}.") - else: - logging.info(f"Creating ClassifyFst grammars.") - tn_cardinal_tagger = TNCardinalTagger(deterministic=False) - tn_date_tagger = TNDateTagger(cardinal=tn_cardinal_tagger, deterministic=False) - tn_decimal_tagger = TNDecimalTagger(cardinal=tn_cardinal_tagger, deterministic=False) - tn_ordinal_verbalizer = TNOrdinalVerbalizer(deterministic=False) - tn_fraction_verbalizer = TNFractionVerbalizer(ordinal=tn_ordinal_verbalizer, deterministic=False) - tn_time_verbalizer = TNTimeVerbalizer(cardinal_tagger=tn_cardinal_tagger, deterministic=False) - tn_date_verbalizer = TNDateVerbalizer(ordinal=tn_ordinal_verbalizer, deterministic=False) - tn_electronic_tagger = TNElectronicTagger(deterministic=False) - tn_electronic_verbalizer = TNElectronicVerbalizer(deterministic=False) - tn_whitelist_tagger = TNWhitelistTagger(input_case="cased", deterministic=False) - - cardinal = CardinalFst(tn_cardinal_tagger=tn_cardinal_tagger) - cardinal_graph = cardinal.fst - - ordinal = OrdinalFst(itn_cardinal_tagger=cardinal, tn_ordinal_verbalizer=tn_ordinal_verbalizer) - ordinal_graph = ordinal.fst - decimal = DecimalFst(itn_cardinal_tagger=cardinal, tn_decimal_tagger=tn_decimal_tagger) - decimal_graph = decimal.fst - - fraction = FractionFst(itn_cardinal_tagger=cardinal, tn_fraction_verbalizer=tn_fraction_verbalizer) - fraction_graph = fraction.fst - - measure_graph = MeasureFst( - itn_cardinal_tagger=cardinal, itn_decimal_tagger=decimal, itn_fraction_tagger=fraction - ).fst - date_graph = DateFst( - itn_cardinal_tagger=cardinal, tn_date_verbalizer=tn_date_verbalizer, tn_date_tagger=tn_date_tagger - ).fst - word_graph = WordFst().fst - time_graph = TimeFst(tn_time_verbalizer=tn_time_verbalizer).fst - money_graph = MoneyFst(itn_cardinal_tagger=cardinal, itn_decimal_tagger=decimal).fst - whitelist_graph = WhiteListFst(tn_whitelist_tagger=tn_whitelist_tagger).fst - punct_graph = PunctuationFst().fst - electronic_graph = ElectronicFst( - tn_electronic_tagger=tn_electronic_tagger, tn_electronic_verbalizer=tn_electronic_verbalizer - ).fst - telephone_graph = TelephoneFst(tn_cardinal_tagger=tn_cardinal_tagger).fst - - classify = ( - pynutil.add_weight(cardinal_graph, 1.1) - | pynutil.add_weight(whitelist_graph, 1.0) - | pynutil.add_weight(time_graph, 1.1) - | pynutil.add_weight(date_graph, 1.1) - | pynutil.add_weight(decimal_graph, 1.1) - | pynutil.add_weight(measure_graph, 1.1) - | pynutil.add_weight(ordinal_graph, 1.1) - | pynutil.add_weight(fraction_graph, 1.1) - | pynutil.add_weight(money_graph, 1.1) - | pynutil.add_weight(telephone_graph, 1.1) - | pynutil.add_weight(electronic_graph, 1.1) - | pynutil.add_weight(word_graph, 100) - ) - - punct = pynutil.insert("tokens { ") + pynutil.add_weight(punct_graph, weight=1.1) + pynutil.insert(" }") - token = pynutil.insert("tokens { ") + classify + pynutil.insert(" }") - token_plus_punct = ( - pynini.closure(punct + pynutil.insert(" ")) + token + pynini.closure(pynutil.insert(" ") + punct) - ) - - graph = token_plus_punct + pynini.closure(delete_extra_space + token_plus_punct) - graph = delete_space + graph + delete_space - - self.fst = graph.optimize() - - if far_file: - generator_main(far_file, {"tokenize_and_classify": self.fst}) - logging.info(f"ClassifyFst grammars are saved to {far_file}.") diff --git a/nemo_text_processing/inverse_text_normalization/de/taggers/whitelist.py b/nemo_text_processing/inverse_text_normalization/de/taggers/whitelist.py deleted file mode 100644 index 5d52e3a37ca1..000000000000 --- a/nemo_text_processing/inverse_text_normalization/de/taggers/whitelist.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst, convert_space -from pynini.lib import pynutil - - -class WhiteListFst(GraphFst): - """ - Finite state transducer for classifying whitelisted tokens - e.g. misses -> tokens { name: "Mrs." } - Args: - tn_whitelist_tagger: TN whitelist tagger - """ - - def __init__(self, tn_whitelist_tagger: GraphFst, deterministic: bool = True): - super().__init__(name="whitelist", kind="classify", deterministic=deterministic) - - whitelist = pynini.invert(tn_whitelist_tagger.graph) - graph = pynutil.insert("name: \"") + convert_space(whitelist) + pynutil.insert("\"") - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/de/verbalizers/__init__.py b/nemo_text_processing/inverse_text_normalization/de/verbalizers/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/inverse_text_normalization/de/verbalizers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/de/verbalizers/cardinal.py b/nemo_text_processing/inverse_text_normalization/de/verbalizers/cardinal.py deleted file mode 100644 index 92a83625a5f2..000000000000 --- a/nemo_text_processing/inverse_text_normalization/de/verbalizers/cardinal.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst -from pynini.lib import pynutil - - -class CardinalFst(GraphFst): - """ - Finite state transducer for verbalizing cardinal - e.g. cardinal { integer: "23" negative: "-" } -> -23 - - Args: - tn_cardinal_verbalizer: TN cardinal verbalizer - """ - - def __init__(self, tn_cardinal_verbalizer: GraphFst, deterministic: bool = True): - super().__init__(name="cardinal", kind="verbalize", deterministic=deterministic) - self.numbers = tn_cardinal_verbalizer.numbers - optional_sign = pynini.closure(pynutil.delete("negative: \"") + NEMO_NOT_QUOTE + pynutil.delete("\" "), 0, 1) - graph = optional_sign + self.numbers - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/de/verbalizers/decimal.py b/nemo_text_processing/inverse_text_normalization/de/verbalizers/decimal.py deleted file mode 100644 index ff3839533d72..000000000000 --- a/nemo_text_processing/inverse_text_normalization/de/verbalizers/decimal.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_preserve_order -from pynini.lib import pynutil - - -class DecimalFst(GraphFst): - """ - Finite state transducer for verbalizing decimal, e.g. - decimal { negative: "true" integer_part: "12" fractional_part: "5006" quantity: "billion" } -> -12.5006 billion - - Args: - tn_decimal_verbalizer: TN decimal verbalizer - """ - - def __init__(self, tn_decimal_verbalizer: GraphFst, deterministic: bool = True): - super().__init__(name="decimal", kind="verbalize", deterministic=deterministic) - delete_space = pynutil.delete(" ") - optional_sign = pynini.closure( - pynutil.delete("negative: \"") + NEMO_NOT_QUOTE + pynutil.delete("\"") + delete_space, 0, 1 - ) - optional_integer = pynini.closure(tn_decimal_verbalizer.integer, 0, 1) - optional_fractional = pynini.closure( - delete_space + pynutil.insert(",") + tn_decimal_verbalizer.fractional_default, 0, 1 - ) - graph = (optional_integer + optional_fractional + tn_decimal_verbalizer.optional_quantity).optimize() - self.numbers = optional_sign + graph - graph = self.numbers + delete_preserve_order - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/de/verbalizers/measure.py b/nemo_text_processing/inverse_text_normalization/de/verbalizers/measure.py deleted file mode 100644 index 90f43e7e6f40..000000000000 --- a/nemo_text_processing/inverse_text_normalization/de/verbalizers/measure.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_CHAR, GraphFst, delete_space -from pynini.lib import pynutil - - -class MeasureFst(GraphFst): - """ - Finite state transducer for verbalizing measure, e.g. - measure { cardinal { negative: "true" integer: "12" } units: "kg" } -> -12 kg - measure { decimal { integer_part: "1/2" } units: "kg" } -> 1/2 kg - measure { decimal { integer_part: "1" fractional_part: "2" quantity: "million" } units: "kg" } -> 1,2 million kg - - Args: - decimal: ITN Decimal verbalizer - cardinal: ITN Cardinal verbalizer - """ - - def __init__(self, decimal: GraphFst, cardinal: GraphFst, deterministic: bool = True): - super().__init__(name="measure", kind="verbalize", deterministic=deterministic) - optional_sign = pynini.closure(pynini.cross("negative: \"true\"", "-"), 0, 1) - unit = ( - pynutil.delete("units:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_CHAR - " ", 1) - + pynutil.delete("\"") - + delete_space - ) - graph_decimal = ( - pynutil.delete("decimal {") - + delete_space - + optional_sign - + delete_space - + decimal.numbers - + delete_space - + pynutil.delete("}") - ) - graph_cardinal = ( - pynutil.delete("cardinal {") - + delete_space - + optional_sign - + delete_space - + cardinal.numbers - + delete_space - + pynutil.delete("}") - ) - - graph = (graph_cardinal | graph_decimal) + delete_space + pynutil.insert(" ") + unit - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/de/verbalizers/money.py b/nemo_text_processing/inverse_text_normalization/de/verbalizers/money.py deleted file mode 100644 index f0377d4fac4e..000000000000 --- a/nemo_text_processing/inverse_text_normalization/de/verbalizers/money.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_CHAR, GraphFst, delete_space -from pynini.lib import pynutil - - -class MoneyFst(GraphFst): - """ - Finite state transducer for verbalizing money, e.g. - money { integer_part: "12" fractional_part: "05" currency: "$" } -> $12.05 - - Args: - decimal: ITN Decimal verbalizer - """ - - def __init__(self, decimal: GraphFst, deterministic: bool = True): - super().__init__(name="money", kind="verbalize", deterministic=deterministic) - unit = ( - pynutil.delete("currency:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_CHAR - " ", 1) - + pynutil.delete("\"") - ) - graph = unit + delete_space + decimal.numbers - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/de/verbalizers/time.py b/nemo_text_processing/inverse_text_normalization/de/verbalizers/time.py deleted file mode 100644 index 13a013db57ed..000000000000 --- a/nemo_text_processing/inverse_text_normalization/de/verbalizers/time.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_ALPHA, NEMO_DIGIT, GraphFst, delete_space -from pynini.lib import pynutil - - -class TimeFst(GraphFst): - """ - Finite state transducer for verbalizing time, e.g. - time { hours: "8" minutes: "30" zone: "e s t" } -> 08:30 Uhr est - time { hours: "8" } -> 8 Uhr - time { hours: "8" minutes: "30" seconds: "10" } -> 08:30:10 Uhr - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="time", kind="verbalize", deterministic=deterministic) - - add_leading_zero_to_double_digit = (NEMO_DIGIT + NEMO_DIGIT) | (pynutil.insert("0") + NEMO_DIGIT) - hour = pynutil.delete("hours: \"") + pynini.closure(NEMO_DIGIT, 1) + pynutil.delete("\"") - minute = pynutil.delete("minutes: \"") + pynini.closure(NEMO_DIGIT, 1) + pynutil.delete("\"") - - second = pynutil.delete("seconds: \"") + pynini.closure(NEMO_DIGIT, 1) + pynutil.delete("\"") - zone = ( - pynutil.delete("zone: \"") + pynini.closure(NEMO_ALPHA + delete_space) + NEMO_ALPHA + pynutil.delete("\"") - ) - optional_zone = pynini.closure(pynini.accep(" ") + zone, 0, 1) - graph = ( - delete_space - + pynutil.insert(":") - + (minute @ add_leading_zero_to_double_digit) - + pynini.closure(delete_space + pynutil.insert(":") + (second @ add_leading_zero_to_double_digit), 0, 1) - + pynutil.insert(" Uhr") - + optional_zone - ) - graph_h = hour + pynutil.insert(" Uhr") + optional_zone - graph_hm = hour @ add_leading_zero_to_double_digit + graph - graph_hms = hour @ add_leading_zero_to_double_digit + graph - final_graph = graph_hm | graph_hms | graph_h - self.fst = self.delete_tokens(final_graph).optimize() diff --git a/nemo_text_processing/inverse_text_normalization/de/verbalizers/verbalize.py b/nemo_text_processing/inverse_text_normalization/de/verbalizers/verbalize.py deleted file mode 100644 index 80bafd1978d6..000000000000 --- a/nemo_text_processing/inverse_text_normalization/de/verbalizers/verbalize.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from nemo_text_processing.inverse_text_normalization.de.verbalizers.cardinal import CardinalFst -from nemo_text_processing.inverse_text_normalization.de.verbalizers.decimal import DecimalFst -from nemo_text_processing.inverse_text_normalization.de.verbalizers.measure import MeasureFst -from nemo_text_processing.inverse_text_normalization.de.verbalizers.money import MoneyFst -from nemo_text_processing.inverse_text_normalization.de.verbalizers.time import TimeFst -from nemo_text_processing.text_normalization.de.verbalizers.cardinal import CardinalFst as TNCardinalVerbalizer -from nemo_text_processing.text_normalization.de.verbalizers.decimal import DecimalFst as TNDecimalVerbalizer -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst - - -class VerbalizeFst(GraphFst): - """ - Composes other verbalizer grammars. - For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="verbalize", kind="verbalize", deterministic=deterministic) - tn_cardinal_verbalizer = TNCardinalVerbalizer(deterministic=False) - tn_decimal_verbalizer = TNDecimalVerbalizer(deterministic=False) - - cardinal = CardinalFst(tn_cardinal_verbalizer=tn_cardinal_verbalizer) - cardinal_graph = cardinal.fst - decimal = DecimalFst(tn_decimal_verbalizer=tn_decimal_verbalizer) - decimal_graph = decimal.fst - measure_graph = MeasureFst(decimal=decimal, cardinal=cardinal).fst - money_graph = MoneyFst(decimal=decimal).fst - time_graph = TimeFst().fst - graph = time_graph | money_graph | measure_graph | decimal_graph | cardinal_graph - self.fst = graph diff --git a/nemo_text_processing/inverse_text_normalization/de/verbalizers/verbalize_final.py b/nemo_text_processing/inverse_text_normalization/de/verbalizers/verbalize_final.py deleted file mode 100644 index 779a00a4234e..000000000000 --- a/nemo_text_processing/inverse_text_normalization/de/verbalizers/verbalize_final.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.de.verbalizers.verbalize import VerbalizeFst -from nemo_text_processing.inverse_text_normalization.en.verbalizers.word import WordFst -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst, delete_extra_space, delete_space -from pynini.lib import pynutil - - -class VerbalizeFinalFst(GraphFst): - """ - Finite state transducer that verbalizes an entire sentence, e.g. - tokens { name: "jetzt" } tokens { name: "ist" } tokens { time { hours: "12" minutes: "30" } } -> jetzt ist 12:30 Uhr - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="verbalize_final", kind="verbalize", deterministic=deterministic) - verbalize = VerbalizeFst().fst - word = WordFst().fst - types = verbalize | word - graph = ( - pynutil.delete("tokens") - + delete_space - + pynutil.delete("{") - + delete_space - + types - + delete_space - + pynutil.delete("}") - ) - graph = delete_space + pynini.closure(graph + delete_extra_space) + graph + delete_space - self.fst = graph diff --git a/nemo_text_processing/inverse_text_normalization/en/__init__.py b/nemo_text_processing/inverse_text_normalization/en/__init__.py deleted file mode 100644 index a1ed7385c85c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from nemo_text_processing.inverse_text_normalization.en.taggers.tokenize_and_classify import ClassifyFst -from nemo_text_processing.inverse_text_normalization.en.verbalizers.verbalize import VerbalizeFst -from nemo_text_processing.inverse_text_normalization.en.verbalizers.verbalize_final import VerbalizeFinalFst diff --git a/nemo_text_processing/inverse_text_normalization/en/clean_eval_data.py b/nemo_text_processing/inverse_text_normalization/en/clean_eval_data.py deleted file mode 100644 index d9bc2fccb685..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/clean_eval_data.py +++ /dev/null @@ -1,342 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from argparse import ArgumentParser -from typing import List - -import regex as re -from nemo_text_processing.text_normalization.data_loader_utils import ( - EOS_TYPE, - Instance, - load_files, - training_data_to_sentences, -) - - -""" -This file is for evaluation purposes. -filter_loaded_data() cleans data (list of instances) for inverse text normalization. Filters and cleaners can be specified for each semiotic class individually. -For example, normalized text should only include characters and whitespace characters but no punctuation. - Cardinal unnormalized instances should contain at least one integer and all other characters are removed. -""" - - -class Filter: - """ - Filter class - - Args: - class_type: semiotic class used in dataset - process_func: function to transform text - filter_func: function to filter text - - """ - - def __init__(self, class_type: str, process_func: object, filter_func: object): - self.class_type = class_type - self.process_func = process_func - self.filter_func = filter_func - - def filter(self, instance: Instance) -> bool: - """ - filter function - - Args: - filters given instance with filter function - - Returns: True if given instance fulfills criteria or does not belong to class type - """ - if instance.token_type != self.class_type: - return True - return self.filter_func(instance) - - def process(self, instance: Instance) -> Instance: - """ - process function - - Args: - processes given instance with process function - - Returns: processed instance if instance belongs to expected class type or original instance - """ - if instance.token_type != self.class_type: - return instance - return self.process_func(instance) - - -def filter_cardinal_1(instance: Instance) -> bool: - ok = re.search(r"[0-9]", instance.un_normalized) - return ok - - -def process_cardinal_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - un_normalized = re.sub(r"[^0-9]", "", un_normalized) - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_ordinal_1(instance: Instance) -> bool: - ok = re.search(r"(st|nd|rd|th)\s*$", instance.un_normalized) - return ok - - -def process_ordinal_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - un_normalized = re.sub(r"[,\s]", "", un_normalized) - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_decimal_1(instance: Instance) -> bool: - ok = re.search(r"[0-9]", instance.un_normalized) - return ok - - -def process_decimal_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - un_normalized = re.sub(r",", "", un_normalized) - normalized = instance.normalized - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_measure_1(instance: Instance) -> bool: - ok = True - return ok - - -def process_measure_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - un_normalized = re.sub(r",", "", un_normalized) - un_normalized = re.sub(r"m2", "m²", un_normalized) - un_normalized = re.sub(r"(\d)([^\d.\s])", r"\1 \2", un_normalized) - normalized = re.sub(r"[^a-z\s]", "", normalized) - normalized = re.sub(r"per ([a-z\s]*)s$", r"per \1", normalized) - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_money_1(instance: Instance) -> bool: - ok = re.search(r"[0-9]", instance.un_normalized) - return ok - - -def process_money_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - un_normalized = re.sub(r",", "", un_normalized) - un_normalized = re.sub(r"a\$", r"$", un_normalized) - un_normalized = re.sub(r"us\$", r"$", un_normalized) - un_normalized = re.sub(r"(\d)m\s*$", r"\1 million", un_normalized) - un_normalized = re.sub(r"(\d)bn?\s*$", r"\1 billion", un_normalized) - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_time_1(instance: Instance) -> bool: - ok = re.search(r"[0-9]", instance.un_normalized) - return ok - - -def process_time_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - un_normalized = re.sub(r": ", ":", un_normalized) - un_normalized = re.sub(r"(\d)\s?a\s?m\s?", r"\1 a.m.", un_normalized) - un_normalized = re.sub(r"(\d)\s?p\s?m\s?", r"\1 p.m.", un_normalized) - normalized = instance.normalized - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_plain_1(instance: Instance) -> bool: - ok = True - return ok - - -def process_plain_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_punct_1(instance: Instance) -> bool: - ok = True - return ok - - -def process_punct_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_date_1(instance: Instance) -> bool: - ok = True - return ok - - -def process_date_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - un_normalized = re.sub(r",", "", un_normalized) - normalized = instance.normalized - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_letters_1(instance: Instance) -> bool: - ok = True - return ok - - -def process_letters_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_verbatim_1(instance: Instance) -> bool: - ok = True - return ok - - -def process_verbatim_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_digit_1(instance: Instance) -> bool: - ok = re.search(r"[0-9]", instance.un_normalized) - return ok - - -def process_digit_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_telephone_1(instance: Instance) -> bool: - ok = re.search(r"[0-9]", instance.un_normalized) - return ok - - -def process_telephone_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_electronic_1(instance: Instance) -> bool: - ok = re.search(r"[0-9]", instance.un_normalized) - return ok - - -def process_electronic_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_fraction_1(instance: Instance) -> bool: - ok = re.search(r"[0-9]", instance.un_normalized) - return ok - - -def process_fraction_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_address_1(instance: Instance) -> bool: - ok = True - return ok - - -def process_address_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -filters = [] -filters.append(Filter(class_type="CARDINAL", process_func=process_cardinal_1, filter_func=filter_cardinal_1)) -filters.append(Filter(class_type="ORDINAL", process_func=process_ordinal_1, filter_func=filter_ordinal_1)) -filters.append(Filter(class_type="DECIMAL", process_func=process_decimal_1, filter_func=filter_decimal_1)) -filters.append(Filter(class_type="MEASURE", process_func=process_measure_1, filter_func=filter_measure_1)) -filters.append(Filter(class_type="MONEY", process_func=process_money_1, filter_func=filter_money_1)) -filters.append(Filter(class_type="TIME", process_func=process_time_1, filter_func=filter_time_1)) - -filters.append(Filter(class_type="DATE", process_func=process_date_1, filter_func=filter_date_1)) -filters.append(Filter(class_type="PLAIN", process_func=process_plain_1, filter_func=filter_plain_1)) -filters.append(Filter(class_type="PUNCT", process_func=process_punct_1, filter_func=filter_punct_1)) -filters.append(Filter(class_type="LETTERS", process_func=process_letters_1, filter_func=filter_letters_1)) -filters.append(Filter(class_type="VERBATIM", process_func=process_verbatim_1, filter_func=filter_verbatim_1)) -filters.append(Filter(class_type="DIGIT", process_func=process_digit_1, filter_func=filter_digit_1)) -filters.append(Filter(class_type="TELEPHONE", process_func=process_telephone_1, filter_func=filter_telephone_1)) -filters.append(Filter(class_type="ELECTRONIC", process_func=process_electronic_1, filter_func=filter_electronic_1)) -filters.append(Filter(class_type="FRACTION", process_func=process_fraction_1, filter_func=filter_fraction_1)) -filters.append(Filter(class_type="ADDRESS", process_func=process_address_1, filter_func=filter_address_1)) -filters.append(Filter(class_type=EOS_TYPE, process_func=lambda x: x, filter_func=lambda x: True)) - - -def filter_loaded_data(data: List[Instance], verbose: bool = False) -> List[Instance]: - """ - Filters list of instances - - Args: - data: list of instances - - Returns: filtered and transformed list of instances - """ - updates_instances = [] - for instance in data: - updated_instance = False - for fil in filters: - if fil.class_type == instance.token_type and fil.filter(instance): - instance = fil.process(instance) - updated_instance = True - if updated_instance: - if verbose: - print(instance) - updates_instances.append(instance) - return updates_instances - - -def parse_args(): - parser = ArgumentParser() - parser.add_argument("--input", help="input file path", type=str, default='./en_with_types/output-00001-of-00100') - parser.add_argument("--verbose", help="print filtered instances", action='store_true') - return parser.parse_args() - - -if __name__ == "__main__": - args = parse_args() - file_path = args.input - - print("Loading training data: " + file_path) - instance_list = load_files([file_path]) # List of instances - filtered_instance_list = filter_loaded_data(instance_list, args.verbose) - training_data_to_sentences(filtered_instance_list) diff --git a/nemo_text_processing/inverse_text_normalization/en/data/__init__.py b/nemo_text_processing/inverse_text_normalization/en/data/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/data/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/en/data/currency.tsv b/nemo_text_processing/inverse_text_normalization/en/data/currency.tsv deleted file mode 100644 index c3712552dc72..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/data/currency.tsv +++ /dev/null @@ -1,35 +0,0 @@ -$ dollar -$ us dollar -$ united states dollar -£ british pound -€ euro -₩ won -nzd new zealand dollar -rs rupee -chf swiss franc -dkk danish kroner -fim finnish markka -aed arab emirates dirham -¥ yen -czk czech koruna -mro mauritanian ouguiya -pkr pakistani rupee -crc costa rican colon -hk$ hong kong dollar -npr nepalese rupee -awg aruban florin -nok norwegian kroner -tzs tanzanian shilling -sek swedish kronor -cyp cypriot pound -r real -sar saudi riyal -cve cape verde escudo -rsd serbian dinar -dm german mark -shp saint helena pounds -php philippine peso -cad canadian dollar -ssp south sudanese pound -scr seychelles rupee -mvr maldivian rufiyaa \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/en/data/electronic/__init__.py b/nemo_text_processing/inverse_text_normalization/en/data/electronic/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/data/electronic/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/en/data/electronic/domain.tsv b/nemo_text_processing/inverse_text_normalization/en/data/electronic/domain.tsv deleted file mode 100644 index 207a14ae1c2d..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/data/electronic/domain.tsv +++ /dev/null @@ -1,10 +0,0 @@ -com -uk -fr -net -br -in -ru -de -it -ai diff --git a/nemo_text_processing/inverse_text_normalization/en/data/electronic/server_name.tsv b/nemo_text_processing/inverse_text_normalization/en/data/electronic/server_name.tsv deleted file mode 100644 index ca7f97bc9f69..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/data/electronic/server_name.tsv +++ /dev/null @@ -1,17 +0,0 @@ -g mail gmail -gmail -n vidia nvidia -nvidia -outlook -hotmail -yahoo -aol -gmx -msn -live -yandex -orange -wanadoo -web -comcast -google diff --git a/nemo_text_processing/inverse_text_normalization/en/data/electronic/symbols.tsv b/nemo_text_processing/inverse_text_normalization/en/data/electronic/symbols.tsv deleted file mode 100644 index 5be8da870eae..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/data/electronic/symbols.tsv +++ /dev/null @@ -1,22 +0,0 @@ -. dot -- dash -- hyphen -_ underscore -! exclamation mark -# number sign -$ dollar sign -% percent sign -& ampersand -' quote -* asterisk -+ plus -/ slash -= equal sign -? question mark -^ circumflex -` right single quote -{ left brace -| vertical bar -} right brace -~ tilde -, comma \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/en/data/magnitudes.tsv b/nemo_text_processing/inverse_text_normalization/en/data/magnitudes.tsv deleted file mode 100644 index 3bfa4849e869..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/data/magnitudes.tsv +++ /dev/null @@ -1,4 +0,0 @@ -k thousand -m million -b billion -t trillion \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/en/data/measurements.tsv b/nemo_text_processing/inverse_text_normalization/en/data/measurements.tsv deleted file mode 100644 index 6801b7b427b8..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/data/measurements.tsv +++ /dev/null @@ -1,109 +0,0 @@ -f fahrenheit -c celsius -km kilometer -m meter -cm centimeter -mm millimeter -ha hectare -mi mile -m² square meter -km² square kilometer -ft foot -% percent -hz hertz -kw kilowatt -hp horsepower -mg milligram -kg kilogram -ghz gigahertz -khz kilohertz -mhz megahertz -v volt -h hour -mc mega coulomb -s second -nm nanometer -rpm revolution per minute -min minute -mA milli ampere -% per cent -kwh kilo watt hour -m³ cubic meter -mph mile per hour -tw tera watt -mv milli volt -mw megawatt -μm micrometer -" inch -tb terabyte -cc c c -g gram -da dalton -atm atmosphere -ω ohm -db decibel -ps peta second -oz ounce -hl hecto liter -μg microgram -pg petagram -gb gigabyte -kb kilobit -ev electron volt -mb megabyte -kb kilobyte -kbps kilobit per second -mbps megabit per second -st stone -kl kilo liter -tj tera joule -kv kilo volt -mv mega volt -kn kilonewton -mm megameter -au astronomical unit -yd yard -rad radian -lm lumen -hs hecto second -mol mole -gpa giga pascal -ml milliliter -gw gigawatt -ma mega ampere -kt knot -kgf kilogram force -ng nano gram -ns nanosecond -ms mega siemens -bar bar -gl giga liter -μs microsecond -da deci ampere -pa pascal -ds deci second -ms milli second -dm deci meter -dm³ cubic deci meter -amu atomic mass unit -mb megabit -mf mega farad -bq becquerel -pb petabit -mm² square millimeter -cm² square centimeter -sq mi square mile -sq ft square foot -kpa kilopascal -cd candela -tl tera liter -ms mega second -mpa megapascal -pm peta meter -pb peta byte -gwh giga watt hour -kcal kilo calory -gy gray -sv sievert -cwt hundredweight -cc c c diff --git a/nemo_text_processing/inverse_text_normalization/en/data/months.tsv b/nemo_text_processing/inverse_text_normalization/en/data/months.tsv deleted file mode 100644 index 6e7903c0ba26..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/data/months.tsv +++ /dev/null @@ -1,12 +0,0 @@ -january -february -march -april -may -june -july -august -september -october -november -december \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/en/data/numbers/__init__.py b/nemo_text_processing/inverse_text_normalization/en/data/numbers/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/data/numbers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/en/data/numbers/digit.tsv b/nemo_text_processing/inverse_text_normalization/en/data/numbers/digit.tsv deleted file mode 100644 index fa329799db6f..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/data/numbers/digit.tsv +++ /dev/null @@ -1,9 +0,0 @@ -one 1 -two 2 -three 3 -four 4 -five 5 -six 6 -seven 7 -eight 8 -nine 9 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/en/data/numbers/hundred.tsv b/nemo_text_processing/inverse_text_normalization/en/data/numbers/hundred.tsv deleted file mode 100644 index 10e2b9645a53..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/data/numbers/hundred.tsv +++ /dev/null @@ -1 +0,0 @@ -hundred \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/en/data/numbers/teen.tsv b/nemo_text_processing/inverse_text_normalization/en/data/numbers/teen.tsv deleted file mode 100644 index 8e60fa1d3cb3..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/data/numbers/teen.tsv +++ /dev/null @@ -1,10 +0,0 @@ -ten 10 -eleven 11 -twelve 12 -thirteen 13 -fourteen 14 -fifteen 15 -sixteen 16 -seventeen 17 -eighteen 18 -nineteen 19 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/en/data/numbers/thousands.tsv b/nemo_text_processing/inverse_text_normalization/en/data/numbers/thousands.tsv deleted file mode 100644 index bf30542bb6bc..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/data/numbers/thousands.tsv +++ /dev/null @@ -1,22 +0,0 @@ -thousand -million -billion -trillion -quadrillion -quintillion -sextillion -septillion -octillion -nonillion -decillion -undecillion -duodecillion -tredecillion -quattuordecillion -quindecillion -sexdecillion -septendecillion -octodecillion -novemdecillion -vigintillion -centillion \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/en/data/numbers/ties.tsv b/nemo_text_processing/inverse_text_normalization/en/data/numbers/ties.tsv deleted file mode 100644 index d7ec82e11478..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/data/numbers/ties.tsv +++ /dev/null @@ -1,9 +0,0 @@ -twenty 2 -thirty 3 -forty 4 -fourty 4 -fifty 5 -sixty 6 -seventy 7 -eighty 8 -ninety 9 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/en/data/numbers/zero.tsv b/nemo_text_processing/inverse_text_normalization/en/data/numbers/zero.tsv deleted file mode 100644 index c479272d4039..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/data/numbers/zero.tsv +++ /dev/null @@ -1 +0,0 @@ -zero 0 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/en/data/ordinals/__init__.py b/nemo_text_processing/inverse_text_normalization/en/data/ordinals/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/data/ordinals/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/en/data/ordinals/digit.tsv b/nemo_text_processing/inverse_text_normalization/en/data/ordinals/digit.tsv deleted file mode 100644 index 4b7fc24f1606..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/data/ordinals/digit.tsv +++ /dev/null @@ -1,9 +0,0 @@ -first one -second two -third three -fourth four -fifth five -sixth sixth -seventh seven -eighth eight -ninth nine \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/en/data/ordinals/teen.tsv b/nemo_text_processing/inverse_text_normalization/en/data/ordinals/teen.tsv deleted file mode 100644 index 496fefc086d1..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/data/ordinals/teen.tsv +++ /dev/null @@ -1 +0,0 @@ -twelfth twelve \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/en/data/time/__init__.py b/nemo_text_processing/inverse_text_normalization/en/data/time/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/data/time/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/en/data/time/minute_to.tsv b/nemo_text_processing/inverse_text_normalization/en/data/time/minute_to.tsv deleted file mode 100644 index edab4d5b03d6..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/data/time/minute_to.tsv +++ /dev/null @@ -1,59 +0,0 @@ -1 59 -2 58 -3 57 -4 56 -5 55 -6 54 -7 53 -8 52 -9 51 -10 50 -11 49 -12 48 -13 47 -14 46 -15 45 -16 44 -17 43 -18 42 -19 41 -20 40 -21 39 -22 38 -23 37 -24 36 -25 35 -26 34 -27 33 -28 32 -29 31 -30 30 -31 29 -32 28 -33 27 -34 26 -35 25 -36 24 -37 23 -38 22 -39 21 -40 20 -41 19 -42 18 -43 17 -44 16 -45 15 -46 14 -47 13 -48 12 -49 11 -50 10 -51 9 -52 8 -53 7 -54 6 -55 5 -56 4 -57 3 -58 2 -59 1 diff --git a/nemo_text_processing/inverse_text_normalization/en/data/time/time_suffix.tsv b/nemo_text_processing/inverse_text_normalization/en/data/time/time_suffix.tsv deleted file mode 100644 index b39a9ef9a92c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/data/time/time_suffix.tsv +++ /dev/null @@ -1,8 +0,0 @@ -p m p.m. -pm p.m. -p.m. -p.m p.m. -am a.m. -a.m. -a.m a.m. -a m a.m. \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/en/data/time/time_zone.tsv b/nemo_text_processing/inverse_text_normalization/en/data/time/time_zone.tsv deleted file mode 100644 index 3e0ade467a84..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/data/time/time_zone.tsv +++ /dev/null @@ -1,7 +0,0 @@ -cst c s t -cet c e t -pst p s t -est e s t -pt p t -et e t -gmt g m t diff --git a/nemo_text_processing/inverse_text_normalization/en/data/time/to_hour.tsv b/nemo_text_processing/inverse_text_normalization/en/data/time/to_hour.tsv deleted file mode 100644 index 1b006ea3ea0f..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/data/time/to_hour.tsv +++ /dev/null @@ -1,12 +0,0 @@ -one 12 -two 1 -three 2 -four 3 -five 4 -six 5 -seven 6 -eigh 7 -nine 8 -ten 9 -eleven 10 -twelve 11 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/en/data/whitelist.tsv b/nemo_text_processing/inverse_text_normalization/en/data/whitelist.tsv deleted file mode 100644 index 7eed41ecf2ee..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/data/whitelist.tsv +++ /dev/null @@ -1,12 +0,0 @@ -e.g. for example -dr. doctor -mr. mister -mrs. misses -st. saint -7-eleven seven eleven -es3 e s three -s&p s and p -ASAP a s a p -AT&T a t and t -LLP l l p -ATM a t m diff --git a/nemo_text_processing/inverse_text_normalization/en/taggers/__init__.py b/nemo_text_processing/inverse_text_normalization/en/taggers/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/taggers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/en/taggers/cardinal.py b/nemo_text_processing/inverse_text_normalization/en/taggers/cardinal.py deleted file mode 100644 index ac72a2bf0e9a..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/taggers/cardinal.py +++ /dev/null @@ -1,143 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.inverse_text_normalization.en.utils import get_abs_path, num_to_word -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_ALPHA, - NEMO_DIGIT, - NEMO_SIGMA, - NEMO_SPACE, - GraphFst, - delete_space, -) -from pynini.lib import pynutil - - -class CardinalFst(GraphFst): - """ - Finite state transducer for classifying cardinals - e.g. minus twenty three -> cardinal { integer: "23" negative: "-" } } - Numbers below thirteen are not converted. - """ - - def __init__(self): - super().__init__(name="cardinal", kind="classify") - graph_zero = pynini.string_file(get_abs_path("data/numbers/zero.tsv")) - graph_digit = pynini.string_file(get_abs_path("data/numbers/digit.tsv")) - graph_ties = pynini.string_file(get_abs_path("data/numbers/ties.tsv")) - graph_teen = pynini.string_file(get_abs_path("data/numbers/teen.tsv")) - - graph_hundred = pynini.cross("hundred", "") - - graph_hundred_component = pynini.union(graph_digit + delete_space + graph_hundred, pynutil.insert("0")) - graph_hundred_component += delete_space - graph_hundred_component += pynini.union( - graph_teen | pynutil.insert("00"), - (graph_ties | pynutil.insert("0")) + delete_space + (graph_digit | pynutil.insert("0")), - ) - - graph_hundred_component_at_least_one_none_zero_digit = graph_hundred_component @ ( - pynini.closure(NEMO_DIGIT) + (NEMO_DIGIT - "0") + pynini.closure(NEMO_DIGIT) - ) - self.graph_hundred_component_at_least_one_none_zero_digit = ( - graph_hundred_component_at_least_one_none_zero_digit - ) - - # Transducer for eleven hundred -> 1100 or twenty one hundred eleven -> 2111 - graph_hundred_as_thousand = pynini.union(graph_teen, graph_ties + delete_space + graph_digit) - graph_hundred_as_thousand += delete_space + graph_hundred - graph_hundred_as_thousand += delete_space + pynini.union( - graph_teen | pynutil.insert("00"), - (graph_ties | pynutil.insert("0")) + delete_space + (graph_digit | pynutil.insert("0")), - ) - - graph_hundreds = graph_hundred_component | graph_hundred_as_thousand - - graph_thousands = pynini.union( - graph_hundred_component_at_least_one_none_zero_digit + delete_space + pynutil.delete("thousand"), - pynutil.insert("000", weight=0.1), - ) - - graph_million = pynini.union( - graph_hundred_component_at_least_one_none_zero_digit + delete_space + pynutil.delete("million"), - pynutil.insert("000", weight=0.1), - ) - graph_billion = pynini.union( - graph_hundred_component_at_least_one_none_zero_digit + delete_space + pynutil.delete("billion"), - pynutil.insert("000", weight=0.1), - ) - graph_trillion = pynini.union( - graph_hundred_component_at_least_one_none_zero_digit + delete_space + pynutil.delete("trillion"), - pynutil.insert("000", weight=0.1), - ) - graph_quadrillion = pynini.union( - graph_hundred_component_at_least_one_none_zero_digit + delete_space + pynutil.delete("quadrillion"), - pynutil.insert("000", weight=0.1), - ) - graph_quintillion = pynini.union( - graph_hundred_component_at_least_one_none_zero_digit + delete_space + pynutil.delete("quintillion"), - pynutil.insert("000", weight=0.1), - ) - graph_sextillion = pynini.union( - graph_hundred_component_at_least_one_none_zero_digit + delete_space + pynutil.delete("sextillion"), - pynutil.insert("000", weight=0.1), - ) - - graph = pynini.union( - graph_sextillion - + delete_space - + graph_quintillion - + delete_space - + graph_quadrillion - + delete_space - + graph_trillion - + delete_space - + graph_billion - + delete_space - + graph_million - + delete_space - + graph_thousands - + delete_space - + graph_hundreds, - graph_zero, - ) - - graph = graph @ pynini.union( - pynutil.delete(pynini.closure("0")) + pynini.difference(NEMO_DIGIT, "0") + pynini.closure(NEMO_DIGIT), "0" - ) - - labels_exception = [num_to_word(x) for x in range(0, 13)] - graph_exception = pynini.union(*labels_exception) - - graph = ( - pynini.cdrewrite(pynutil.delete("and"), NEMO_SPACE, NEMO_SPACE, NEMO_SIGMA) - @ (NEMO_ALPHA + NEMO_SIGMA) - @ graph - ) - - self.graph_no_exception = graph - - self.graph = (pynini.project(graph, "input") - graph_exception.arcsort()) @ graph - - optional_minus_graph = pynini.closure( - pynutil.insert("negative: ") + pynini.cross("minus", "\"-\"") + NEMO_SPACE, 0, 1 - ) - - final_graph = optional_minus_graph + pynutil.insert("integer: \"") + self.graph + pynutil.insert("\"") - - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/en/taggers/date.py b/nemo_text_processing/inverse_text_normalization/en/taggers/date.py deleted file mode 100644 index 43fe65f3e2e8..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/taggers/date.py +++ /dev/null @@ -1,154 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.en.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_ALPHA, - NEMO_DIGIT, - GraphFst, - delete_extra_space, - delete_space, -) -from pynini.lib import pynutil - -graph_teen = pynini.string_file(get_abs_path("data/numbers/teen.tsv")).optimize() -graph_digit = pynini.string_file(get_abs_path("data/numbers/digit.tsv")).optimize() -ties_graph = pynini.string_file(get_abs_path("data/numbers/ties.tsv")).optimize() - - -def _get_month_graph(): - """ - Transducer for month, e.g. march -> march - """ - month_graph = pynini.string_file(get_abs_path("data/months.tsv")) - return month_graph - - -def _get_ties_graph(): - """ - Transducer for 20-99 e.g - twenty three -> 23 - """ - graph = ties_graph + (delete_space + graph_digit | pynutil.insert("0")) - return graph - - -def _get_range_graph(): - """ - Transducer for decades (1**0s, 2**0s), centuries (2*00s, 1*00s), millennia (2000s) - """ - graph_ties = _get_ties_graph() - graph = (graph_ties | graph_teen) + delete_space + pynini.cross("hundreds", "00s") - graph |= pynini.cross("two", "2") + delete_space + pynini.cross("thousands", "000s") - graph |= ( - (graph_ties | graph_teen) - + delete_space - + (pynini.closure(NEMO_ALPHA, 1) + (pynini.cross("ies", "y") | pynutil.delete("s"))) - @ (graph_ties | pynini.cross("ten", "10")) - + pynutil.insert("s") - ) - graph @= pynini.union("1", "2") + NEMO_DIGIT + NEMO_DIGIT + NEMO_DIGIT + "s" - return graph - - -def _get_year_graph(): - """ - Transducer for year, e.g. twenty twenty -> 2020 - """ - - def _get_digits_graph(): - zero = pynini.cross((pynini.accep("oh") | pynini.accep("o")), "0") - graph = zero + delete_space + graph_digit - graph.optimize() - return graph - - def _get_thousands_graph(): - graph_ties = _get_ties_graph() - graph_hundred_component = (graph_digit + delete_space + pynutil.delete("hundred")) | pynutil.insert("0") - optional_end = pynini.closure(pynutil.delete("and "), 0, 1) - graph = ( - graph_digit - + delete_space - + pynutil.delete("thousand") - + delete_space - + graph_hundred_component - + delete_space - + (graph_teen | graph_ties | (optional_end + pynutil.insert("0") + graph_digit)) - ) - return graph - - graph_ties = _get_ties_graph() - graph_digits = _get_digits_graph() - graph_thousands = _get_thousands_graph() - year_graph = ( - # 20 19, 40 12, 2012 - assuming no limit on the year - (graph_teen + delete_space + (graph_ties | graph_digits | graph_teen)) - | (graph_ties + delete_space + (graph_ties | graph_digits | graph_teen)) - | graph_thousands - ) - year_graph.optimize() - return year_graph - - -class DateFst(GraphFst): - """ - Finite state transducer for classifying date, - e.g. january fifth twenty twelve -> date { month: "january" day: "5" year: "2012" preserve_order: true } - e.g. the fifth of january twenty twelve -> date { day: "5" month: "january" year: "2012" preserve_order: true } - e.g. twenty twenty -> date { year: "2012" preserve_order: true } - - Args: - ordinal: OrdinalFst - """ - - def __init__(self, ordinal: GraphFst): - super().__init__(name="date", kind="classify") - - ordinal_graph = ordinal.graph - year_graph = _get_year_graph() - YEAR_WEIGHT = 0.001 - year_graph = pynutil.add_weight(year_graph, YEAR_WEIGHT) - month_graph = _get_month_graph() - - month_graph = pynutil.insert("month: \"") + month_graph + pynutil.insert("\"") - - day_graph = pynutil.insert("day: \"") + pynutil.add_weight(ordinal_graph, -0.7) + pynutil.insert("\"") - graph_year = ( - delete_extra_space - + pynutil.insert("year: \"") - + pynutil.add_weight(year_graph, -YEAR_WEIGHT) - + pynutil.insert("\"") - ) - optional_graph_year = pynini.closure(graph_year, 0, 1,) - graph_mdy = month_graph + ( - (delete_extra_space + day_graph) | graph_year | (delete_extra_space + day_graph + graph_year) - ) - graph_dmy = ( - pynutil.delete("the") - + delete_space - + day_graph - + delete_space - + pynutil.delete("of") - + delete_extra_space - + month_graph - + optional_graph_year - ) - graph_year = pynutil.insert("year: \"") + (year_graph | _get_range_graph()) + pynutil.insert("\"") - - final_graph = graph_mdy | graph_dmy | graph_year - final_graph += pynutil.insert(" preserve_order: true") - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/en/taggers/decimal.py b/nemo_text_processing/inverse_text_normalization/en/taggers/decimal.py deleted file mode 100644 index 0112cdc81b09..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/taggers/decimal.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.en.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_DIGIT, - GraphFst, - delete_extra_space, - delete_space, -) -from pynini.lib import pynutil - - -def get_quantity(decimal: 'pynini.FstLike', cardinal_up_to_hundred: 'pynini.FstLike') -> 'pynini.FstLike': - """ - Returns FST that transforms either a cardinal or decimal followed by a quantity into a numeral, - e.g. one million -> integer_part: "1" quantity: "million" - e.g. one point five million -> integer_part: "1" fractional_part: "5" quantity: "million" - - Args: - decimal: decimal FST - cardinal_up_to_hundred: cardinal FST - """ - numbers = cardinal_up_to_hundred @ ( - pynutil.delete(pynini.closure("0")) + pynini.difference(NEMO_DIGIT, "0") + pynini.closure(NEMO_DIGIT) - ) - suffix = pynini.union("million", "billion", "trillion", "quadrillion", "quintillion", "sextillion") - res = ( - pynutil.insert("integer_part: \"") - + numbers - + pynutil.insert("\"") - + delete_extra_space - + pynutil.insert("quantity: \"") - + suffix - + pynutil.insert("\"") - ) - res |= decimal + delete_extra_space + pynutil.insert("quantity: \"") + (suffix | "thousand") + pynutil.insert("\"") - return res - - -class DecimalFst(GraphFst): - """ - Finite state transducer for classifying decimal - e.g. minus twelve point five o o six billion -> decimal { negative: "true" integer_part: "12" fractional_part: "5006" quantity: "billion" } - e.g. one billion -> decimal { integer_part: "1" quantity: "billion" } - Args: - cardinal: CardinalFst - """ - - def __init__(self, cardinal: GraphFst): - super().__init__(name="decimal", kind="classify") - - cardinal_graph = cardinal.graph_no_exception - - graph_decimal = pynini.string_file(get_abs_path("data/numbers/digit.tsv")) - graph_decimal |= pynini.string_file(get_abs_path("data/numbers/zero.tsv")) | pynini.cross("o", "0") - - graph_decimal = pynini.closure(graph_decimal + delete_space) + graph_decimal - self.graph = graph_decimal - - point = pynutil.delete("point") - - optional_graph_negative = pynini.closure( - pynutil.insert("negative: ") + pynini.cross("minus", "\"true\"") + delete_extra_space, 0, 1 - ) - - graph_fractional = pynutil.insert("fractional_part: \"") + graph_decimal + pynutil.insert("\"") - graph_integer = pynutil.insert("integer_part: \"") + cardinal_graph + pynutil.insert("\"") - final_graph_wo_sign = ( - pynini.closure(graph_integer + delete_extra_space, 0, 1) + point + delete_extra_space + graph_fractional - ) - final_graph = optional_graph_negative + final_graph_wo_sign - - self.final_graph_wo_negative = final_graph_wo_sign | get_quantity( - final_graph_wo_sign, cardinal.graph_hundred_component_at_least_one_none_zero_digit - ) - final_graph |= optional_graph_negative + get_quantity( - final_graph_wo_sign, cardinal.graph_hundred_component_at_least_one_none_zero_digit - ) - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/en/taggers/electronic.py b/nemo_text_processing/inverse_text_normalization/en/taggers/electronic.py deleted file mode 100644 index 5a2ef1a023bc..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/taggers/electronic.py +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.en.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_ALPHA, GraphFst, insert_space -from pynini.lib import pynutil - - -class ElectronicFst(GraphFst): - """ - Finite state transducer for classifying electronic: as URLs, email addresses, etc. - e.g. c d f one at a b c dot e d u -> tokens { electronic { username: "cdf1" domain: "abc.edu" } } - """ - - def __init__(self): - super().__init__(name="electronic", kind="classify") - - delete_extra_space = pynutil.delete(" ") - alpha_num = ( - NEMO_ALPHA - | pynini.string_file(get_abs_path("data/numbers/digit.tsv")) - | pynini.string_file(get_abs_path("data/numbers/zero.tsv")) - ) - - symbols = pynini.string_file(get_abs_path("data/electronic/symbols.tsv")).invert() - - accepted_username = alpha_num | symbols - process_dot = pynini.cross("dot", ".") - username = (alpha_num + pynini.closure(delete_extra_space + accepted_username)) | pynutil.add_weight( - pynini.closure(NEMO_ALPHA, 1), weight=0.0001 - ) - username = pynutil.insert("username: \"") + username + pynutil.insert("\"") - single_alphanum = pynini.closure(alpha_num + delete_extra_space) + alpha_num - server = single_alphanum | pynini.string_file(get_abs_path("data/electronic/server_name.tsv")) - domain = single_alphanum | pynini.string_file(get_abs_path("data/electronic/domain.tsv")) - domain_graph = ( - pynutil.insert("domain: \"") - + server - + delete_extra_space - + process_dot - + delete_extra_space - + domain - + pynutil.insert("\"") - ) - graph = username + delete_extra_space + pynutil.delete("at") + insert_space + delete_extra_space + domain_graph - - ############# url ### - protocol_end = pynini.cross(pynini.union("w w w", "www"), "www") - protocol_start = (pynini.cross("h t t p", "http") | pynini.cross("h t t p s", "https")) + pynini.cross( - " colon slash slash ", "://" - ) - # .com, - ending = ( - delete_extra_space - + symbols - + delete_extra_space - + (domain | pynini.closure(accepted_username + delete_extra_space,) + accepted_username) - ) - - protocol_default = ( - ( - (pynini.closure(delete_extra_space + accepted_username, 1) | server) - | pynutil.add_weight(pynini.closure(NEMO_ALPHA, 1), weight=0.0001) - ) - + pynini.closure(ending, 1) - ).optimize() - protocol = ( - pynini.closure(protocol_start, 0, 1) + protocol_end + delete_extra_space + process_dot + protocol_default - ).optimize() - - protocol |= pynini.closure(protocol_end + delete_extra_space + process_dot, 0, 1) + protocol_default - - protocol = pynutil.insert("protocol: \"") + protocol.optimize() + pynutil.insert("\"") - graph |= protocol - ######## - - final_graph = self.add_tokens(graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/en/taggers/fraction.py b/nemo_text_processing/inverse_text_normalization/en/taggers/fraction.py deleted file mode 100644 index 5e7fd7dad848..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/taggers/fraction.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst - - -class FractionFst(GraphFst): - """ - Finite state transducer for classifying fraction - """ - - def __init__(self): - super().__init__(name="fraction", kind="classify") - # integer_part # numerator # denominator diff --git a/nemo_text_processing/inverse_text_normalization/en/taggers/measure.py b/nemo_text_processing/inverse_text_normalization/en/taggers/measure.py deleted file mode 100644 index c050bc759379..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/taggers/measure.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.en.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_SIGMA, - GraphFst, - convert_space, - delete_extra_space, - delete_space, - get_singulars, -) -from pynini.lib import pynutil - - -class MeasureFst(GraphFst): - """ - Finite state transducer for classifying measure - e.g. minus twelve kilograms -> measure { negative: "true" cardinal { integer: "12" } units: "kg" } - - Args: - cardinal: CardinalFst - decimal: DecimalFst - """ - - def __init__(self, cardinal: GraphFst, decimal: GraphFst): - super().__init__(name="measure", kind="classify") - - cardinal_graph = cardinal.graph_no_exception - - graph_unit = pynini.string_file(get_abs_path("data/measurements.tsv")) - graph_unit_singular = pynini.invert(graph_unit) # singular -> abbr - graph_unit_plural = get_singulars(graph_unit_singular) # plural -> abbr - - optional_graph_negative = pynini.closure( - pynutil.insert("negative: ") + pynini.cross("minus", "\"true\"") + delete_extra_space, 0, 1 - ) - - unit_singular = convert_space(graph_unit_singular) - unit_plural = convert_space(graph_unit_plural) - unit_misc = pynutil.insert("/") + pynutil.delete("per") + delete_space + convert_space(graph_unit_singular) - - unit_singular = ( - pynutil.insert("units: \"") - + (unit_singular | unit_misc | pynutil.add_weight(unit_singular + delete_space + unit_misc, 0.01)) - + pynutil.insert("\"") - ) - unit_plural = ( - pynutil.insert("units: \"") - + (unit_plural | unit_misc | pynutil.add_weight(unit_plural + delete_space + unit_misc, 0.01)) - + pynutil.insert("\"") - ) - - subgraph_decimal = ( - pynutil.insert("decimal { ") - + optional_graph_negative - + decimal.final_graph_wo_negative - + pynutil.insert(" }") - + delete_extra_space - + unit_plural - ) - subgraph_cardinal = ( - pynutil.insert("cardinal { ") - + optional_graph_negative - + pynutil.insert("integer: \"") - + ((NEMO_SIGMA - "one") @ cardinal_graph) - + pynutil.insert("\"") - + pynutil.insert(" }") - + delete_extra_space - + unit_plural - ) - subgraph_cardinal |= ( - pynutil.insert("cardinal { ") - + optional_graph_negative - + pynutil.insert("integer: \"") - + pynini.cross("one", "1") - + pynutil.insert("\"") - + pynutil.insert(" }") - + delete_extra_space - + unit_singular - ) - final_graph = subgraph_decimal | subgraph_cardinal - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/en/taggers/money.py b/nemo_text_processing/inverse_text_normalization/en/taggers/money.py deleted file mode 100644 index c6227b7efd20..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/taggers/money.py +++ /dev/null @@ -1,115 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.en.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_DIGIT, - NEMO_NOT_SPACE, - NEMO_SIGMA, - GraphFst, - convert_space, - delete_extra_space, - delete_space, - get_singulars, - insert_space, -) -from pynini.lib import pynutil - - -class MoneyFst(GraphFst): - """ - Finite state transducer for classifying money - e.g. twelve dollars and five cents -> money { integer_part: "12" fractional_part: 05 currency: "$" } - - Args: - cardinal: CardinalFst - decimal: DecimalFst - """ - - def __init__(self, cardinal: GraphFst, decimal: GraphFst): - super().__init__(name="money", kind="classify") - # quantity, integer_part, fractional_part, currency - - cardinal_graph = cardinal.graph_no_exception - # add support for missing hundred (only for 3 digit numbers) - # "one fifty" -> "one hundred fifty" - with_hundred = pynini.compose( - pynini.closure(NEMO_NOT_SPACE) + pynini.accep(" ") + pynutil.insert("hundred ") + NEMO_SIGMA, - pynini.compose(cardinal_graph, NEMO_DIGIT ** 3), - ) - cardinal_graph |= with_hundred - graph_decimal_final = decimal.final_graph_wo_negative - - unit = pynini.string_file(get_abs_path("data/currency.tsv")) - unit_singular = pynini.invert(unit) - unit_plural = get_singulars(unit_singular) - - graph_unit_singular = pynutil.insert("currency: \"") + convert_space(unit_singular) + pynutil.insert("\"") - graph_unit_plural = pynutil.insert("currency: \"") + convert_space(unit_plural) + pynutil.insert("\"") - - add_leading_zero_to_double_digit = (NEMO_DIGIT + NEMO_DIGIT) | (pynutil.insert("0") + NEMO_DIGIT) - # twelve dollars (and) fifty cents, zero cents - cents_standalone = ( - pynutil.insert("fractional_part: \"") - + pynini.union( - pynutil.add_weight(((NEMO_SIGMA - "one") @ cardinal_graph), -0.7) @ add_leading_zero_to_double_digit - + delete_space - + pynutil.delete("cents"), - pynini.cross("one", "01") + delete_space + pynutil.delete("cent"), - ) - + pynutil.insert("\"") - ) - - optional_cents_standalone = pynini.closure( - delete_space - + pynini.closure(pynutil.delete("and") + delete_space, 0, 1) - + insert_space - + cents_standalone, - 0, - 1, - ) - # twelve dollars fifty, only after integer - optional_cents_suffix = pynini.closure( - delete_extra_space - + pynutil.insert("fractional_part: \"") - + pynutil.add_weight(cardinal_graph @ add_leading_zero_to_double_digit, -0.7) - + pynutil.insert("\""), - 0, - 1, - ) - - graph_integer = ( - pynutil.insert("integer_part: \"") - + ((NEMO_SIGMA - "one") @ cardinal_graph) - + pynutil.insert("\"") - + delete_extra_space - + graph_unit_plural - + (optional_cents_standalone | optional_cents_suffix) - ) - graph_integer |= ( - pynutil.insert("integer_part: \"") - + pynini.cross("one", "1") - + pynutil.insert("\"") - + delete_extra_space - + graph_unit_singular - + (optional_cents_standalone | optional_cents_suffix) - ) - graph_decimal = graph_decimal_final + delete_extra_space + graph_unit_plural - graph_decimal |= pynutil.insert("currency: \"$\" integer_part: \"0\" ") + cents_standalone - final_graph = graph_integer | graph_decimal - - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/en/taggers/ordinal.py b/nemo_text_processing/inverse_text_normalization/en/taggers/ordinal.py deleted file mode 100644 index ea80bc7b512d..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/taggers/ordinal.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.en.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_CHAR, GraphFst -from pynini.lib import pynutil - - -class OrdinalFst(GraphFst): - """ - Finite state transducer for classifying ordinal - e.g. thirteenth -> ordinal { integer: "13" } - - Args: - cardinal: CardinalFst - """ - - def __init__(self, cardinal: GraphFst): - super().__init__(name="ordinal", kind="classify") - - cardinal_graph = cardinal.graph_no_exception - graph_digit = pynini.string_file(get_abs_path("data/ordinals/digit.tsv")) - graph_teens = pynini.string_file(get_abs_path("data/ordinals/teen.tsv")) - graph = pynini.closure(NEMO_CHAR) + pynini.union( - graph_digit, graph_teens, pynini.cross("tieth", "ty"), pynini.cross("th", "") - ) - - self.graph = graph @ cardinal_graph - final_graph = pynutil.insert("integer: \"") + self.graph + pynutil.insert("\"") - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/en/taggers/punctuation.py b/nemo_text_processing/inverse_text_normalization/en/taggers/punctuation.py deleted file mode 100644 index 10de67cdb08e..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/taggers/punctuation.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst -from pynini.lib import pynutil - - -class PunctuationFst(GraphFst): - """ - Finite state transducer for classifying punctuation - e.g. a, -> tokens { name: "a" } tokens { name: "," } - """ - - def __init__(self): - super().__init__(name="punctuation", kind="classify") - - s = "!#$%&\'()*+,-./:;<=>?@^_`{|}~" - punct = pynini.union(*s) - - graph = pynutil.insert("name: \"") + punct + pynutil.insert("\"") - - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/en/taggers/telephone.py b/nemo_text_processing/inverse_text_normalization/en/taggers/telephone.py deleted file mode 100644 index b230db216250..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/taggers/telephone.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.en.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_ALNUM, - NEMO_ALPHA, - NEMO_DIGIT, - GraphFst, - insert_space, -) -from pynini.lib import pynutil - - -def get_serial_number(cardinal): - """ - any alphanumerical character sequence with at least one number with length greater equal to 3 - """ - digit = pynini.compose(cardinal.graph_no_exception, NEMO_DIGIT) - character = digit | NEMO_ALPHA - sequence = character + pynini.closure(pynutil.delete(" ") + character, 2) - sequence = sequence @ (pynini.closure(NEMO_ALNUM) + NEMO_DIGIT + pynini.closure(NEMO_ALNUM)) - return sequence.optimize() - - -class TelephoneFst(GraphFst): - """ - Finite state transducer for classifying telephone numbers, e.g. - one two three one two three five six seven eight -> { number_part: "123-123-5678" } - - This class also support card number and IP format. - "one two three dot one double three dot o dot four o" -> { number_part: "123.133.0.40"} - - "three two double seven three two one four three two one four three double zero five" -> - { number_part: 3277 3214 3214 3005} - - Args: - cardinal: CardinalFst - """ - - def __init__(self, cardinal: GraphFst): - super().__init__(name="telephone", kind="classify") - # country code, number_part, extension - digit_to_str = ( - pynini.invert(pynini.string_file(get_abs_path("data/numbers/digit.tsv")).optimize()) - | pynini.cross("0", pynini.union("o", "oh", "zero")).optimize() - ) - - str_to_digit = pynini.invert(digit_to_str) - - double_digit = pynini.union( - *[ - pynini.cross( - pynini.project(str(i) @ digit_to_str, "output") - + pynini.accep(" ") - + pynini.project(str(i) @ digit_to_str, "output"), - pynutil.insert("double ") + pynini.project(str(i) @ digit_to_str, "output"), - ) - for i in range(10) - ] - ) - double_digit.invert() - - # to handle cases like "one twenty three" - two_digit_cardinal = pynini.compose(cardinal.graph_no_exception, NEMO_DIGIT ** 2) - double_digit_to_digit = ( - pynini.compose(double_digit, str_to_digit + pynutil.delete(" ") + str_to_digit) | two_digit_cardinal - ) - - single_or_double_digit = (pynutil.add_weight(double_digit_to_digit, -0.0001) | str_to_digit).optimize() - single_or_double_digit |= ( - single_or_double_digit - + pynini.closure(pynutil.add_weight(pynutil.delete(" ") + single_or_double_digit, 0.0001)) - ).optimize() - - number_part = pynini.compose( - single_or_double_digit, - NEMO_DIGIT ** 3 + pynutil.insert("-") + NEMO_DIGIT ** 3 + pynutil.insert("-") + NEMO_DIGIT ** 4, - ).optimize() - number_part = pynutil.insert("number_part: \"") + number_part.optimize() + pynutil.insert("\"") - - cardinal_option = pynini.compose(single_or_double_digit, NEMO_DIGIT ** (2, 3)) - - country_code = ( - pynutil.insert("country_code: \"") - + pynini.closure(pynini.cross("plus ", "+"), 0, 1) - + ((pynini.closure(str_to_digit + pynutil.delete(" "), 0, 2) + str_to_digit) | cardinal_option) - + pynutil.insert("\"") - ) - - optional_country_code = pynini.closure(country_code + pynutil.delete(" ") + insert_space, 0, 1).optimize() - graph = optional_country_code + number_part - - # credit card number - space_four_digits = insert_space + NEMO_DIGIT ** 4 - credit_card_graph = pynini.compose(single_or_double_digit, NEMO_DIGIT ** 4 + space_four_digits ** 3).optimize() - graph |= pynutil.insert("number_part: \"") + credit_card_graph.optimize() + pynutil.insert("\"") - - # SSN - ssn_graph = pynini.compose( - single_or_double_digit, - NEMO_DIGIT ** 3 + pynutil.insert("-") + NEMO_DIGIT ** 2 + pynutil.insert("-") + NEMO_DIGIT ** 4, - ).optimize() - graph |= pynutil.insert("number_part: \"") + ssn_graph.optimize() + pynutil.insert("\"") - - # ip - digit_or_double = pynini.closure(str_to_digit + pynutil.delete(" "), 0, 1) + double_digit_to_digit - digit_or_double |= double_digit_to_digit + pynini.closure(pynutil.delete(" ") + str_to_digit, 0, 1) - digit_or_double |= str_to_digit + (pynutil.delete(" ") + str_to_digit) ** (0, 2) - digit_or_double |= cardinal_option - digit_or_double = digit_or_double.optimize() - - ip_graph = digit_or_double + (pynini.cross(" dot ", ".") + digit_or_double) ** 3 - - graph |= pynutil.insert("number_part: \"") + ip_graph.optimize() + pynutil.insert("\"") - graph |= ( - pynutil.insert("number_part: \"") - + pynutil.add_weight(get_serial_number(cardinal=cardinal), weight=0.0001) - + pynutil.insert("\"") - ) - - final_graph = self.add_tokens(graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/en/taggers/time.py b/nemo_text_processing/inverse_text_normalization/en/taggers/time.py deleted file mode 100644 index aceb50799239..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/taggers/time.py +++ /dev/null @@ -1,143 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.inverse_text_normalization.en.taggers.cardinal import CardinalFst -from nemo_text_processing.inverse_text_normalization.en.utils import get_abs_path, num_to_word -from nemo_text_processing.text_normalization.en.graph_utils import ( - GraphFst, - convert_space, - delete_extra_space, - delete_space, - insert_space, -) -from pynini.lib import pynutil - - -class TimeFst(GraphFst): - """ - Finite state transducer for classifying time - e.g. twelve thirty -> time { hours: "12" minutes: "30" } - e.g. twelve past one -> time { minutes: "12" hours: "1" } - e.g. two o clock a m -> time { hours: "2" suffix: "a.m." } - e.g. quarter to two -> time { hours: "1" minutes: "45" } - e.g. quarter past two -> time { hours: "2" minutes: "15" } - e.g. half past two -> time { hours: "2" minutes: "30" } - """ - - def __init__(self): - super().__init__(name="time", kind="classify") - # hours, minutes, seconds, suffix, zone, style, speak_period - - suffix_graph = pynini.string_file(get_abs_path("data/time/time_suffix.tsv")) - time_zone_graph = pynini.invert(pynini.string_file(get_abs_path("data/time/time_zone.tsv"))) - to_hour_graph = pynini.string_file(get_abs_path("data/time/to_hour.tsv")) - minute_to_graph = pynini.string_file(get_abs_path("data/time/minute_to.tsv")) - - # only used for < 1000 thousand -> 0 weight - cardinal = pynutil.add_weight(CardinalFst().graph_no_exception, weight=-0.7) - - labels_hour = [num_to_word(x) for x in range(0, 24)] - labels_minute_single = [num_to_word(x) for x in range(1, 10)] - labels_minute_double = [num_to_word(x) for x in range(10, 60)] - - graph_hour = pynini.union(*labels_hour) @ cardinal - - graph_minute_single = pynini.union(*labels_minute_single) @ cardinal - graph_minute_double = pynini.union(*labels_minute_double) @ cardinal - graph_minute_verbose = pynini.cross("half", "30") | pynini.cross("quarter", "15") - oclock = pynini.cross(pynini.union("o' clock", "o clock", "o'clock", "oclock"), "") - - final_graph_hour = pynutil.insert("hours: \"") + graph_hour + pynutil.insert("\"") - graph_minute = ( - oclock + pynutil.insert("00") - | pynutil.delete("o") + delete_space + graph_minute_single - | graph_minute_double - ) - final_suffix = pynutil.insert("suffix: \"") + convert_space(suffix_graph) + pynutil.insert("\"") - final_suffix = delete_space + insert_space + final_suffix - final_suffix_optional = pynini.closure(final_suffix, 0, 1) - final_time_zone_optional = pynini.closure( - delete_space - + insert_space - + pynutil.insert("zone: \"") - + convert_space(time_zone_graph) - + pynutil.insert("\""), - 0, - 1, - ) - - # five o' clock - # two o eight, two thirty five (am/pm) - # two pm/am - graph_hm = ( - final_graph_hour + delete_extra_space + pynutil.insert("minutes: \"") + graph_minute + pynutil.insert("\"") - ) - # 10 past four, quarter past four, half past four - graph_m_past_h = ( - pynutil.insert("minutes: \"") - + pynini.union(graph_minute_single, graph_minute_double, graph_minute_verbose) - + pynutil.insert("\"") - + delete_space - + pynutil.delete("past") - + delete_extra_space - + final_graph_hour - ) - - graph_quarter_time = ( - pynutil.insert("minutes: \"") - + pynini.cross("quarter", "45") - + pynutil.insert("\"") - + delete_space - + pynutil.delete(pynini.union("to", "till")) - + delete_extra_space - + pynutil.insert("hours: \"") - + to_hour_graph - + pynutil.insert("\"") - ) - - graph_m_to_h_suffix_time = ( - pynutil.insert("minutes: \"") - + ((graph_minute_single | graph_minute_double).optimize() @ minute_to_graph) - + pynutil.insert("\"") - + pynini.closure(delete_space + pynutil.delete(pynini.union("min", "mins", "minute", "minutes")), 0, 1) - + delete_space - + pynutil.delete(pynini.union("to", "till")) - + delete_extra_space - + pynutil.insert("hours: \"") - + to_hour_graph - + pynutil.insert("\"") - + final_suffix - ) - - graph_h = ( - final_graph_hour - + delete_extra_space - + pynutil.insert("minutes: \"") - + (pynutil.insert("00") | graph_minute) - + pynutil.insert("\"") - + final_suffix - + final_time_zone_optional - ) - final_graph = ( - (graph_hm | graph_m_past_h | graph_quarter_time) + final_suffix_optional + final_time_zone_optional - ) - final_graph |= graph_h - final_graph |= graph_m_to_h_suffix_time - - final_graph = self.add_tokens(final_graph.optimize()) - - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/en/taggers/tokenize_and_classify.py b/nemo_text_processing/inverse_text_normalization/en/taggers/tokenize_and_classify.py deleted file mode 100644 index 9130ffc9ba08..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/taggers/tokenize_and_classify.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -import pynini -from nemo_text_processing.inverse_text_normalization.en.taggers.cardinal import CardinalFst -from nemo_text_processing.inverse_text_normalization.en.taggers.date import DateFst -from nemo_text_processing.inverse_text_normalization.en.taggers.decimal import DecimalFst -from nemo_text_processing.inverse_text_normalization.en.taggers.electronic import ElectronicFst -from nemo_text_processing.inverse_text_normalization.en.taggers.measure import MeasureFst -from nemo_text_processing.inverse_text_normalization.en.taggers.money import MoneyFst -from nemo_text_processing.inverse_text_normalization.en.taggers.ordinal import OrdinalFst -from nemo_text_processing.inverse_text_normalization.en.taggers.punctuation import PunctuationFst -from nemo_text_processing.inverse_text_normalization.en.taggers.telephone import TelephoneFst -from nemo_text_processing.inverse_text_normalization.en.taggers.time import TimeFst -from nemo_text_processing.inverse_text_normalization.en.taggers.whitelist import WhiteListFst -from nemo_text_processing.inverse_text_normalization.en.taggers.word import WordFst -from nemo_text_processing.text_normalization.en.graph_utils import ( - GraphFst, - delete_extra_space, - delete_space, - generator_main, -) -from pynini.lib import pynutil - -from nemo.utils import logging - - -class ClassifyFst(GraphFst): - """ - Final class that composes all other classification grammars. This class can process an entire sentence, that is lower cased. - For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - - Args: - cache_dir: path to a dir with .far grammar file. Set to None to avoid using cache. - overwrite_cache: set to True to overwrite .far files - """ - - def __init__(self, cache_dir: str = None, overwrite_cache: bool = False): - super().__init__(name="tokenize_and_classify", kind="classify") - - far_file = None - if cache_dir is not None and cache_dir != "None": - os.makedirs(cache_dir, exist_ok=True) - far_file = os.path.join(cache_dir, "_en_itn.far") - if not overwrite_cache and far_file and os.path.exists(far_file): - self.fst = pynini.Far(far_file, mode="r")["tokenize_and_classify"] - logging.info(f"ClassifyFst.fst was restored from {far_file}.") - else: - logging.info(f"Creating ClassifyFst grammars.") - cardinal = CardinalFst() - cardinal_graph = cardinal.fst - - ordinal = OrdinalFst(cardinal) - ordinal_graph = ordinal.fst - - decimal = DecimalFst(cardinal) - decimal_graph = decimal.fst - - measure_graph = MeasureFst(cardinal=cardinal, decimal=decimal).fst - date_graph = DateFst(ordinal=ordinal).fst - word_graph = WordFst().fst - time_graph = TimeFst().fst - money_graph = MoneyFst(cardinal=cardinal, decimal=decimal).fst - whitelist_graph = WhiteListFst().fst - punct_graph = PunctuationFst().fst - electronic_graph = ElectronicFst().fst - telephone_graph = TelephoneFst(cardinal).fst - - classify = ( - pynutil.add_weight(whitelist_graph, 1.01) - | pynutil.add_weight(time_graph, 1.1) - | pynutil.add_weight(date_graph, 1.09) - | pynutil.add_weight(decimal_graph, 1.1) - | pynutil.add_weight(measure_graph, 1.1) - | pynutil.add_weight(cardinal_graph, 1.1) - | pynutil.add_weight(ordinal_graph, 1.1) - | pynutil.add_weight(money_graph, 1.1) - | pynutil.add_weight(telephone_graph, 1.1) - | pynutil.add_weight(electronic_graph, 1.1) - | pynutil.add_weight(word_graph, 100) - ) - - punct = pynutil.insert("tokens { ") + pynutil.add_weight(punct_graph, weight=1.1) + pynutil.insert(" }") - token = pynutil.insert("tokens { ") + classify + pynutil.insert(" }") - token_plus_punct = ( - pynini.closure(punct + pynutil.insert(" ")) + token + pynini.closure(pynutil.insert(" ") + punct) - ) - - graph = token_plus_punct + pynini.closure(delete_extra_space + token_plus_punct) - graph = delete_space + graph + delete_space - - self.fst = graph.optimize() - - if far_file: - generator_main(far_file, {"tokenize_and_classify": self.fst}) - logging.info(f"ClassifyFst grammars are saved to {far_file}.") diff --git a/nemo_text_processing/inverse_text_normalization/en/taggers/whitelist.py b/nemo_text_processing/inverse_text_normalization/en/taggers/whitelist.py deleted file mode 100644 index dced4d4faf62..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/taggers/whitelist.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.en.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst, convert_space -from pynini.lib import pynutil - - -class WhiteListFst(GraphFst): - """ - Finite state transducer for classifying whitelisted tokens - e.g. misses -> tokens { name: "mrs." } - This class has highest priority among all classifier grammars. Whitelisted tokens are defined and loaded from "data/whitelist.tsv". - """ - - def __init__(self): - super().__init__(name="whitelist", kind="classify") - - whitelist = pynini.string_file(get_abs_path("data/whitelist.tsv")).invert() - graph = pynutil.insert("name: \"") + convert_space(whitelist) + pynutil.insert("\"") - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/en/taggers/word.py b/nemo_text_processing/inverse_text_normalization/en/taggers/word.py deleted file mode 100644 index 714e68efeb3d..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/taggers/word.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_SPACE, GraphFst -from pynini.lib import pynutil - - -class WordFst(GraphFst): - """ - Finite state transducer for classifying plain tokens, that do not belong to any special class. This can be considered as the default class. - e.g. sleep -> tokens { name: "sleep" } - """ - - def __init__(self): - super().__init__(name="word", kind="classify") - word = pynutil.insert("name: \"") + pynini.closure(NEMO_NOT_SPACE, 1) + pynutil.insert("\"") - self.fst = word.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/en/utils.py b/nemo_text_processing/inverse_text_normalization/en/utils.py deleted file mode 100644 index 6a8bd48cda5e..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/utils.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -from typing import Union - -import inflect - -_inflect = inflect.engine() - - -def num_to_word(x: Union[str, int]): - """ - converts integer to spoken representation - - Args - x: integer - - Returns: spoken representation - """ - if isinstance(x, int): - x = str(x) - x = _inflect.number_to_words(str(x)).replace("-", " ").replace(",", "") - return x - - -def get_abs_path(rel_path): - """ - Get absolute path - - Args: - rel_path: relative path to this file - - Returns absolute path - """ - return os.path.dirname(os.path.abspath(__file__)) + '/' + rel_path diff --git a/nemo_text_processing/inverse_text_normalization/en/verbalizers/__init__.py b/nemo_text_processing/inverse_text_normalization/en/verbalizers/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/verbalizers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/en/verbalizers/cardinal.py b/nemo_text_processing/inverse_text_normalization/en/verbalizers/cardinal.py deleted file mode 100644 index 8d9c3a621b78..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/verbalizers/cardinal.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class CardinalFst(GraphFst): - """ - Finite state transducer for verbalizing cardinal - e.g. cardinal { integer: "23" negative: "-" } -> -23 - """ - - def __init__(self): - super().__init__(name="cardinal", kind="verbalize") - optional_sign = pynini.closure( - pynutil.delete("negative:") - + delete_space - + pynutil.delete("\"") - + NEMO_NOT_QUOTE - + pynutil.delete("\"") - + delete_space, - 0, - 1, - ) - graph = ( - pynutil.delete("integer:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - self.numbers = graph - graph = optional_sign + graph - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/en/verbalizers/date.py b/nemo_text_processing/inverse_text_normalization/en/verbalizers/date.py deleted file mode 100644 index 276223bb5cf4..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/verbalizers/date.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_NOT_QUOTE, - GraphFst, - delete_extra_space, - delete_space, -) -from pynini.lib import pynutil - - -class DateFst(GraphFst): - """ - Finite state transducer for verbalizing date, e.g. - date { month: "january" day: "5" year: "2012" preserve_order: true } -> february 5 2012 - date { day: "5" month: "january" year: "2012" preserve_order: true } -> 5 february 2012 - """ - - def __init__(self): - super().__init__(name="date", kind="verbalize") - month = ( - pynutil.delete("month:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - day = ( - pynutil.delete("day:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - year = ( - pynutil.delete("year:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + delete_space - + pynutil.delete("\"") - ) - - # month (day) year - graph_mdy = ( - month + pynini.closure(delete_extra_space + day, 0, 1) + pynini.closure(delete_extra_space + year, 0, 1) - ) - - # (day) month year - graph_dmy = ( - pynini.closure(day + delete_extra_space, 0, 1) + month + pynini.closure(delete_extra_space + year, 0, 1) - ) - - optional_preserve_order = pynini.closure( - pynutil.delete("preserve_order:") + delete_space + pynutil.delete("true") + delete_space - | pynutil.delete("field_order:") - + delete_space - + pynutil.delete("\"") - + NEMO_NOT_QUOTE - + pynutil.delete("\"") - + delete_space - ) - - final_graph = (graph_mdy | year | graph_dmy) + delete_space + optional_preserve_order - - delete_tokens = self.delete_tokens(final_graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/en/verbalizers/decimal.py b/nemo_text_processing/inverse_text_normalization/en/verbalizers/decimal.py deleted file mode 100644 index 41b3a281afd4..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/verbalizers/decimal.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class DecimalFst(GraphFst): - """ - Finite state transducer for verbalizing decimal, e.g. - decimal { negative: "true" integer_part: "12" fractional_part: "5006" quantity: "billion" } -> -12.5006 billion - """ - - def __init__(self): - super().__init__(name="decimal", kind="verbalize") - optionl_sign = pynini.closure(pynini.cross("negative: \"true\"", "-") + delete_space, 0, 1) - integer = ( - pynutil.delete("integer_part:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - optional_integer = pynini.closure(integer + delete_space, 0, 1) - fractional = ( - pynutil.insert(".") - + pynutil.delete("fractional_part:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - optional_fractional = pynini.closure(fractional + delete_space, 0, 1) - quantity = ( - pynutil.delete("quantity:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - optional_quantity = pynini.closure(pynutil.insert(" ") + quantity + delete_space, 0, 1) - graph = optional_integer + optional_fractional + optional_quantity - self.numbers = graph - graph = optionl_sign + graph - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/en/verbalizers/electronic.py b/nemo_text_processing/inverse_text_normalization/en/verbalizers/electronic.py deleted file mode 100644 index 4c9951c95360..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/verbalizers/electronic.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class ElectronicFst(GraphFst): - """ - Finite state transducer for verbalizing electronic - e.g. tokens { electronic { username: "cdf1" domain: "abc.edu" } } -> cdf1@abc.edu - """ - - def __init__(self): - super().__init__(name="electronic", kind="verbalize") - user_name = ( - pynutil.delete("username:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - domain = ( - pynutil.delete("domain:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - - protocol = ( - pynutil.delete("protocol:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - - graph = user_name + delete_space + pynutil.insert("@") + domain - graph |= protocol - - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/en/verbalizers/fraction.py b/nemo_text_processing/inverse_text_normalization/en/verbalizers/fraction.py deleted file mode 100644 index ca2bdcee2da5..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/verbalizers/fraction.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst - - -class FractionFst(GraphFst): - """ - Finite state transducer for verbalizing fraction, - """ - - def __init__(self): - super().__init__(name="fraction", kind="verbalize") diff --git a/nemo_text_processing/inverse_text_normalization/en/verbalizers/measure.py b/nemo_text_processing/inverse_text_normalization/en/verbalizers/measure.py deleted file mode 100644 index c5a7617610c6..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/verbalizers/measure.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_CHAR, GraphFst, delete_space -from pynini.lib import pynutil - - -class MeasureFst(GraphFst): - """ - Finite state transducer for verbalizing measure, e.g. - measure { negative: "true" cardinal { integer: "12" } units: "kg" } -> -12 kg - - Args: - decimal: DecimalFst - cardinal: CardinalFst - """ - - def __init__(self, decimal: GraphFst, cardinal: GraphFst): - super().__init__(name="measure", kind="verbalize") - optional_sign = pynini.closure(pynini.cross("negative: \"true\"", "-"), 0, 1) - unit = ( - pynutil.delete("units:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_CHAR - " ", 1) - + pynutil.delete("\"") - + delete_space - ) - graph_decimal = ( - pynutil.delete("decimal {") - + delete_space - + optional_sign - + delete_space - + decimal.numbers - + delete_space - + pynutil.delete("}") - ) - graph_cardinal = ( - pynutil.delete("cardinal {") - + delete_space - + optional_sign - + delete_space - + cardinal.numbers - + delete_space - + pynutil.delete("}") - ) - graph = (graph_cardinal | graph_decimal) + delete_space + pynutil.insert(" ") + unit - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/en/verbalizers/money.py b/nemo_text_processing/inverse_text_normalization/en/verbalizers/money.py deleted file mode 100644 index 7218257f7ff7..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/verbalizers/money.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_CHAR, GraphFst, delete_space -from pynini.lib import pynutil - - -class MoneyFst(GraphFst): - """ - Finite state transducer for verbalizing money, e.g. - money { integer_part: "12" fractional_part: "05" currency: "$" } -> $12.05 - - Args: - decimal: DecimalFst - """ - - def __init__(self, decimal: GraphFst): - super().__init__(name="money", kind="verbalize") - unit = ( - pynutil.delete("currency:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_CHAR - " ", 1) - + pynutil.delete("\"") - ) - graph = unit + delete_space + decimal.numbers - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/en/verbalizers/ordinal.py b/nemo_text_processing/inverse_text_normalization/en/verbalizers/ordinal.py deleted file mode 100644 index 378939812fc2..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/verbalizers/ordinal.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, NEMO_SIGMA, GraphFst, delete_space -from pynini.lib import pynutil - - -class OrdinalFst(GraphFst): - """ - Finite state transducer for verbalizing ordinal, e.g. - ordinal { integer: "13" } -> 13th - """ - - def __init__(self): - super().__init__(name="ordinal", kind="verbalize") - graph = ( - pynutil.delete("integer:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - convert_eleven = pynini.cross("11", "11th") - convert_twelve = pynini.cross("12", "12th") - convert_thirteen = pynini.cross("13", "13th") - convert_one = pynini.cross("1", "1st") - convert_two = pynini.cross("2", "2nd") - convert_three = pynini.cross("3", "3rd") - convert_rest = pynutil.insert("th", weight=0.01) - - suffix = pynini.cdrewrite( - convert_eleven - | convert_twelve - | convert_thirteen - | convert_one - | convert_two - | convert_three - | convert_rest, - "", - "[EOS]", - NEMO_SIGMA, - ) - graph = graph @ suffix - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/en/verbalizers/telephone.py b/nemo_text_processing/inverse_text_normalization/en/verbalizers/telephone.py deleted file mode 100644 index 6c37ba468af6..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/verbalizers/telephone.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst -from pynini.lib import pynutil - - -class TelephoneFst(GraphFst): - """ - Finite state transducer for verbalizing telephone, e.g. - telephone { number_part: "123-123-5678" } - -> 123-123-5678 - """ - - def __init__(self): - super().__init__(name="telephone", kind="verbalize") - - number_part = pynutil.delete("number_part: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - optional_country_code = pynini.closure( - pynutil.delete("country_code: \"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - + pynini.accep(" "), - 0, - 1, - ) - delete_tokens = self.delete_tokens(optional_country_code + number_part) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/en/verbalizers/time.py b/nemo_text_processing/inverse_text_normalization/en/verbalizers/time.py deleted file mode 100644 index 5a2c8709345e..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/verbalizers/time.py +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_CHAR, - NEMO_DIGIT, - GraphFst, - delete_space, - insert_space, -) -from pynini.lib import pynutil - - -class TimeFst(GraphFst): - """ - Finite state transducer for verbalizing time, e.g. - time { hours: "12" minutes: "30" } -> 12:30 - time { hours: "1" minutes: "12" } -> 01:12 - time { hours: "2" suffix: "a.m." } -> 02:00 a.m. - """ - - def __init__(self): - super().__init__(name="time", kind="verbalize") - add_leading_zero_to_double_digit = (NEMO_DIGIT + NEMO_DIGIT) | (pynutil.insert("0") + NEMO_DIGIT) - hour = ( - pynutil.delete("hours:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_DIGIT, 1) - + pynutil.delete("\"") - ) - minute = ( - pynutil.delete("minutes:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_DIGIT, 1) - + pynutil.delete("\"") - ) - suffix = ( - delete_space - + insert_space - + pynutil.delete("suffix:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_CHAR - " ", 1) - + pynutil.delete("\"") - ) - optional_suffix = pynini.closure(suffix, 0, 1) - zone = ( - delete_space - + insert_space - + pynutil.delete("zone:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_CHAR - " ", 1) - + pynutil.delete("\"") - ) - optional_zone = pynini.closure(zone, 0, 1) - graph = ( - hour @ add_leading_zero_to_double_digit - + delete_space - + pynutil.insert(":") - + (minute @ add_leading_zero_to_double_digit) - + optional_suffix - + optional_zone - ) - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/en/verbalizers/verbalize.py b/nemo_text_processing/inverse_text_normalization/en/verbalizers/verbalize.py deleted file mode 100644 index 48d052ea7a62..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/verbalizers/verbalize.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from nemo_text_processing.inverse_text_normalization.en.verbalizers.cardinal import CardinalFst -from nemo_text_processing.inverse_text_normalization.en.verbalizers.date import DateFst -from nemo_text_processing.inverse_text_normalization.en.verbalizers.decimal import DecimalFst -from nemo_text_processing.inverse_text_normalization.en.verbalizers.electronic import ElectronicFst -from nemo_text_processing.inverse_text_normalization.en.verbalizers.measure import MeasureFst -from nemo_text_processing.inverse_text_normalization.en.verbalizers.money import MoneyFst -from nemo_text_processing.inverse_text_normalization.en.verbalizers.ordinal import OrdinalFst -from nemo_text_processing.inverse_text_normalization.en.verbalizers.telephone import TelephoneFst -from nemo_text_processing.inverse_text_normalization.en.verbalizers.time import TimeFst -from nemo_text_processing.inverse_text_normalization.en.verbalizers.whitelist import WhiteListFst -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst - - -class VerbalizeFst(GraphFst): - """ - Composes other verbalizer grammars. - For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - """ - - def __init__(self): - super().__init__(name="verbalize", kind="verbalize") - cardinal = CardinalFst() - cardinal_graph = cardinal.fst - ordinal_graph = OrdinalFst().fst - decimal = DecimalFst() - decimal_graph = decimal.fst - measure_graph = MeasureFst(decimal=decimal, cardinal=cardinal).fst - money_graph = MoneyFst(decimal=decimal).fst - time_graph = TimeFst().fst - date_graph = DateFst().fst - whitelist_graph = WhiteListFst().fst - telephone_graph = TelephoneFst().fst - electronic_graph = ElectronicFst().fst - graph = ( - time_graph - | date_graph - | money_graph - | measure_graph - | ordinal_graph - | decimal_graph - | cardinal_graph - | whitelist_graph - | telephone_graph - | electronic_graph - ) - self.fst = graph diff --git a/nemo_text_processing/inverse_text_normalization/en/verbalizers/verbalize_final.py b/nemo_text_processing/inverse_text_normalization/en/verbalizers/verbalize_final.py deleted file mode 100644 index a8ea187009db..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/verbalizers/verbalize_final.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.en.verbalizers.verbalize import VerbalizeFst -from nemo_text_processing.inverse_text_normalization.en.verbalizers.word import WordFst -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst, delete_extra_space, delete_space -from pynini.lib import pynutil - - -class VerbalizeFinalFst(GraphFst): - """ - Finite state transducer that verbalizes an entire sentence, e.g. - tokens { name: "its" } tokens { time { hours: "12" minutes: "30" } } tokens { name: "now" } -> its 12:30 now - """ - - def __init__(self): - super().__init__(name="verbalize_final", kind="verbalize") - verbalize = VerbalizeFst().fst - word = WordFst().fst - types = verbalize | word - graph = ( - pynutil.delete("tokens") - + delete_space - + pynutil.delete("{") - + delete_space - + types - + delete_space - + pynutil.delete("}") - ) - graph = delete_space + pynini.closure(graph + delete_extra_space) + graph + delete_space - self.fst = graph diff --git a/nemo_text_processing/inverse_text_normalization/en/verbalizers/whitelist.py b/nemo_text_processing/inverse_text_normalization/en/verbalizers/whitelist.py deleted file mode 100644 index 67ffe4c76826..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/verbalizers/whitelist.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_CHAR, NEMO_SIGMA, GraphFst, delete_space -from pynini.lib import pynutil - - -class WhiteListFst(GraphFst): - """ - Finite state transducer for verbalizing whitelist - e.g. tokens { name: "mrs." } -> mrs. - """ - - def __init__(self): - super().__init__(name="whitelist", kind="verbalize") - graph = ( - pynutil.delete("name:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_CHAR - " ", 1) - + pynutil.delete("\"") - ) - graph = graph @ pynini.cdrewrite(pynini.cross(u"\u00A0", " "), "", "", NEMO_SIGMA) - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/en/verbalizers/word.py b/nemo_text_processing/inverse_text_normalization/en/verbalizers/word.py deleted file mode 100644 index 6e94ac8c892c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/en/verbalizers/word.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_CHAR, NEMO_SIGMA, GraphFst, delete_space -from pynini.lib import pynutil - - -class WordFst(GraphFst): - """ - Finite state transducer for verbalizing plain tokens - e.g. tokens { name: "sleep" } -> sleep - """ - - def __init__(self): - super().__init__(name="word", kind="verbalize") - chars = pynini.closure(NEMO_CHAR - " ", 1) - char = pynutil.delete("name:") + delete_space + pynutil.delete("\"") + chars + pynutil.delete("\"") - graph = char @ pynini.cdrewrite(pynini.cross(u"\u00A0", " "), "", "", NEMO_SIGMA) - - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/es/__init__.py b/nemo_text_processing/inverse_text_normalization/es/__init__.py deleted file mode 100644 index bbc86ae98762..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from nemo_text_processing.inverse_text_normalization.es.taggers.tokenize_and_classify import ClassifyFst -from nemo_text_processing.inverse_text_normalization.es.verbalizers.verbalize import VerbalizeFst -from nemo_text_processing.inverse_text_normalization.es.verbalizers.verbalize_final import VerbalizeFinalFst diff --git a/nemo_text_processing/inverse_text_normalization/es/data/__init__.py b/nemo_text_processing/inverse_text_normalization/es/data/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/es/data/currency_plural.tsv b/nemo_text_processing/inverse_text_normalization/es/data/currency_plural.tsv deleted file mode 100644 index b222ff77a263..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/currency_plural.tsv +++ /dev/null @@ -1,6 +0,0 @@ -€ euros -US$ dólares estadounidenses -US$ dólares americanos -$ dólares -$ pesos -¥ yenes \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/es/data/currency_singular.tsv b/nemo_text_processing/inverse_text_normalization/es/data/currency_singular.tsv deleted file mode 100644 index f3977eaf936a..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/currency_singular.tsv +++ /dev/null @@ -1,6 +0,0 @@ -€ euro -US$ dólar estadounidense -US$ dólar americano -$ dólar -$ peso -¥ yen \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/es/data/electronic/__init__.py b/nemo_text_processing/inverse_text_normalization/es/data/electronic/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/electronic/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/es/data/electronic/domain.tsv b/nemo_text_processing/inverse_text_normalization/es/data/electronic/domain.tsv deleted file mode 100644 index 8053757321a0..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/electronic/domain.tsv +++ /dev/null @@ -1,25 +0,0 @@ -com -es -uk -fr -net -br -in -ru -de -it -edu -co -ar -bo -cl -co -ec -fk -gf -fy -pe -py -sr -ve -uy \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/es/data/electronic/server_name.tsv b/nemo_text_processing/inverse_text_normalization/es/data/electronic/server_name.tsv deleted file mode 100644 index 711073d5d88d..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/electronic/server_name.tsv +++ /dev/null @@ -1,17 +0,0 @@ -gmail g mail -gmail -nvidia n vidia -nvidia -outlook -hotmail -yahoo -aol -gmx -msn -live -yandex -orange -wanadoo -web -comcast -bbc \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/es/data/electronic/symbols.tsv b/nemo_text_processing/inverse_text_normalization/es/data/electronic/symbols.tsv deleted file mode 100644 index 9835226f9c25..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/electronic/symbols.tsv +++ /dev/null @@ -1,4 +0,0 @@ -. punto -- guion -_ guion bajo -/ barra \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/es/data/measurements_plural.tsv b/nemo_text_processing/inverse_text_normalization/es/data/measurements_plural.tsv deleted file mode 100644 index a27d890228a4..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/measurements_plural.tsv +++ /dev/null @@ -1,19 +0,0 @@ -cm centímetros -g gramos -h horas -kg kilos -kg kilogramos -km kilómetros -km² kilómetros cuadrados -l litros -m metros -m² metros cuadrados -m³ metros cubicos -mph millas por hora -ml mililitros -mm milímetros -ms milisegundos -min minutos -% por ciento -% porciento -s segundos diff --git a/nemo_text_processing/inverse_text_normalization/es/data/measurements_singular.tsv b/nemo_text_processing/inverse_text_normalization/es/data/measurements_singular.tsv deleted file mode 100644 index 7c892b5d06d4..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/measurements_singular.tsv +++ /dev/null @@ -1,19 +0,0 @@ -cm centímetro -g gramo -h hora -kg kilo -kg kilogramo -km kilómetro -km² kilómetro cuadrado -l litro -m metro -m² metro cuadrado -m³ metro cubico -mph milla por hora -ml mililitro -mm milímetro -ms milisegundo -min minuto -% por ciento -% porciento -s segundo diff --git a/nemo_text_processing/inverse_text_normalization/es/data/months.tsv b/nemo_text_processing/inverse_text_normalization/es/data/months.tsv deleted file mode 100644 index 55ada228937a..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/months.tsv +++ /dev/null @@ -1,12 +0,0 @@ -enero -febrero -marzo -abril -mayo -junio -julio -agosto -septiembre -octubre -noviembre -diciembre \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/es/data/numbers/__init__.py b/nemo_text_processing/inverse_text_normalization/es/data/numbers/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/numbers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/es/data/numbers/digit.tsv b/nemo_text_processing/inverse_text_normalization/es/data/numbers/digit.tsv deleted file mode 100644 index 639fe1c73579..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/numbers/digit.tsv +++ /dev/null @@ -1,12 +0,0 @@ -uno 1 -un 1 -ún 1 -una 1 -dos 2 -tres 3 -cuatro 4 -cinco 5 -seis 6 -siete 7 -ocho 8 -nueve 9 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/es/data/numbers/hundreds.tsv b/nemo_text_processing/inverse_text_normalization/es/data/numbers/hundreds.tsv deleted file mode 100644 index 972e687760b3..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/numbers/hundreds.tsv +++ /dev/null @@ -1,18 +0,0 @@ -ciento 1 -cien 1 -doscientos 2 -doscientas 2 -trescientos 3 -trescientas 3 -cuatrocientos 4 -cuatrocientas 4 -quinientos 5 -quinientas 5 -seiscientos 6 -seiscientas 6 -setecientos 7 -setecientas 7 -ochocientos 8 -ochocientas 8 -novecientos 9 -novecientas 9 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/es/data/numbers/teen.tsv b/nemo_text_processing/inverse_text_normalization/es/data/numbers/teen.tsv deleted file mode 100644 index 370526ea57c3..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/numbers/teen.tsv +++ /dev/null @@ -1,10 +0,0 @@ -diez 10 -once 11 -doce 12 -trece 13 -catorce 14 -quince 15 -dieciséis 16 -diecisiete 17 -dieciocho 18 -diecinueve 19 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/es/data/numbers/ties.tsv b/nemo_text_processing/inverse_text_normalization/es/data/numbers/ties.tsv deleted file mode 100644 index 30f0d43f25ad..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/numbers/ties.tsv +++ /dev/null @@ -1,9 +0,0 @@ -veinte 2 -treinta 3 -cuarenta 4 -cincuenta 5 -cinquenta 5 -sesenta 6 -setenta 7 -ochenta 8 -noventa 9 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/es/data/numbers/twenties.tsv b/nemo_text_processing/inverse_text_normalization/es/data/numbers/twenties.tsv deleted file mode 100644 index e586cfa432a0..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/numbers/twenties.tsv +++ /dev/null @@ -1,14 +0,0 @@ -veintiuno 21 -veintiún 21 -veintiuna 21 -veintidós 22 -veintitrés 23 -veintitres 23 -veinticuatro 24 -veinticuátro 24 -veintiacuátro 24 -veinticinco 25 -veintiséis 26 -veintisiete 27 -veintiocho 28 -veintinueve 29 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/es/data/numbers/zero.tsv b/nemo_text_processing/inverse_text_normalization/es/data/numbers/zero.tsv deleted file mode 100644 index 945785283a51..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/numbers/zero.tsv +++ /dev/null @@ -1 +0,0 @@ -cero 0 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/es/data/ordinals/__init__.py b/nemo_text_processing/inverse_text_normalization/es/data/ordinals/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/ordinals/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/es/data/ordinals/digit.tsv b/nemo_text_processing/inverse_text_normalization/es/data/ordinals/digit.tsv deleted file mode 100644 index a478659f09aa..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/ordinals/digit.tsv +++ /dev/null @@ -1,22 +0,0 @@ -primero uno -primera uno -primer uno -segundo dos -segunda dos -tercero tres -tercera tres -tercer tres -cuarto cuatro -cuarta cuatro -quinto cinco -quinta cinco -sexto seis -sexta seis -séptimo siete -séptima siete -sétimo siete -sétima siete -octavo ocho -octava ocho -noveno nueve -novena nueve \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/es/data/ordinals/hundreds.tsv b/nemo_text_processing/inverse_text_normalization/es/data/ordinals/hundreds.tsv deleted file mode 100644 index 488730618f19..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/ordinals/hundreds.tsv +++ /dev/null @@ -1,18 +0,0 @@ -centésimo ciento -centésima ciento -ducentésimo doscientos -ducentésima doscientos -tricentésimo trescientos -tricentésima trescientos -cuadringentésimo cuatrocientos -cuadringentésima cuatrocientos -quingentésimo quinientos -quingentésima quinientos -sexcentésimo seiscientos -sexcentésima seiscientos -septingentésimo setecientos -septingentésima setecientos -octingentésimo ochocientos -octingentésima ochocientos -noningentésimo novecientos -noningentésima novecientos \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/es/data/ordinals/teen.tsv b/nemo_text_processing/inverse_text_normalization/es/data/ordinals/teen.tsv deleted file mode 100644 index 946398825d24..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/ordinals/teen.tsv +++ /dev/null @@ -1,55 +0,0 @@ -décimo diez -décima diez -decimoprimero once -decimoprimera once -decimoprimer once -décimo primero once -décima primera once -décimo primera once -décimo primer once -undécimo once -undécima once -decimosegundo doce -decimosegunda doce -décimo segundo doce -décima segunda doce -décimo segunda doce -duodécimo doce -duodécima doce -decimotercero trece -decimotercera trece -decimotercer trece -décimo tercero trece -décima tercera trece -décimo tercera trece -décimo tercer trece -decimocuarto catorce -decimocuarta catorce -décimo cuarto catorce -décima cuarta catorce -décimo cuarta catorce -decimoquinto quince -decimoquinta quince -décimo quinto quince -décima quinta quince -décimo quinta quince -decimosexto dieciséis -decimosexta dieciséis -décimo sexto dieciséis -décima sexta dieciséis -décimo sexta dieciséis -decimoséptimo diecisiete -decimoséptima diecisiete -décimo séptimo diecisiete -décima séptima diecisiete -décimo séptima diecisiete -decimoctavo dieciocho -decimoctava dieciocho -décimo octavo dieciocho -décima octava dieciocho -décimo octava dieciocho -decimonoveno diecinueve -decimonovena diecinueve -décimo noveno diecinueve -décima novena diecinueve -décimo novena diecinueve \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/es/data/ordinals/ties.tsv b/nemo_text_processing/inverse_text_normalization/es/data/ordinals/ties.tsv deleted file mode 100644 index 5c22d5520e3f..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/ordinals/ties.tsv +++ /dev/null @@ -1,15 +0,0 @@ -vigésimo veinte -vigésima veinte -trigésimo treinta -cuadragésimo cuarenta -cuadragésima cuarenta -quincuagésimo cincuenta -quincuagésima cincuenta -sexagésimo sesenta -sexagésima sesenta -septuagésimo setenta -septuagésima setenta -octogésimo ochenta -octogésima ochenta -nonagésimo noventa -nonagésima noventa \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/es/data/ordinals/twenties.tsv b/nemo_text_processing/inverse_text_normalization/es/data/ordinals/twenties.tsv deleted file mode 100644 index 4c1b1770c823..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/ordinals/twenties.tsv +++ /dev/null @@ -1,20 +0,0 @@ -vigesimoprimero veintiuno -vigesimoprimera veintiuno -vigesimoprimer veintiuno -vigésimosegundo veintidós -vigésimosegunda veintidós -vigésimotercero veintitrés -vigésimotercera veintitrés -vigésimotercer veintitrés -vigésimocuarto veinticuatro -vigésimocuarta veinticuatro -vigésimoquinto veinticinco -vigésimoquinta veinticinco -vigésimosexto veintiséis -vigésimosexta veintiséis -vigésimoséptimo veintisiete -vigésimoséptima veintisiete -vigésimooctavo veintiocho -vigésimooctava veintiocho -vigésimonoveno veintinueve -vigésimonovena veintinueve \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/es/data/time/__init__.py b/nemo_text_processing/inverse_text_normalization/es/data/time/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/time/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/es/data/time/time_suffix.tsv b/nemo_text_processing/inverse_text_normalization/es/data/time/time_suffix.tsv deleted file mode 100644 index 00604d9003ee..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/time/time_suffix.tsv +++ /dev/null @@ -1,12 +0,0 @@ -peme p.m. -pe eme p.m. -p m p.m. -pm p.m. -p.m. -p.m p.m. -ame a.m. -a eme a.m. -am a.m. -a.m. -a.m a.m. -a m a.m. \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/es/data/time/time_to.tsv b/nemo_text_processing/inverse_text_normalization/es/data/time/time_to.tsv deleted file mode 100644 index 71d4bcd0802e..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/time/time_to.tsv +++ /dev/null @@ -1,24 +0,0 @@ -la una las 12 -las dos la 1 -las tres las 2 -las cuatro las 3 -las cinco las 4 -las seis las 5 -las siete las 6 -las ocho las 7 -las nueve las 8 -las diez las 9 -las once las 10 -las doce las 11 -las trece las 12 -las catorce las 13 -las quince las 14 -las dieciséis las 15 -las diecisiete las 16 -las diecieocho las 17 -las diecinueve las 18 -las veinte las 19 -las veintiuna las 20 -las veintidos las 21 -las veintitres las 22 -las cero las 23 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/es/data/whitelist.tsv b/nemo_text_processing/inverse_text_normalization/es/data/whitelist.tsv deleted file mode 100644 index e1816d9768dd..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/data/whitelist.tsv +++ /dev/null @@ -1,2 +0,0 @@ -ud. usted -uds. ustedes diff --git a/nemo_text_processing/inverse_text_normalization/es/taggers/__init__.py b/nemo_text_processing/inverse_text_normalization/es/taggers/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/taggers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/es/taggers/cardinal.py b/nemo_text_processing/inverse_text_normalization/es/taggers/cardinal.py deleted file mode 100644 index 2afce51ef24f..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/taggers/cardinal.py +++ /dev/null @@ -1,188 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.inverse_text_normalization.es.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_ALPHA, - NEMO_DIGIT, - NEMO_SIGMA, - NEMO_SPACE, - GraphFst, - delete_space, -) -from pynini.lib import pynutil - - -class CardinalFst(GraphFst): - """ - Finite state transducer for classifying cardinals - e.g. menos veintitrés -> cardinal { negative: "-" integer: "23"} - This class converts cardinals up to (but not including) "un cuatrillón", - i.e up to "one septillion" in English (10^{24}). - Cardinals below ten are not converted (in order to avoid - "vivo en una casa" --> "vivo en 1 casa" and any other odd conversions.) - - Although technically Spanish grammar requires that "y" only comes after - "10s" numbers (ie. "treinta", ..., "noventa"), these rules will convert - numbers even with "y" in an ungrammatical place (because "y" is ignored - inside cardinal numbers). - e.g. "mil y una" -> cardinal { integer: "1001"} - e.g. "ciento y una" -> cardinal { integer: "101"} - """ - - def __init__(self): - super().__init__(name="cardinal", kind="classify") - graph_zero = pynini.string_file(get_abs_path("data/numbers/zero.tsv")) - graph_digit = pynini.string_file(get_abs_path("data/numbers/digit.tsv")) - graph_ties = pynini.string_file(get_abs_path("data/numbers/ties.tsv")) - graph_teen = pynini.string_file(get_abs_path("data/numbers/teen.tsv")) - graph_twenties = pynini.string_file(get_abs_path("data/numbers/twenties.tsv")) - graph_hundreds = pynini.string_file(get_abs_path("data/numbers/hundreds.tsv")) - - graph_hundred_component = graph_hundreds | pynutil.insert("0") - graph_hundred_component += delete_space - graph_hundred_component += pynini.union( - graph_twenties | graph_teen | pynutil.insert("00"), - (graph_ties | pynutil.insert("0")) + delete_space + (graph_digit | pynutil.insert("0")), - ) - - graph_hundred_component_at_least_one_none_zero_digit = graph_hundred_component @ ( - pynini.closure(NEMO_DIGIT) + (NEMO_DIGIT - "0") + pynini.closure(NEMO_DIGIT) - ) - self.graph_hundred_component_at_least_one_none_zero_digit = ( - graph_hundred_component_at_least_one_none_zero_digit - ) - - graph_thousands = pynini.union( - graph_hundred_component_at_least_one_none_zero_digit + delete_space + pynutil.delete("mil"), - pynutil.insert("001") + pynutil.delete("mil"), # because we say 'mil', not 'un mil' - pynutil.insert("000", weight=0.1), - ) - - graph_millones = pynini.union( - graph_hundred_component_at_least_one_none_zero_digit - + delete_space - + (pynutil.delete("millones") | pynutil.delete("millón")), - pynutil.insert("000") + pynutil.delete("millones"), # to allow for 'mil millones' - ) - - graph_mil_millones = pynini.union( - graph_hundred_component_at_least_one_none_zero_digit + delete_space + pynutil.delete("mil"), - pynutil.insert("001") + pynutil.delete("mil"), # because we say 'mil', not 'un mil' - ) - graph_mil_millones += delete_space + ( - graph_millones | pynutil.insert("000") + pynutil.delete("millones") - ) # allow for 'mil millones' - graph_mil_millones |= pynutil.insert("000000", weight=0.1) - - # also allow 'millardo' instead of 'mil millones' - graph_millardo = ( - graph_hundred_component_at_least_one_none_zero_digit - + delete_space - + (pynutil.delete("millardo") | pynutil.delete("millardos")) - ) - - graph_billones = pynini.union( - graph_hundred_component_at_least_one_none_zero_digit - + delete_space - + (pynutil.delete("billones") | pynutil.delete("billón")), - ) - - graph_mil_billones = pynini.union( - graph_hundred_component_at_least_one_none_zero_digit + delete_space + pynutil.delete("mil"), - pynutil.insert("001") + pynutil.delete("mil"), # because we say 'mil', not 'un mil' - ) - graph_mil_billones += delete_space + ( - graph_billones | pynutil.insert("000") + pynutil.delete("billones") - ) # allow for 'mil billones' - graph_mil_billones |= pynutil.insert("000000", weight=0.1) - - graph_trillones = pynini.union( - graph_hundred_component_at_least_one_none_zero_digit - + delete_space - + (pynutil.delete("trillones") | pynutil.delete("trillón")), - ) - - graph_mil_trillones = pynini.union( - graph_hundred_component_at_least_one_none_zero_digit + delete_space + pynutil.delete("mil"), - pynutil.insert("001") + pynutil.delete("mil"), # because we say 'mil', not 'un mil' - ) - graph_mil_trillones += delete_space + ( - graph_trillones | pynutil.insert("000") + pynutil.delete("trillones") - ) # allow for 'mil trillones' - graph_mil_trillones |= pynutil.insert("000000", weight=0.1) - - graph = pynini.union( - (graph_mil_trillones | pynutil.insert("000", weight=0.1) + graph_trillones) - + delete_space - + (graph_mil_billones | pynutil.insert("000", weight=0.1) + graph_billones) - + delete_space - + pynini.union( - graph_mil_millones, - pynutil.insert("000", weight=0.1) + graph_millones, - graph_millardo + graph_millones, - graph_millardo + pynutil.insert("000", weight=0.1), - ) - + delete_space - + graph_thousands - + delete_space - + graph_hundred_component, - graph_zero, - ) - - graph = graph @ pynini.union( - pynutil.delete(pynini.closure("0")) + pynini.difference(NEMO_DIGIT, "0") + pynini.closure(NEMO_DIGIT), "0" - ) - - # ignore "y" inside cardinal numbers - graph = ( - pynini.cdrewrite(pynutil.delete("y"), NEMO_SPACE, NEMO_SPACE, NEMO_SIGMA) - @ (NEMO_ALPHA + NEMO_SIGMA) - @ graph - ) - - self.graph_no_exception = graph - - # save self.numbers_up_to_thousand for use in DecimalFst - digits_up_to_thousand = NEMO_DIGIT | (NEMO_DIGIT ** 2) | (NEMO_DIGIT ** 3) - numbers_up_to_thousand = pynini.compose(graph, digits_up_to_thousand).optimize() - self.numbers_up_to_thousand = numbers_up_to_thousand - - # save self.numbers_up_to_million for use in DecimalFst - digits_up_to_million = ( - NEMO_DIGIT - | (NEMO_DIGIT ** 2) - | (NEMO_DIGIT ** 3) - | (NEMO_DIGIT ** 4) - | (NEMO_DIGIT ** 5) - | (NEMO_DIGIT ** 6) - ) - numbers_up_to_million = pynini.compose(graph, digits_up_to_million).optimize() - self.numbers_up_to_million = numbers_up_to_million - - # don't convert cardinals from zero to nine inclusive - graph_exception = pynini.project(pynini.union(graph_digit, graph_zero), 'input') - - self.graph = (pynini.project(graph, "input") - graph_exception.arcsort()) @ graph - - optional_minus_graph = pynini.closure( - pynutil.insert("negative: ") + pynini.cross("menos", "\"-\"") + NEMO_SPACE, 0, 1 - ) - - final_graph = optional_minus_graph + pynutil.insert("integer: \"") + self.graph + pynutil.insert("\"") - - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/es/taggers/date.py b/nemo_text_processing/inverse_text_normalization/es/taggers/date.py deleted file mode 100644 index 4eda33129787..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/taggers/date.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.es.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst, delete_extra_space, delete_space -from pynini.lib import pynutil - - -class DateFst(GraphFst): - """ - Finite state transducer for classifying date, - e.g. primero de enero -> date { day: "1" month: "enero" } - e.g. uno de enero -> date { day: "1" month: "enero" } - """ - - def __init__(self): - super().__init__(name="date", kind="classify") - - graph_digit = pynini.string_file(get_abs_path("data/numbers/digit.tsv")) - graph_ties = pynini.string_file(get_abs_path("data/numbers/ties.tsv")) - graph_teen = pynini.string_file(get_abs_path("data/numbers/teen.tsv")) - graph_twenties = pynini.string_file(get_abs_path("data/numbers/twenties.tsv")) - - graph_1_to_100 = pynini.union( - graph_digit, - graph_twenties, - graph_teen, - (graph_ties + pynutil.insert("0")), - (graph_ties + pynutil.delete(" y ") + graph_digit), - ) - - digits_1_to_31 = [str(digits) for digits in range(1, 32)] - graph_1_to_31 = graph_1_to_100 @ pynini.union(*digits_1_to_31) - # can use "primero" for 1st day of the month - graph_1_to_31 = pynini.union(graph_1_to_31, pynini.cross("primero", "1")) - - day_graph = pynutil.insert("day: \"") + graph_1_to_31 + pynutil.insert("\"") - - month_graph = pynini.string_file(get_abs_path("data/months.tsv")) - month_graph = pynutil.insert("month: \"") + month_graph + pynutil.insert("\"") - - graph_dm = day_graph + delete_space + pynutil.delete("de") + delete_extra_space + month_graph - - final_graph = graph_dm - final_graph += pynutil.insert(" preserve_order: true") - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/es/taggers/decimal.py b/nemo_text_processing/inverse_text_normalization/es/taggers/decimal.py deleted file mode 100644 index bdbf180495de..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/taggers/decimal.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.es.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_DIGIT, - GraphFst, - delete_extra_space, - delete_space, -) -from pynini.lib import pynutil - - -def get_quantity(decimal: 'pynini.FstLike', cardinal_up_to_million: 'pynini.FstLike') -> 'pynini.FstLike': - """ - Returns FST that transforms either a cardinal or decimal followed by a quantity into a numeral, - e.g. one million -> integer_part: "1" quantity: "million" - e.g. one point five million -> integer_part: "1" fractional_part: "5" quantity: "million" - - Args: - decimal: decimal FST - cardinal_up_to_million: cardinal FST - """ - numbers = cardinal_up_to_million @ ( - pynutil.delete(pynini.closure("0")) + pynini.difference(NEMO_DIGIT, "0") + pynini.closure(NEMO_DIGIT) - ) - - suffix = pynini.union( - "millón", - "millones", - "millardo", - "millardos", - "billón", - "billones", - "trillón", - "trillones", - "cuatrillón", - "cuatrillones", - ) - res = ( - pynutil.insert("integer_part: \"") - + numbers - + pynutil.insert("\"") - + delete_extra_space - + pynutil.insert("quantity: \"") - + suffix - + pynutil.insert("\"") - ) - res |= decimal + delete_extra_space + pynutil.insert("quantity: \"") + suffix + pynutil.insert("\"") - return res - - -class DecimalFst(GraphFst): - """ - Finite state transducer for classifying decimal - Decimal point is either "." or ",", determined by whether "punto" or "coma" is spoken. - e.g. menos uno coma dos seis -> decimal { negative: "true" integer_part: "1" morphosyntactic_features: "," fractional_part: "26" } - e.g. menos uno punto dos seis -> decimal { negative: "true" integer_part: "1" morphosyntactic_features: "." fractional_part: "26" } - - This decimal rule assumes that decimals can be pronounced as: - (a cardinal) + ('coma' or 'punto') plus (any sequence of cardinals <1000, including 'zero') - - Also writes large numbers in shortened form, e.g. - e.g. uno coma dos seis millón -> decimal { negative: "false" integer_part: "1" morphosyntactic_features: "," fractional_part: "26" quantity: "millón" } - e.g. dos millones -> decimal { negative: "false" integer_part: "2" quantity: "millones" } - e.g. mil ochocientos veinticuatro millones -> decimal { negative: "false" integer_part: "1824" quantity: "millones" } - Args: - cardinal: CardinalFst - - """ - - def __init__(self, cardinal: GraphFst): - super().__init__(name="decimal", kind="classify") - - # number after decimal point can be any series of cardinals <1000, including 'zero' - graph_decimal = cardinal.numbers_up_to_thousand - graph_decimal = pynini.closure(graph_decimal + delete_space) + graph_decimal - self.graph = graph_decimal - - # decimal point can be denoted by 'coma' or 'punto' - decimal_point = pynini.cross("coma", "morphosyntactic_features: \",\"") - decimal_point |= pynini.cross("punto", "morphosyntactic_features: \".\"") - - optional_graph_negative = pynini.closure( - pynutil.insert("negative: ") + pynini.cross("menos", "\"true\"") + delete_extra_space, 0, 1 - ) - - graph_fractional = pynutil.insert("fractional_part: \"") + graph_decimal + pynutil.insert("\"") - - cardinal_graph = cardinal.graph_no_exception | pynini.string_file(get_abs_path("data/numbers/zero.tsv")) - graph_integer = pynutil.insert("integer_part: \"") + cardinal_graph + pynutil.insert("\"") - final_graph_wo_sign = ( - pynini.closure(graph_integer + delete_extra_space, 0, 1) - + decimal_point - + delete_extra_space - + graph_fractional - ) - final_graph = optional_graph_negative + final_graph_wo_sign - - self.final_graph_wo_negative = final_graph_wo_sign | get_quantity( - final_graph_wo_sign, cardinal.numbers_up_to_million - ) - final_graph |= optional_graph_negative + get_quantity(final_graph_wo_sign, cardinal.numbers_up_to_million) - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/es/taggers/electronic.py b/nemo_text_processing/inverse_text_normalization/es/taggers/electronic.py deleted file mode 100644 index 53b6b4d090cf..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/taggers/electronic.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.es.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_ALPHA, GraphFst, insert_space -from pynini.lib import pynutil - - -class ElectronicFst(GraphFst): - """ - Finite state transducer for classifying 'electronic' semiotic classes, i.e. - email address (which get converted to "username" and "domain" fields), - and URLS (which get converted to a "protocol" field). - e.g. c d f uno arroba a b c punto e d u -> tokens { electronic { username: "cdf1" domain: "abc.edu" } } - e.g. doble ve doble ve doble ve a b c punto e d u -> tokens { electronic { protocol: "www.abc.edu" } } - """ - - def __init__(self): - super().__init__(name="electronic", kind="classify") - - delete_extra_space = pynutil.delete(" ") - alpha_num = ( - NEMO_ALPHA - | pynini.string_file(get_abs_path("data/numbers/digit.tsv")) - | pynini.string_file(get_abs_path("data/numbers/zero.tsv")) - ) - - symbols = pynini.string_file(get_abs_path("data/electronic/symbols.tsv")).invert() - - accepted_username = alpha_num | symbols - process_dot = pynini.cross("punto", ".") - username = ( - pynutil.insert("username: \"") - + alpha_num - + delete_extra_space - + pynini.closure(accepted_username + delete_extra_space) - + alpha_num - + pynutil.insert("\"") - ) - single_alphanum = pynini.closure(alpha_num + delete_extra_space) + alpha_num - server = single_alphanum | pynini.string_file(get_abs_path("data/electronic/server_name.tsv")).invert() - domain = single_alphanum | pynini.string_file(get_abs_path("data/electronic/domain.tsv")).invert() - domain_graph = ( - pynutil.insert("domain: \"") - + server - + delete_extra_space - + process_dot - + delete_extra_space - + domain - + pynutil.insert("\"") - ) - graph = ( - username + delete_extra_space + pynutil.delete("arroba") + insert_space + delete_extra_space + domain_graph - ) - - ############# url ### - protocol_end = pynini.cross(pynini.union("www", "w w w", "doble ve doble ve doble ve"), "www") - protocol_start = pynini.cross(pynini.union("http", "h t t p", "hache te te pe"), "http") - protocol_start |= pynini.cross(pynini.union("https", "h t t p s", "hache te te pe ese"), "https") - protocol_start += pynini.cross(" dos puntos barra barra ", "://") - - # e.g. .com, .es - ending = ( - delete_extra_space - + symbols - + delete_extra_space - + (domain | pynini.closure(accepted_username + delete_extra_space,) + accepted_username) - ) - - protocol = ( - pynini.closure(protocol_start, 0, 1) - + protocol_end - + delete_extra_space - + process_dot - + delete_extra_space - + (pynini.closure(delete_extra_space + accepted_username, 1) | server) - + pynini.closure(ending, 1) - ) - protocol = pynutil.insert("protocol: \"") + protocol + pynutil.insert("\"") - graph |= protocol - ######## - - final_graph = self.add_tokens(graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/es/taggers/measure.py b/nemo_text_processing/inverse_text_normalization/es/taggers/measure.py deleted file mode 100644 index a660da140040..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/taggers/measure.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.es.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_SIGMA, - GraphFst, - convert_space, - delete_extra_space, - delete_space, -) -from pynini.lib import pynutil - - -class MeasureFst(GraphFst): - """ - Finite state transducer for classifying measure - e.g. menos doce kilogramos -> measure { cardinal { negative: "true" integer: "12" } units: "kg" } - - Args: - cardinal: CardinalFst - decimal: DecimalFst - """ - - def __init__(self, cardinal: GraphFst, decimal: GraphFst): - super().__init__(name="measure", kind="classify") - - cardinal_graph = cardinal.graph_no_exception - - graph_unit_singular = pynini.string_file(get_abs_path("data/measurements_singular.tsv")) - graph_unit_singular = pynini.invert(graph_unit_singular) # singular -> abbr - graph_unit_plural = pynini.string_file(get_abs_path("data/measurements_plural.tsv")) - graph_unit_plural = pynini.invert(graph_unit_plural) # plural -> abbr - - optional_graph_negative = pynini.closure( - pynutil.insert("negative: ") + pynini.cross("menos", "\"true\"") + delete_extra_space, 0, 1 - ) - - unit_singular = convert_space(graph_unit_singular) - unit_plural = convert_space(graph_unit_plural) - unit_misc = pynutil.insert("/") + pynutil.delete("por") + delete_space + convert_space(graph_unit_singular) - - unit_singular = ( - pynutil.insert("units: \"") - + (unit_singular | unit_misc | pynutil.add_weight(unit_singular + delete_space + unit_misc, 0.01)) - + pynutil.insert("\"") - ) - unit_plural = ( - pynutil.insert("units: \"") - + (unit_plural | unit_misc | pynutil.add_weight(unit_plural + delete_space + unit_misc, 0.01)) - + pynutil.insert("\"") - ) - - subgraph_decimal = ( - pynutil.insert("decimal { ") - + optional_graph_negative - + decimal.final_graph_wo_negative - + pynutil.insert(" }") - + delete_extra_space - + unit_plural - ) - subgraph_cardinal = ( - pynutil.insert("cardinal { ") - + optional_graph_negative - + pynutil.insert("integer: \"") - + ((NEMO_SIGMA - "un" - "una" - "uno") @ cardinal_graph) - + pynutil.insert("\"") - + pynutil.insert(" }") - + delete_extra_space - + unit_plural - ) - subgraph_cardinal |= ( - pynutil.insert("cardinal { ") - + optional_graph_negative - + pynutil.insert("integer: \"") - + (pynini.cross("un", "1") | pynini.cross("una", "1") | pynini.cross("uno", "1")) - + pynutil.insert("\"") - + pynutil.insert(" }") - + delete_extra_space - + unit_singular - ) - final_graph = subgraph_decimal | subgraph_cardinal - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/es/taggers/money.py b/nemo_text_processing/inverse_text_normalization/es/taggers/money.py deleted file mode 100644 index 754afdd36b65..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/taggers/money.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.es.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_DIGIT, - NEMO_SIGMA, - GraphFst, - convert_space, - delete_extra_space, - delete_space, - insert_space, -) -from pynini.lib import pynutil - - -class MoneyFst(GraphFst): - """ - Finite state transducer for classifying money - e.g. doce dólares y cinco céntimos -> money { integer_part: "12" fractional_part: 05 currency: "$" } - - Args: - cardinal: CardinalFst - decimal: DecimalFst - """ - - def __init__(self, cardinal: GraphFst, decimal: GraphFst): - super().__init__(name="money", kind="classify") - # quantity, integer_part, fractional_part, currency - - cardinal_graph = cardinal.graph_no_exception - graph_decimal_final = decimal.final_graph_wo_negative - - unit_singular = pynini.string_file(get_abs_path("data/currency_singular.tsv")) - unit_singular = pynini.invert(unit_singular) - unit_plural = pynini.string_file(get_abs_path("data/currency_plural.tsv")) - unit_plural = pynini.invert(unit_plural) - - graph_unit_singular = pynutil.insert("currency: \"") + convert_space(unit_singular) + pynutil.insert("\"") - graph_unit_plural = pynutil.insert("currency: \"") + convert_space(unit_plural) + pynutil.insert("\"") - - add_leading_zero_to_double_digit = (NEMO_DIGIT + NEMO_DIGIT) | (pynutil.insert("0") + NEMO_DIGIT) - # twelve dollars (and) fifty cents, zero cents - cents_standalone = ( - pynutil.insert("morphosyntactic_features: \",\"") # always use a comma in the decimal - + insert_space - + pynutil.insert("fractional_part: \"") - + pynini.union( - pynutil.add_weight(((NEMO_SIGMA - "un") @ cardinal_graph), -0.7) @ add_leading_zero_to_double_digit - + delete_space - + pynutil.delete(pynini.union("centavos", "céntimos")), - pynini.cross("un", "01") + delete_space + pynutil.delete(pynini.union("centavo", "céntimo")), - ) - + pynutil.insert("\"") - ) - - optional_cents_standalone = pynini.closure( - delete_space - + pynini.closure((pynutil.delete("con") | pynutil.delete('y')) + delete_space, 0, 1) - + insert_space - + cents_standalone, - 0, - 1, - ) - # twelve dollars fifty, only after integer - # setenta y cinco dólares con sesenta y tres~$75,63 - optional_cents_suffix = pynini.closure( - delete_extra_space - + pynutil.insert("morphosyntactic_features: \",\"") # always use a comma in the decimal - + insert_space - + pynutil.insert("fractional_part: \"") - + pynini.closure((pynutil.delete("con") | pynutil.delete('y')) + delete_space, 0, 1) - + pynutil.add_weight(cardinal_graph @ add_leading_zero_to_double_digit, -0.7) - + pynutil.insert("\""), - 0, - 1, - ) - - graph_integer = ( - pynutil.insert("integer_part: \"") - + ((NEMO_SIGMA - "un" - "una") @ cardinal_graph) - + pynutil.insert("\"") - + delete_extra_space - + graph_unit_plural - + (optional_cents_standalone | optional_cents_suffix) - ) - graph_integer |= ( - pynutil.insert("integer_part: \"") - + (pynini.cross("un", "1") | pynini.cross("una", "1")) - + pynutil.insert("\"") - + delete_extra_space - + graph_unit_singular - + (optional_cents_standalone | optional_cents_suffix) - ) - graph_decimal = graph_decimal_final + delete_extra_space + graph_unit_plural - graph_decimal |= pynutil.insert("currency: \"$\" integer_part: \"0\" ") + cents_standalone - final_graph = graph_integer | graph_decimal - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/es/taggers/ordinal.py b/nemo_text_processing/inverse_text_normalization/es/taggers/ordinal.py deleted file mode 100644 index fdb04a504bfa..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/taggers/ordinal.py +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.es.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_SIGMA, GraphFst, delete_space -from pynini.lib import pynutil - - -class OrdinalFst(GraphFst): - """ - Finite state transducer for classifying ordinal - vigésimo primero -> ordinal { integer: "21" morphosyntactic_features: "o" } - This class converts ordinal up to "millesímo" (one thousandth) exclusive. - - Cardinals below ten are not converted (in order to avoid - e.g. "primero hice ..." -> "1.º hice...", "segunda guerra mundial" -> "2.ª guerra mundial" - and any other odd conversions.) - - This FST also records the ending of the ordinal (called "morphosyntactic_features"): - either "o", "a", or "er". - - Args: - cardinal: CardinalFst - """ - - def __init__(self, cardinal: GraphFst): - super().__init__(name="ordinal", kind="classify") - - cardinal_graph = cardinal.graph_no_exception - graph_digit = pynini.string_file(get_abs_path("data/ordinals/digit.tsv")) - graph_teens = pynini.string_file(get_abs_path("data/ordinals/teen.tsv")) - graph_twenties = pynini.string_file(get_abs_path("data/ordinals/twenties.tsv")) - graph_ties = pynini.string_file(get_abs_path("data/ordinals/ties.tsv")) - graph_hundreds = pynini.string_file(get_abs_path("data/ordinals/hundreds.tsv")) - - ordinal_graph_union = pynini.union(graph_digit, graph_teens, graph_twenties, graph_ties, graph_hundreds,) - - accept_o_endings = NEMO_SIGMA + pynini.accep("o") - accept_a_endings = NEMO_SIGMA + pynini.accep("a") - accept_er_endings = NEMO_SIGMA.closure() + pynini.accep("er") - - ordinal_graph_o = accept_o_endings @ ordinal_graph_union - ordinal_graph_a = accept_a_endings @ ordinal_graph_union - ordinal_graph_er = accept_er_endings @ ordinal_graph_union - - # 'optional_numbers_in_front' have negative weight so we always - # include them if they're there - optional_numbers_in_front = (pynutil.add_weight(ordinal_graph_union, -0.1) + delete_space.closure()).closure() - graph_o_suffix = (optional_numbers_in_front + ordinal_graph_o) @ cardinal_graph - graph_a_suffix = (optional_numbers_in_front + ordinal_graph_a) @ cardinal_graph - graph_er_suffix = (optional_numbers_in_front + ordinal_graph_er) @ cardinal_graph - - # don't convert ordinals from one to nine inclusive - graph_exception = pynini.project(pynini.union(graph_digit), 'input') - graph_o_suffix = (pynini.project(graph_o_suffix, "input") - graph_exception.arcsort()) @ graph_o_suffix - graph_a_suffix = (pynini.project(graph_a_suffix, "input") - graph_exception.arcsort()) @ graph_a_suffix - graph_er_suffix = (pynini.project(graph_er_suffix, "input") - graph_exception.arcsort()) @ graph_er_suffix - - graph = ( - pynutil.insert("integer: \"") - + graph_o_suffix - + pynutil.insert("\"") - + pynutil.insert(" morphosyntactic_features: \"o\"") - ) - graph |= ( - pynutil.insert("integer: \"") - + graph_a_suffix - + pynutil.insert("\"") - + pynutil.insert(" morphosyntactic_features: \"a\"") - ) - graph |= ( - pynutil.insert("integer: \"") - + graph_er_suffix - + pynutil.insert("\"") - + pynutil.insert(" morphosyntactic_features: \"er\"") - ) - - final_graph = self.add_tokens(graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/es/taggers/punctuation.py b/nemo_text_processing/inverse_text_normalization/es/taggers/punctuation.py deleted file mode 100644 index 12405d5c5cb2..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/taggers/punctuation.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst -from pynini.lib import pynutil - - -class PunctuationFst(GraphFst): - """ - Finite state transducer for classifying punctuation - e.g. a, -> tokens { name: "a" } tokens { name: "," } - """ - - def __init__(self): - super().__init__(name="punctuation", kind="classify") - - s = "!#$%&\'()*+,-./:;<=>?@^_`{|}~" - punct = pynini.union(*s) - - graph = pynutil.insert("name: \"") + punct + pynutil.insert("\"") - - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/es/taggers/telephone.py b/nemo_text_processing/inverse_text_normalization/es/taggers/telephone.py deleted file mode 100644 index 0e3a47c903b9..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/taggers/telephone.py +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.es.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst, delete_space, insert_space -from pynini.lib import pynutil - - -class TelephoneFst(GraphFst): - """ - Finite state transducer for classifying telephone numbers, e.g. - uno dos tres uno dos tres cinco seis siete ocho -> { number_part: "123-123-5678" }. - If 10 digits are spoken, they are grouped as 3+3+4 (eg. 123-456-7890). - If 9 digits are spoken, they are grouped as 3+3+3 (eg. 123-456-789). - If 8 digits are spoken, they are grouped as 4+4 (eg. 1234-5678). - In Spanish, digits are generally spoken individually, or as 2-digit numbers, - eg. "one twenty three" = "123", - "twelve thirty four" = "1234". - - (we ignore more complicated cases such as "three hundred and two" or "three nines"). - """ - - def __init__(self): - super().__init__(name="telephone", kind="classify") - - # create `single_digits` and `double_digits` graphs as these will be - # the building blocks of possible telephone numbers - graph_digit = pynini.string_file(get_abs_path("data/numbers/digit.tsv")) - graph_ties = pynini.string_file(get_abs_path("data/numbers/ties.tsv")) - graph_teen = pynini.string_file(get_abs_path("data/numbers/teen.tsv")) - graph_twenties = pynini.string_file(get_abs_path("data/numbers/twenties.tsv")) - - single_digits = pynini.invert(graph_digit).optimize() | pynini.cross("0", "cero") - - double_digits = pynini.union( - graph_twenties, - graph_teen, - (graph_ties + pynutil.insert("0")), - (graph_ties + delete_space + pynutil.delete("y") + delete_space + graph_digit), - ).invert() - - # define `ten_digit_graph`, `nine_digit_graph`, `eight_digit_graph` - # which accept telephone numbers spoken (1) only with single digits, - # or (2) spoken with double digits (and sometimes single digits) - - # 10-digit option (1): all single digits - ten_digit_graph = ( - pynini.closure(single_digits + insert_space, 3, 3) - + pynutil.delete("-") - + pynini.closure(single_digits + insert_space, 3, 3) - + pynutil.delete("-") - + pynini.closure(single_digits + insert_space, 3, 3) - + single_digits - ) - - # 10-digit option (2): (1+2) + (1+2) + (2+2) digits - ten_digit_graph |= ( - single_digits - + insert_space - + double_digits - + insert_space - + pynutil.delete("-") - + single_digits - + insert_space - + double_digits - + insert_space - + pynutil.delete("-") - + double_digits - + insert_space - + double_digits - ) - - # 9-digit option (1): all single digits - nine_digit_graph = ( - pynini.closure(single_digits + insert_space, 3, 3) - + pynutil.delete("-") - + pynini.closure(single_digits + insert_space, 3, 3) - + pynutil.delete("-") - + pynini.closure(single_digits + insert_space, 2, 2) - + single_digits - ) - - # 9-digit option (2): (1+2) + (1+2) + (1+2) digits - nine_digit_graph |= ( - single_digits - + insert_space - + double_digits - + insert_space - + pynutil.delete("-") - + single_digits - + insert_space - + double_digits - + insert_space - + pynutil.delete("-") - + single_digits - + insert_space - + double_digits - ) - - # 8-digit option (1): all single digits - eight_digit_graph = ( - pynini.closure(single_digits + insert_space, 4, 4) - + pynutil.delete("-") - + pynini.closure(single_digits + insert_space, 3, 3) - + single_digits - ) - - # 8-digit option (2): (2+2) + (2+2) digits - eight_digit_graph |= ( - double_digits - + insert_space - + double_digits - + insert_space - + pynutil.delete("-") - + double_digits - + insert_space - + double_digits - ) - - number_part = pynini.union(ten_digit_graph, nine_digit_graph, eight_digit_graph,) - - number_part = pynutil.insert("number_part: \"") + pynini.invert(number_part) + pynutil.insert("\"") - - graph = number_part - final_graph = self.add_tokens(graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/es/taggers/time.py b/nemo_text_processing/inverse_text_normalization/es/taggers/time.py deleted file mode 100644 index 2eb8b4f681bf..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/taggers/time.py +++ /dev/null @@ -1,143 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.inverse_text_normalization.es.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import ( - GraphFst, - convert_space, - delete_extra_space, - delete_space, - insert_space, -) -from pynini.lib import pynutil - - -class TimeFst(GraphFst): - """ - Finite state transducer for classifying time - Time formats that it converts: - - + - e.g. la una diez -> time { hours: "la 1" minutes: "10" } - - + " y " + - e.g. la una y diez -> time { hours: "la 1" minutes: "10" } - - + " con " + - e.g. la una con diez -> time { hours: "la 1" minutes: "10" } - - + " menos " + - e.g. las dos menos cuarto -> time { hours: "la 1" minutes: "45" } - - "(un) cuarto para " + - e.g. cuarto para las dos -> time { minutes: "45" hours: "la 1" } - - Note that times on the hour (e.g. "las dos" i.e. "two o'clock") do not get - converted into a time format. This is to avoid converting phrases that are - not part of a time phrase (e.g. "las dos personas" i.e. "the two people") - e.g. las dos -> tokens { name: "las" } tokens { name: "dos" } - However, if a time on the hour is followed by a suffix (indicating 'a.m.' - or 'p.m.'), it will be converted. - e.g. las dos pe eme -> time { hours: "las 2" minutes: "00" suffix: "p.m." } - - Note that although the TimeFst verbalizer can accept 'zone' (timezone) fields, - so far the rules have not been added to the TimeFst tagger to process - timezones (to keep the rules simple, and because timezones are not very - often specified in Spanish.) - """ - - def __init__(self): - super().__init__(name="time", kind="classify") - - suffix_graph = pynini.string_file(get_abs_path("data/time/time_suffix.tsv")) - time_to_graph = pynini.string_file(get_abs_path("data/time/time_to.tsv")) - - graph_digit = pynini.string_file(get_abs_path("data/numbers/digit.tsv")) - graph_ties = pynini.string_file(get_abs_path("data/numbers/ties.tsv")) - graph_teen = pynini.string_file(get_abs_path("data/numbers/teen.tsv")) - graph_twenties = pynini.string_file(get_abs_path("data/numbers/twenties.tsv")) - - graph_1_to_100 = pynini.union( - graph_digit, - graph_twenties, - graph_teen, - (graph_ties + pynutil.insert("0")), - (graph_ties + pynutil.delete(" y ") + graph_digit), - ) - - # note that graph_hour will start from 2 hours - # "1 o'clock" will be treated differently because it - # is singular - digits_2_to_23 = [str(digits) for digits in range(2, 24)] - digits_1_to_59 = [str(digits) for digits in range(1, 60)] - - graph_1oclock = pynini.cross("la una", "la 1") - graph_hour = pynini.cross("las ", "las ") + graph_1_to_100 @ pynini.union(*digits_2_to_23) - graph_minute = graph_1_to_100 @ pynini.union(*digits_1_to_59) - graph_minute_verbose = pynini.cross("media", "30") | pynini.cross("cuarto", "15") - - final_graph_hour = pynutil.insert("hours: \"") + (graph_1oclock | graph_hour) + pynutil.insert("\"") - - final_graph_minute = ( - pynutil.insert("minutes: \"") - + pynini.closure((pynutil.delete("y") | pynutil.delete("con")) + delete_space, 0, 1) - + (graph_minute | graph_minute_verbose) - + pynutil.insert("\"") - ) - - final_suffix = pynutil.insert("suffix: \"") + convert_space(suffix_graph) + pynutil.insert("\"") - final_suffix_optional = pynini.closure(delete_space + insert_space + final_suffix, 0, 1) - - # las nueve a eme (only convert on-the-hour times if they are followed by a suffix) - graph_hsuffix = ( - final_graph_hour + delete_extra_space + pynutil.insert("minutes: \"00\"") + insert_space + final_suffix - ) - - # las nueve y veinticinco - graph_hm = final_graph_hour + delete_extra_space + final_graph_minute - - # un cuarto para las cinco - graph_mh = ( - pynutil.insert("minutes: \"") - + pynini.union(pynini.cross("un cuarto para", "45"), pynini.cross("cuarto para", "45"),) - + pynutil.insert("\"") - + delete_extra_space - + pynutil.insert("hours: \"") - + time_to_graph - + pynutil.insert("\"") - ) - - # las diez menos diez - graph_time_to = ( - pynutil.insert("hours: \"") - + time_to_graph - + pynutil.insert("\"") - + delete_extra_space - + pynutil.insert("minutes: \"") - + delete_space - + pynutil.delete("menos") - + delete_space - + pynini.union( - pynini.cross("cinco", "55"), - pynini.cross("diez", "50"), - pynini.cross("cuarto", "45"), - pynini.cross("veinte", "40"), - pynini.cross("veinticinco", "30"), - ) - + pynutil.insert("\"") - ) - final_graph = pynini.union( - (graph_hm | graph_mh | graph_time_to) + final_suffix_optional, graph_hsuffix - ).optimize() - - final_graph = self.add_tokens(final_graph) - - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/es/taggers/tokenize_and_classify.py b/nemo_text_processing/inverse_text_normalization/es/taggers/tokenize_and_classify.py deleted file mode 100644 index 730fe023653c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/taggers/tokenize_and_classify.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -import pynini -from nemo_text_processing.inverse_text_normalization.es.taggers.cardinal import CardinalFst -from nemo_text_processing.inverse_text_normalization.es.taggers.date import DateFst -from nemo_text_processing.inverse_text_normalization.es.taggers.decimal import DecimalFst -from nemo_text_processing.inverse_text_normalization.es.taggers.electronic import ElectronicFst -from nemo_text_processing.inverse_text_normalization.es.taggers.measure import MeasureFst -from nemo_text_processing.inverse_text_normalization.es.taggers.money import MoneyFst -from nemo_text_processing.inverse_text_normalization.es.taggers.ordinal import OrdinalFst -from nemo_text_processing.inverse_text_normalization.es.taggers.punctuation import PunctuationFst -from nemo_text_processing.inverse_text_normalization.es.taggers.telephone import TelephoneFst -from nemo_text_processing.inverse_text_normalization.es.taggers.time import TimeFst -from nemo_text_processing.inverse_text_normalization.es.taggers.whitelist import WhiteListFst -from nemo_text_processing.inverse_text_normalization.es.taggers.word import WordFst -from nemo_text_processing.text_normalization.en.graph_utils import ( - GraphFst, - delete_extra_space, - delete_space, - generator_main, -) -from pynini.lib import pynutil - -from nemo.utils import logging - - -class ClassifyFst(GraphFst): - """ - Final class that composes all other classification grammars. This class can process an entire sentence, that is lower cased. - For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - - Args: - cache_dir: path to a dir with .far grammar file. Set to None to avoid using cache. - overwrite_cache: set to True to overwrite .far files - """ - - def __init__(self, cache_dir: str = None, overwrite_cache: bool = False): - super().__init__(name="tokenize_and_classify", kind="classify") - - far_file = None - if cache_dir is not None and cache_dir != "None": - os.makedirs(cache_dir, exist_ok=True) - far_file = os.path.join(cache_dir, "_es_itn.far") - if not overwrite_cache and far_file and os.path.exists(far_file): - self.fst = pynini.Far(far_file, mode="r")["tokenize_and_classify"] - logging.info(f"ClassifyFst.fst was restored from {far_file}.") - else: - logging.info(f"Creating ClassifyFst grammars.") - - cardinal = CardinalFst() - cardinal_graph = cardinal.fst - - ordinal = OrdinalFst(cardinal) - ordinal_graph = ordinal.fst - - decimal = DecimalFst(cardinal) - decimal_graph = decimal.fst - - measure_graph = MeasureFst(cardinal=cardinal, decimal=decimal).fst - date_graph = DateFst().fst - word_graph = WordFst().fst - time_graph = TimeFst().fst - money_graph = MoneyFst(cardinal=cardinal, decimal=decimal).fst - whitelist_graph = WhiteListFst().fst - punct_graph = PunctuationFst().fst - electronic_graph = ElectronicFst().fst - telephone_graph = TelephoneFst().fst - - classify = ( - pynutil.add_weight(whitelist_graph, 1.01) - | pynutil.add_weight(time_graph, 1.1) - | pynutil.add_weight(date_graph, 1.09) - | pynutil.add_weight(decimal_graph, 1.09) - | pynutil.add_weight(measure_graph, 1.1) - | pynutil.add_weight(cardinal_graph, 1.1) - | pynutil.add_weight(ordinal_graph, 1.1) - | pynutil.add_weight(money_graph, 1.1) - | pynutil.add_weight(telephone_graph, 1.1) - | pynutil.add_weight(electronic_graph, 1.1) - | pynutil.add_weight(word_graph, 100) - ) - - punct = pynutil.insert("tokens { ") + pynutil.add_weight(punct_graph, weight=1.1) + pynutil.insert(" }") - token = pynutil.insert("tokens { ") + classify + pynutil.insert(" }") - token_plus_punct = ( - pynini.closure(punct + pynutil.insert(" ")) + token + pynini.closure(pynutil.insert(" ") + punct) - ) - - graph = token_plus_punct + pynini.closure(delete_extra_space + token_plus_punct) - graph = delete_space + graph + delete_space - - self.fst = graph.optimize() - - if far_file: - generator_main(far_file, {"tokenize_and_classify": self.fst}) - logging.info(f"ClassifyFst grammars are saved to {far_file}.") diff --git a/nemo_text_processing/inverse_text_normalization/es/taggers/whitelist.py b/nemo_text_processing/inverse_text_normalization/es/taggers/whitelist.py deleted file mode 100644 index 4cad1d70ee9d..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/taggers/whitelist.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.es.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst, convert_space -from pynini.lib import pynutil - - -class WhiteListFst(GraphFst): - """ - Finite state transducer for classifying whitelisted tokens - e.g. usted -> tokens { name: "ud." } - This class has highest priority among all classifier grammars. Whitelisted tokens are defined and loaded from "data/whitelist.tsv". - """ - - def __init__(self): - super().__init__(name="whitelist", kind="classify") - - whitelist = pynini.string_file(get_abs_path("data/whitelist.tsv")).invert() - graph = pynutil.insert("name: \"") + convert_space(whitelist) + pynutil.insert("\"") - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/es/taggers/word.py b/nemo_text_processing/inverse_text_normalization/es/taggers/word.py deleted file mode 100644 index 57f143d0d458..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/taggers/word.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_SPACE, GraphFst -from pynini.lib import pynutil - - -class WordFst(GraphFst): - """ - Finite state transducer for classifying plain tokens, that do not belong to any special class. This can be considered as the default class. - e.g. sleep -> tokens { name: "sleep" } - """ - - def __init__(self): - super().__init__(name="word", kind="classify") - word = pynutil.insert("name: \"") + pynini.closure(NEMO_NOT_SPACE, 1) + pynutil.insert("\"") - self.fst = word.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/es/utils.py b/nemo_text_processing/inverse_text_normalization/es/utils.py deleted file mode 100644 index f6e06f793c07..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/utils.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - - -def get_abs_path(rel_path): - """ - Get absolute path - - Args: - rel_path: relative path to this file - - Returns absolute path - """ - return os.path.dirname(os.path.abspath(__file__)) + '/' + rel_path diff --git a/nemo_text_processing/inverse_text_normalization/es/verbalizers/__init__.py b/nemo_text_processing/inverse_text_normalization/es/verbalizers/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/verbalizers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/es/verbalizers/cardinal.py b/nemo_text_processing/inverse_text_normalization/es/verbalizers/cardinal.py deleted file mode 100644 index e6737be6e864..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/verbalizers/cardinal.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class CardinalFst(GraphFst): - """ - Finite state transducer for verbalizing cardinal - e.g. cardinal { negative: "-" integer: "23" } -> -23 - """ - - def __init__(self): - super().__init__(name="cardinal", kind="verbalize") - optional_sign = pynini.closure( - pynutil.delete("negative:") - + delete_space - + pynutil.delete("\"") - + NEMO_NOT_QUOTE - + pynutil.delete("\"") - + delete_space, - 0, - 1, - ) - graph = ( - pynutil.delete("integer:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - self.numbers = graph - graph = optional_sign + graph - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/es/verbalizers/date.py b/nemo_text_processing/inverse_text_normalization/es/verbalizers/date.py deleted file mode 100644 index a6e9df0d26c6..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/verbalizers/date.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_NOT_QUOTE, - GraphFst, - delete_extra_space, - delete_space, - insert_space, -) -from pynini.lib import pynutil - - -class DateFst(GraphFst): - """ - Finite state transducer for verbalizing date, e.g. - date { day: "1" month: "enero" preserve_order: true } -> 1 de enero - """ - - def __init__(self): - super().__init__(name="date", kind="verbalize") - month = ( - pynutil.delete("month:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - day = ( - pynutil.delete("day:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - - # day month - graph_dm = day + delete_extra_space + pynutil.insert("de") + insert_space + month - - optional_preserve_order = pynini.closure( - pynutil.delete("preserve_order:") + delete_space + pynutil.delete("true") + delete_space - | pynutil.delete("field_order:") - + delete_space - + pynutil.delete("\"") - + NEMO_NOT_QUOTE - + pynutil.delete("\"") - + delete_space - ) - - final_graph = graph_dm + delete_space + optional_preserve_order - - delete_tokens = self.delete_tokens(final_graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/es/verbalizers/decimal.py b/nemo_text_processing/inverse_text_normalization/es/verbalizers/decimal.py deleted file mode 100644 index b443733a2f5e..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/verbalizers/decimal.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class DecimalFst(GraphFst): - """ - Finite state transducer for verbalizing decimal, - e.g. decimal { negative: "true" integer_part: "1" morphosyntactic_features: "," fractional_part: "26" } -> -1,26 - e.g. decimal { negative: "true" integer_part: "1" morphosyntactic_features: "." fractional_part: "26" } -> -1.26 - e.g. decimal { negative: "false" integer_part: "1" morphosyntactic_features: "," fractional_part: "26" quantity: "millón" } -> 1,26 millón - e.g. decimal { negative: "false" integer_part: "2" quantity: "millones" } -> 2 millones - """ - - def __init__(self): - super().__init__(name="decimal", kind="verbalize") - optionl_sign = pynini.closure(pynini.cross("negative: \"true\"", "-") + delete_space, 0, 1) - integer = ( - pynutil.delete("integer_part:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - optional_integer = pynini.closure(integer + delete_space, 0, 1) - - decimal_point = pynini.cross("morphosyntactic_features: \",\"", ",") - decimal_point |= pynini.cross("morphosyntactic_features: \".\"", ".") - - fractional = ( - decimal_point - + delete_space - + pynutil.delete("fractional_part:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - optional_fractional = pynini.closure(fractional + delete_space, 0, 1) - quantity = ( - pynutil.delete("quantity:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - optional_quantity = pynini.closure(pynutil.insert(" ") + quantity + delete_space, 0, 1) - graph = optional_integer + optional_fractional + optional_quantity - self.numbers = graph - graph = optionl_sign + graph - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/es/verbalizers/electronic.py b/nemo_text_processing/inverse_text_normalization/es/verbalizers/electronic.py deleted file mode 100644 index cf6bdc779f0e..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/verbalizers/electronic.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class ElectronicFst(GraphFst): - """ - Finite state transducer for verbalizing electronic - e.g. tokens { electronic { username: "cdf1" domain: "abc.edu" } } -> cdf1@abc.edu - e.g. tokens { electronic { protocol: "www.abc.edu" } } -> www.abc.edu - """ - - def __init__(self): - super().__init__(name="electronic", kind="verbalize") - user_name = ( - pynutil.delete("username:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - domain = ( - pynutil.delete("domain:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - - protocol = ( - pynutil.delete("protocol:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - - graph = user_name + delete_space + pynutil.insert("@") + domain - graph |= protocol - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/es/verbalizers/measure.py b/nemo_text_processing/inverse_text_normalization/es/verbalizers/measure.py deleted file mode 100644 index c25a3fd9e3ae..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/verbalizers/measure.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_CHAR, GraphFst, delete_space -from pynini.lib import pynutil - - -class MeasureFst(GraphFst): - """ - Finite state transducer for verbalizing measure, e.g. - measure { cardinal { negative: "true" integer: "12" } units: "kg" } -> -12 kg - - Args: - decimal: DecimalFst - cardinal: CardinalFst - """ - - def __init__(self, decimal: GraphFst, cardinal: GraphFst): - super().__init__(name="measure", kind="verbalize") - optional_sign = pynini.closure(pynini.cross("negative: \"true\"", "-"), 0, 1) - unit = ( - pynutil.delete("units:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_CHAR - " ", 1) - + pynutil.delete("\"") - + delete_space - ) - graph_decimal = ( - pynutil.delete("decimal {") - + delete_space - + optional_sign - + delete_space - + decimal.numbers - + delete_space - + pynutil.delete("}") - ) - graph_cardinal = ( - pynutil.delete("cardinal {") - + delete_space - + optional_sign - + delete_space - + cardinal.numbers - + delete_space - + pynutil.delete("}") - ) - graph = (graph_cardinal | graph_decimal) + delete_space + pynutil.insert(" ") + unit - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/es/verbalizers/money.py b/nemo_text_processing/inverse_text_normalization/es/verbalizers/money.py deleted file mode 100644 index a52371f1e756..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/verbalizers/money.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_CHAR, GraphFst, delete_space -from pynini.lib import pynutil - - -class MoneyFst(GraphFst): - """ - Finite state transducer for verbalizing money, e.g. - money { integer_part: "12" morphosyntactic_features: "," fractional_part: "05" currency: "$" } -> $12,05 - - Args: - decimal: DecimalFst - """ - - def __init__(self, decimal: GraphFst): - super().__init__(name="money", kind="verbalize") - unit = ( - pynutil.delete("currency:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_CHAR - " ", 1) - + pynutil.delete("\"") - ) - graph = unit + delete_space + decimal.numbers - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/es/verbalizers/ordinal.py b/nemo_text_processing/inverse_text_normalization/es/verbalizers/ordinal.py deleted file mode 100644 index 8cfb15095367..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/verbalizers/ordinal.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class OrdinalFst(GraphFst): - """ - Finite state transducer for verbalizing ordinal, e.g. - ordinal { integer: "13" morphosyntactic_features: "o" } -> 13.º - """ - - def __init__(self): - super().__init__(name="ordinal", kind="verbalize") - graph = ( - pynutil.delete("integer:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - - replace_suffix = pynini.union( - pynini.cross(" morphosyntactic_features: \"o\"", ".º"), - pynini.cross(" morphosyntactic_features: \"a\"", ".ª"), - pynini.cross(" morphosyntactic_features: \"er\"", ".ᵉʳ"), - ) - - graph = graph + replace_suffix - - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/es/verbalizers/telephone.py b/nemo_text_processing/inverse_text_normalization/es/verbalizers/telephone.py deleted file mode 100644 index bc32f62fe474..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/verbalizers/telephone.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst -from pynini.lib import pynutil - - -class TelephoneFst(GraphFst): - """ - Finite state transducer for verbalizing telephone, e.g. - telephone { number_part: "123-123-5678" } - -> 123-123-5678 - """ - - def __init__(self): - super().__init__(name="telephone", kind="verbalize") - - number_part = pynutil.delete("number_part: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - delete_tokens = self.delete_tokens(number_part) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/es/verbalizers/time.py b/nemo_text_processing/inverse_text_normalization/es/verbalizers/time.py deleted file mode 100644 index 1cb725099891..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/verbalizers/time.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_CHAR, - NEMO_DIGIT, - GraphFst, - delete_space, - insert_space, -) -from pynini.lib import pynutil - - -class TimeFst(GraphFst): - """ - Finite state transducer for verbalizing time, - e.g. time { hours: "la 1" minutes: "10" } -> la 1:10 - e.g. time { hours: "la 1" minutes: "45" } -> la 1:45 - """ - - def __init__(self): - super().__init__(name="time", kind="verbalize") - add_leading_zero_to_double_digit = (NEMO_DIGIT + NEMO_DIGIT) | (pynutil.insert("0") + NEMO_DIGIT) - - # hour includes preposition ("la" or "las") - hour = ( - pynutil.delete("hours:") - + delete_space - + pynutil.delete("\"") - + pynini.union("la ", "las ") - + pynini.closure(NEMO_DIGIT, 1) - + pynutil.delete("\"") - ) - minute = ( - pynutil.delete("minutes:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_DIGIT, 1) - + pynutil.delete("\"") - ) - suffix = ( - delete_space - + insert_space - + pynutil.delete("suffix:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_CHAR - " ", 1) - + pynutil.delete("\"") - ) - optional_suffix = pynini.closure(suffix, 0, 1) - zone = ( - delete_space - + insert_space - + pynutil.delete("zone:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_CHAR - " ", 1) - + pynutil.delete("\"") - ) - optional_zone = pynini.closure(zone, 0, 1) - graph = ( - hour - + delete_space - + pynutil.insert(":") - + (minute @ add_leading_zero_to_double_digit) - + optional_suffix - + optional_zone - ) - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/es/verbalizers/verbalize.py b/nemo_text_processing/inverse_text_normalization/es/verbalizers/verbalize.py deleted file mode 100644 index 4cbb140a7179..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/verbalizers/verbalize.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from nemo_text_processing.inverse_text_normalization.es.verbalizers.cardinal import CardinalFst -from nemo_text_processing.inverse_text_normalization.es.verbalizers.date import DateFst -from nemo_text_processing.inverse_text_normalization.es.verbalizers.decimal import DecimalFst -from nemo_text_processing.inverse_text_normalization.es.verbalizers.electronic import ElectronicFst -from nemo_text_processing.inverse_text_normalization.es.verbalizers.measure import MeasureFst -from nemo_text_processing.inverse_text_normalization.es.verbalizers.money import MoneyFst -from nemo_text_processing.inverse_text_normalization.es.verbalizers.ordinal import OrdinalFst -from nemo_text_processing.inverse_text_normalization.es.verbalizers.telephone import TelephoneFst -from nemo_text_processing.inverse_text_normalization.es.verbalizers.time import TimeFst -from nemo_text_processing.inverse_text_normalization.es.verbalizers.whitelist import WhiteListFst -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst - - -class VerbalizeFst(GraphFst): - """ - Composes other verbalizer grammars. - For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - """ - - def __init__(self): - super().__init__(name="verbalize", kind="verbalize") - cardinal = CardinalFst() - cardinal_graph = cardinal.fst - ordinal_graph = OrdinalFst().fst - decimal = DecimalFst() - decimal_graph = decimal.fst - measure_graph = MeasureFst(decimal=decimal, cardinal=cardinal).fst - money_graph = MoneyFst(decimal=decimal).fst - time_graph = TimeFst().fst - date_graph = DateFst().fst - whitelist_graph = WhiteListFst().fst - telephone_graph = TelephoneFst().fst - electronic_graph = ElectronicFst().fst - - graph = ( - time_graph - | date_graph - | money_graph - | measure_graph - | ordinal_graph - | decimal_graph - | cardinal_graph - | whitelist_graph - | telephone_graph - | electronic_graph - ) - self.fst = graph diff --git a/nemo_text_processing/inverse_text_normalization/es/verbalizers/verbalize_final.py b/nemo_text_processing/inverse_text_normalization/es/verbalizers/verbalize_final.py deleted file mode 100644 index 26d09996b67d..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/verbalizers/verbalize_final.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.es.verbalizers.verbalize import VerbalizeFst -from nemo_text_processing.inverse_text_normalization.es.verbalizers.word import WordFst -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst, delete_extra_space, delete_space -from pynini.lib import pynutil - - -class VerbalizeFinalFst(GraphFst): - """ - Finite state transducer that verbalizes an entire sentence, e.g. - tokens { name: "its" } tokens { time { hours: "12" minutes: "30" } } tokens { name: "now" } -> its 12:30 now - """ - - def __init__(self): - super().__init__(name="verbalize_final", kind="verbalize") - verbalize = VerbalizeFst().fst - word = WordFst().fst - types = verbalize | word - graph = ( - pynutil.delete("tokens") - + delete_space - + pynutil.delete("{") - + delete_space - + types - + delete_space - + pynutil.delete("}") - ) - graph = delete_space + pynini.closure(graph + delete_extra_space) + graph + delete_space - self.fst = graph diff --git a/nemo_text_processing/inverse_text_normalization/es/verbalizers/whitelist.py b/nemo_text_processing/inverse_text_normalization/es/verbalizers/whitelist.py deleted file mode 100644 index cc231a46b655..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/verbalizers/whitelist.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_CHAR, NEMO_SIGMA, GraphFst, delete_space -from pynini.lib import pynutil - - -class WhiteListFst(GraphFst): - """ - Finite state transducer for verbalizing whitelist - e.g. tokens { name: "uds." } -> uds. - """ - - def __init__(self): - super().__init__(name="whitelist", kind="verbalize") - graph = ( - pynutil.delete("name:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_CHAR - " ", 1) - + pynutil.delete("\"") - ) - graph = graph @ pynini.cdrewrite(pynini.cross(u"\u00A0", " "), "", "", NEMO_SIGMA) - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/es/verbalizers/word.py b/nemo_text_processing/inverse_text_normalization/es/verbalizers/word.py deleted file mode 100644 index 3a5ba96b95d5..000000000000 --- a/nemo_text_processing/inverse_text_normalization/es/verbalizers/word.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_CHAR, NEMO_SIGMA, GraphFst, delete_space -from pynini.lib import pynutil - - -class WordFst(GraphFst): - """ - Finite state transducer for verbalizing plain tokens - e.g. tokens { name: "sleep" } -> sleep - """ - - def __init__(self): - super().__init__(name="word", kind="verbalize") - chars = pynini.closure(NEMO_CHAR - " ", 1) - char = pynutil.delete("name:") + delete_space + pynutil.delete("\"") + chars + pynutil.delete("\"") - graph = char @ pynini.cdrewrite(pynini.cross(u"\u00A0", " "), "", "", NEMO_SIGMA) - - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/fr/README.md b/nemo_text_processing/inverse_text_normalization/fr/README.md deleted file mode 100644 index 049f7182c9af..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# Note on French spelling - -Due to a 1990 orthographic reform, there are currently two conventions for written French numbers: - -1. **Reformed** All composite words are joined by a hyphen: -e.g. `1122 -> mille-cent-vingt-deux` - -2. **Traditional** Hyphenation only occurs (with exception) for numbers from 17 to 99 (inclusive): -e.g. `1122 -> mille cent vingt-deux` - -As available training data for upstream ASR will vary in use of convention, NeMo's French ITN accomodates either style for normalization e.g. - -``` - python inverse_normalize.py "mille-cent-vingt-deux" --language="fr" --> 1122 - python inverse_normalize.py "mille cent vingt-deux" --language="fr" --> 1122 -``` - -As a result, there exists some ambiguity in the case of currency conversions, namely minor denominations of the dollar e.g. - -``` - 300 -> "trois-cents" # Reformed spelling - 300 -> "trois cents" # Traditional spelling - 3 ¢ -> "trois cents" # Valid for both -``` - -Cardinals take priority in such cases. - -``` -python inverse_normalize.py "trois cents" --language="fr" -> 300 -``` diff --git a/nemo_text_processing/inverse_text_normalization/fr/__init__.py b/nemo_text_processing/inverse_text_normalization/fr/__init__.py deleted file mode 100644 index 37e27e0e968c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from nemo_text_processing.inverse_text_normalization.fr.taggers.tokenize_and_classify import ClassifyFst -from nemo_text_processing.inverse_text_normalization.fr.verbalizers.verbalize import VerbalizeFst -from nemo_text_processing.inverse_text_normalization.fr.verbalizers.verbalize_final import VerbalizeFinalFst diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/__init__.py b/nemo_text_processing/inverse_text_normalization/fr/data/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/electronic/__init__.py b/nemo_text_processing/inverse_text_normalization/fr/data/electronic/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/electronic/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/electronic/domain.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/electronic/domain.tsv deleted file mode 100644 index 8053757321a0..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/electronic/domain.tsv +++ /dev/null @@ -1,25 +0,0 @@ -com -es -uk -fr -net -br -in -ru -de -it -edu -co -ar -bo -cl -co -ec -fk -gf -fy -pe -py -sr -ve -uy \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/electronic/server_name.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/electronic/server_name.tsv deleted file mode 100644 index cf6bbf44d524..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/electronic/server_name.tsv +++ /dev/null @@ -1,17 +0,0 @@ -g mail gmail -gmail -n vidia nvidia -nvidia -outlook -hotmail -yahoo -aol -gmx -msn -live -yandex -orange -wanadoo -web -google -comcast \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/electronic/symbols.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/electronic/symbols.tsv deleted file mode 100644 index 198f13c3ca8e..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/electronic/symbols.tsv +++ /dev/null @@ -1,12 +0,0 @@ -chez @ -at @ -à @ -arobase @ -point . -barre oblique / -tiret - -tiret bas _ -souligné _ -sous-tiret _ -blanc souligné _ -underscore _ \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/fractions.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/fractions.tsv deleted file mode 100644 index 5f18ea20aad0..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/fractions.tsv +++ /dev/null @@ -1,41 +0,0 @@ -demie deux -demies deux -demi deux -demis deux -tiers trois -quart quatre -quarts quatre -quatrièmes quatre -quatrième quatre -cinquième cinq -cinquièmes cinq -neuvième neuf -neuvièmes neuf -onzième onze -onzièmes onze -douzième douze -douzièmes douze -treizième treize -treizièmes treize -quatorzième quatorze -quatorzièmes quatorze -quinzième quinze -quinzièmes quinze -seizième seize -seizièmes seize -trentième trente -trentièmes trente -quarantième quarante -quarantièmes quarante -cinquantième cinquante -cinquantièmes cinquante -soixantième soixante -soixantièmes soixante -septantième septante -septantièmes septante -huitantième huitante -huitantièmes huitante -nonantième nonante -nonantièmes nonante -millième mille -millièmes mille \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/measurements/__init__.py b/nemo_text_processing/inverse_text_normalization/fr/data/measurements/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/measurements/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/measurements/magnitudes.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/measurements/magnitudes.tsv deleted file mode 100644 index ded57b90a31d..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/measurements/magnitudes.tsv +++ /dev/null @@ -1,16 +0,0 @@ -exa E -péta P -téra T -giga G -méga M -kilo k -hecto h -déca da -déci d -centi c -milli m -micro µ -nano n -pico p -femto f -atto a \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/measurements/measurements.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/measurements/measurements.tsv deleted file mode 100644 index 91ca880c4aef..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/measurements/measurements.tsv +++ /dev/null @@ -1,27 +0,0 @@ -mètre m -mètre carré m² -mètres carrés m² -mètre cube m³ -mètres cubes m³ -seconde s -minute min -heure h -degré ° -degrés ° -degré celsius °C -degrés celsius °C -gramme g -litre l -kilo kg -pouce '' -livre lb -poid lb -mile ml -pour cent % -pour mille ‰ -mètre heure km/h -mètres heure m/h -mètre à l’heure m/h -mètres à l’heure m/h -mètre par l’heure m/h -mètres par l’heure m/h \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/money/__init__.py b/nemo_text_processing/inverse_text_normalization/fr/data/money/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/money/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/money/currency_major.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/money/currency_major.tsv deleted file mode 100644 index 3f9e1cec2c3d..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/money/currency_major.tsv +++ /dev/null @@ -1,40 +0,0 @@ -dollar $ -dollars $ -dollar américain $ US -dollars américains $ US -dollar des États-Unis $ US -dollars des États-Unis $ US -dollar canadien $ CA -dollars canadiens $ CA -dollar australien $ AU -dollars australien $ AU -dollar néo-zélandais $ NZ -dollars néo-zélandais $ NZ -dollar de Hong Kong $ HK -dollars de Hong Kong $ HK -euro € -euros € -livre sterling £ -livre £ -livres £ -livre britannique £ -livres britanniques £ -sterling £ -won ₩ -won sud-coréen ₩ -yen japonais ¥ -yens japonais ¥ -yen ¥ -yens ¥ -yuan ¥ -yuans ¥ -franc CHF -franc suisse CHF -dinar algérien DA -dinars algériens DA -dinar DA -dinars DA -dirham marocain DH -dirhams marocains DH -dirham DH -dirhams DH \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/money/currency_minor.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/money/currency_minor.tsv deleted file mode 100644 index f5622c2d23b0..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/money/currency_minor.tsv +++ /dev/null @@ -1,5 +0,0 @@ -centime € -centimes € -eurocent € -eurocents € -pence £ \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/months.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/months.tsv deleted file mode 100644 index 5051c6835098..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/months.tsv +++ /dev/null @@ -1,12 +0,0 @@ -janvier -février -mars -avril -mai -juin -juillet -août -septembre -octobre -novembre -décembre \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/numbers/__init__.py b/nemo_text_processing/inverse_text_normalization/fr/data/numbers/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/numbers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/numbers/digit.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/numbers/digit.tsv deleted file mode 100644 index 1cb49929aaf7..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/numbers/digit.tsv +++ /dev/null @@ -1,10 +0,0 @@ -un 1 -une 1 -deux 2 -trois 3 -quatre 4 -cinq 5 -six 6 -sept 7 -huit 8 -neuf 9 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/numbers/hundreds.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/numbers/hundreds.tsv deleted file mode 100644 index f23060a2b6a7..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/numbers/hundreds.tsv +++ /dev/null @@ -1,2 +0,0 @@ -cent -cents \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/numbers/teen.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/numbers/teen.tsv deleted file mode 100644 index 0229696e9a72..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/numbers/teen.tsv +++ /dev/null @@ -1,6 +0,0 @@ -onze 11 -douze 12 -treize 13 -quatorze 14 -quinze 15 -seize 16 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/numbers/thousands.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/numbers/thousands.tsv deleted file mode 100644 index f77e5142656d..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/numbers/thousands.tsv +++ /dev/null @@ -1,23 +0,0 @@ -mille -million -millions -milliard -milliards -billion -billions -trillion -trillions -quadrillion -quadrillions -quintillion -quintillions -sextillion -sextillions -septillion -septillions -octillion -octillions -nonillion -nonillions -decillion -decillions \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/numbers/ties.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/numbers/ties.tsv deleted file mode 100644 index b4dc2d6def67..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/numbers/ties.tsv +++ /dev/null @@ -1,13 +0,0 @@ -dix 1 -vingt 2 -vingts 2 -trente 3 -quarante 4 -cinquante 5 -soixante 6 -soixante-dix 7 -septante 7 -huitante 8 -quatre-vingt 8 -quatre-vingt-dix 9 -nonante 9 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/numbers/ties_unique.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/numbers/ties_unique.tsv deleted file mode 100644 index 2729167c192c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/numbers/ties_unique.tsv +++ /dev/null @@ -1,25 +0,0 @@ -vingts 20 -vingt-et-un 21 -vingt-et-une 21 -trente-et-un 31 -trente-et-une 31 -quarante-et-un 41 -quarante-et-une 41 -cinquante-et-un 51 -cinquante-et-une 51 -soixante-et-un 61 -soixante-et-une 61 -soixante-et-onze 71 -soixante-douze 72 -soixante-treize 73 -soixante-quatorze 74 -soixante-quinze 75 -soixante-seize 76 -quatre-vingts 80 -quatre-vingt 80 -quatre-vingt-onze 91 -quatre-vingt-douze 92 -quatre-vingt-treize 93 -quatre-vingt-quatorze 94 -quatre-vingt-quinze 95 -quatre-vingt-seize 96 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/numbers/zero.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/numbers/zero.tsv deleted file mode 100644 index 3ee7c2afa353..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/numbers/zero.tsv +++ /dev/null @@ -1 +0,0 @@ -zéro 0 diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/ordinals/__init__.py b/nemo_text_processing/inverse_text_normalization/fr/data/ordinals/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/ordinals/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/ordinals/digits_root_change.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/ordinals/digits_root_change.tsv deleted file mode 100644 index ab7c43859d19..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/ordinals/digits_root_change.tsv +++ /dev/null @@ -1,17 +0,0 @@ -quatrième quatre -cinquième cinq -neuvième neuf -onzième onze -douzième douze -treizième treize -quatorzième quatorze -quinzième quinze -seizième seize -trentième trente -quarantième quarante -cinquantième cinquante -soixantième soixante -septantième septante -huitantième huitante -nonantième nonante -millième mille \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/ordinals/firsts.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/ordinals/firsts.tsv deleted file mode 100644 index d648b34b1e69..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/ordinals/firsts.tsv +++ /dev/null @@ -1,4 +0,0 @@ -premier er -premiers ers -première re -premières res \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/ordinals/key_nouns.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/ordinals/key_nouns.tsv deleted file mode 100644 index 543e4f051280..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/ordinals/key_nouns.tsv +++ /dev/null @@ -1 +0,0 @@ -siècle \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/ordinals/second.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/ordinals/second.tsv deleted file mode 100644 index 2eaead50b4e9..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/ordinals/second.tsv +++ /dev/null @@ -1,4 +0,0 @@ -second d -seconde de -seconds ds -secondes des \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/roman/__init__.py b/nemo_text_processing/inverse_text_normalization/fr/data/roman/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/roman/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/roman/digits_large.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/roman/digits_large.tsv deleted file mode 100644 index e5fde2cc6722..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/roman/digits_large.tsv +++ /dev/null @@ -1,9 +0,0 @@ -I 1 -II 2 -III 3 -IV 4 -V 5 -VI 6 -VII 7 -VIII 8 -IX 9 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/roman/hundreds_large.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/roman/hundreds_large.tsv deleted file mode 100644 index 5e04779be5ea..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/roman/hundreds_large.tsv +++ /dev/null @@ -1,9 +0,0 @@ -C 1 -CC 2 -CCC 3 -CD 4 -D 5 -DC 6 -DCC 7 -DCCC 8 -CM 9 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/roman/ties_large.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/roman/ties_large.tsv deleted file mode 100644 index 445773d91f0c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/roman/ties_large.tsv +++ /dev/null @@ -1,9 +0,0 @@ -X 1 -XX 2 -XXX 3 -XL 4 -L 5 -LX 6 -LXX 7 -LXXX 8 -XC 9 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/suppletive.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/suppletive.tsv deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/time/__init__.py b/nemo_text_processing/inverse_text_normalization/fr/data/time/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/time/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/time/hour_to_night.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/time/hour_to_night.tsv deleted file mode 100644 index 1374f45c0f5b..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/time/hour_to_night.tsv +++ /dev/null @@ -1,12 +0,0 @@ -1 13 -2 14 -3 15 -4 16 -5 17 -6 18 -7 19 -8 20 -9 21 -10 22 -11 23 -12 0 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/time/hours.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/time/hours.tsv deleted file mode 100644 index 732b2b0096fa..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/time/hours.tsv +++ /dev/null @@ -1,26 +0,0 @@ -zéro 0 -une 1 -deux 2 -trois 3 -quatre 4 -cinq 5 -six 6 -sept 7 -huit 8 -neuf 9 -dix 10 -onze 11 -douze 12 -treize 13 -quatorze 14 -quinze 15 -seize 16 -dix-sept 17 -dix-huit 18 -dix-neuf 19 -vingt 20 -vingt-et-une 21 -vingt et une -vingt-deux 22 -vingt-trois 23 -vingt-quatre 24 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/time/hours_to.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/time/hours_to.tsv deleted file mode 100644 index a56219579975..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/time/hours_to.tsv +++ /dev/null @@ -1,25 +0,0 @@ -1 0 -2 1 -3 2 -4 3 -5 4 -6 5 -7 6 -8 7 -9 8 -10 9 -11 10 -12 11 -13 12 -14 13 -15 14 -16 15 -17 16 -18 17 -19 18 -20 19 -21 20 -22 21 -23 22 -24 23 -0 23 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/time/minutes.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/time/minutes.tsv deleted file mode 100644 index c0a555475902..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/time/minutes.tsv +++ /dev/null @@ -1,63 +0,0 @@ -une 01 -deux 02 -trois 03 -quatre 04 -cinq 05 -six 06 -sept 07 -huit 08 -neuf 09 -dix 10 -onze 11 -douze 12 -treize 13 -quatorze 14 -quinze 15 -seize 16 -dix-sept 17 -dix-huit 18 -dix-neuf 19 -vingt 20 -vingt-et-une 21 -vingt et une 21 -vingt-deux 22 -vingt-trois 23 -vingt-quatre 27 -vingt-cinq 25 -vingt-six 26 -vingt-sept 27 -vingt-huit 28 -vingt-neuf 29 -trente 30 -trente-et-une 31 -trente et une 31 -trente-deux 32 -trente-trois 33 -trente-quatre 34 -trente-cinq 35 -trente-six 36 -trente-sept 37 -trente-huit 38 -trente-neuf 39 -quarante 40 -quarante-et-une 41 -quarante et une 41 -quarante-deux 42 -quarante-trois 43 -quarante-quatre 44 -quarante-cinq 45 -quarante-six 46 -quarante-sept 47 -quarante-huit 48 -quarante-neuf 49 -cinquante 50 -cinquante-et-une 51 -cinquante et une 51 -cinquante-deux 52 -cinquante-trois 53 -cinquante-quatre 54 -cinquante-cinq 55 -cinquante-six 56 -cinquante-sept 57 -cinquante-huit 58 -cinquante-neuf 59 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/time/minutes_to.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/time/minutes_to.tsv deleted file mode 100644 index d8516a9f83ce..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/time/minutes_to.tsv +++ /dev/null @@ -1,59 +0,0 @@ -01 59 -02 58 -03 57 -04 56 -05 55 -06 54 -07 53 -08 52 -09 51 -10 50 -11 49 -12 48 -13 47 -14 46 -15 45 -16 44 -17 43 -18 42 -19 41 -20 40 -21 39 -22 38 -23 37 -24 36 -25 35 -26 34 -27 33 -28 32 -29 31 -30 30 -31 29 -32 28 -33 27 -34 26 -35 25 -36 24 -37 23 -38 22 -39 21 -40 20 -41 19 -42 18 -43 17 -44 16 -45 15 -46 14 -47 13 -48 12 -49 11 -50 10 -51 09 -52 08 -53 07 -54 06 -55 05 -56 04 -57 03 -58 02 -59 01 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/time/time_suffix_am.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/time/time_suffix_am.tsv deleted file mode 100644 index 7ae369317bc9..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/time/time_suffix_am.tsv +++ /dev/null @@ -1 +0,0 @@ -du matin \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/time/time_suffix_pm.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/time/time_suffix_pm.tsv deleted file mode 100644 index 3cad151dca4e..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/time/time_suffix_pm.tsv +++ /dev/null @@ -1,2 +0,0 @@ -de l'après-midi -du soir \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/data/whitelist.tsv b/nemo_text_processing/inverse_text_normalization/fr/data/whitelist.tsv deleted file mode 100644 index 1a09a2ece8e4..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/data/whitelist.tsv +++ /dev/null @@ -1,16 +0,0 @@ -monsieur M. -messieurs MM. -madame Mᵐᵉ -mesdames Mᵐᵉˢ -mademoiselle Mˡˡᵉ -mademoiselles Mˡˡᵉˢ -docteur Dʳ -docteurs Dʳˢ -docteure Dʳᵉ -docteures Dʳᵉˢ -après jésus-christ apr. J.-C. -avant Jésus-Christ av. J.-C. -ca v. -vers v. -l’honorable le hon. -le très hononrable le très hon. \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/fr/graph_utils.py b/nemo_text_processing/inverse_text_normalization/fr/graph_utils.py deleted file mode 100644 index 1ca29ee113ba..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/graph_utils.py +++ /dev/null @@ -1,189 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import string -from pathlib import Path -from typing import Dict - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.utils import get_abs_path -from pynini import Far -from pynini.examples import plurals -from pynini.export import export -from pynini.lib import byte, pynutil, utf8 - -NEMO_CHAR = utf8.VALID_UTF8_CHAR - -NEMO_DIGIT = byte.DIGIT -NEMO_LOWER = pynini.union(*string.ascii_lowercase).optimize() -NEMO_UPPER = pynini.union(*string.ascii_uppercase).optimize() -NEMO_ALPHA = pynini.union(NEMO_LOWER, NEMO_UPPER).optimize() -NEMO_ALNUM = pynini.union(NEMO_DIGIT, NEMO_ALPHA).optimize() -NEMO_HEX = pynini.union(*string.hexdigits).optimize() -NEMO_NON_BREAKING_SPACE = u"\u00A0" -NEMO_SPACE = " " -NEMO_WHITE_SPACE = pynini.union(" ", "\t", "\n", "\r", u"\u00A0").optimize() -NEMO_NOT_SPACE = pynini.difference(NEMO_CHAR, NEMO_WHITE_SPACE).optimize() -NEMO_NOT_QUOTE = pynini.difference(NEMO_CHAR, r'"').optimize() - -NEMO_PUNCT = pynini.union(*map(pynini.escape, string.punctuation)).optimize() -NEMO_GRAPH = pynini.union(NEMO_ALNUM, NEMO_PUNCT).optimize() - -NEMO_SIGMA = pynini.closure(NEMO_CHAR) - -delete_space = pynutil.delete(pynini.closure(NEMO_WHITE_SPACE)) -insert_space = pynutil.insert(" ") -delete_extra_space = pynini.cross(pynini.closure(NEMO_WHITE_SPACE, 1), " ") - -# French frequently compounds numbers with hyphen. -delete_hyphen = pynutil.delete(pynini.closure("-", 0, 1)) -insert_hyphen = pynutil.insert("-") -suppletive = pynini.string_file(get_abs_path("data/suppletive.tsv")) - -_s = NEMO_SIGMA + pynutil.insert("s") -_x = NEMO_SIGMA + pynini.string_map([("eau"), ("eu"), ("ou")]) + pynutil.insert("x") -_aux = NEMO_SIGMA + pynini.string_map([("al", "aux"), ("ail", "aux")]) - -graph_plural = plurals._priority_union( - suppletive, plurals._priority_union(_s, pynini.union(_x, _aux), NEMO_SIGMA), NEMO_SIGMA -).optimize() - -SINGULAR_TO_PLURAL = graph_plural -PLURAL_TO_SINGULAR = pynini.invert(graph_plural) -TO_LOWER = pynini.union(*[pynini.cross(x, y) for x, y in zip(string.ascii_uppercase, string.ascii_lowercase)]) -TO_UPPER = pynini.invert(TO_LOWER) - - -def generator_main(file_name: str, graphs: Dict[str, pynini.FstLike]): - """ - Exports graph as OpenFst finite state archive (FAR) file with given file name and rule name. - - Args: - file_name: exported file name - graphs: Mapping of a rule name and Pynini WFST graph to be exported - """ - exporter = export.Exporter(file_name) - for rule, graph in graphs.items(): - exporter[rule] = graph.optimize() - exporter.close() - print(f'Created {file_name}') - - -def get_plurals(fst): - """ - Given singular returns plurals - - Args: - fst: Fst - - Returns plurals to given singular forms - """ - return SINGULAR_TO_PLURAL @ fst - - -def get_singulars(fst): - """ - Given plural returns singulars - - Args: - fst: Fst - - Returns singulars to given plural forms - """ - return PLURAL_TO_SINGULAR @ fst - - -def convert_space(fst) -> 'pynini.FstLike': - """ - Converts space to nonbreaking space. - Used only in tagger grammars for transducing token values within quotes, e.g. name: "hello kitty" - This is making transducer significantly slower, so only use when there could be potential spaces within quotes, otherwise leave it. - - Args: - fst: input fst - - Returns output fst where breaking spaces are converted to non breaking spaces - """ - return fst @ pynini.cdrewrite(pynini.cross(NEMO_SPACE, NEMO_NON_BREAKING_SPACE), "", "", NEMO_SIGMA) - - -class GraphFst: - """ - Base class for all grammar fsts. - - Args: - name: name of grammar class - kind: either 'classify' or 'verbalize' - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, name: str, kind: str, deterministic: bool = True): - self.name = name - self.kind = kind - self._fst = None - self.deterministic = deterministic - - self.far_path = Path(os.path.dirname(__file__) + '/grammars/' + kind + '/' + name + '.far') - if self.far_exist(): - self._fst = Far(self.far_path, mode="r", arc_type="standard", far_type="default").get_fst() - - def far_exist(self) -> bool: - """ - Returns true if FAR can be loaded - """ - return self.far_path.exists() - - @property - def fst(self) -> 'pynini.FstLike': - return self._fst - - @fst.setter - def fst(self, fst): - self._fst = fst - - def add_tokens(self, fst) -> 'pynini.FstLike': - """ - Wraps class name around to given fst - - Args: - fst: input fst - - Returns: - Fst: fst - """ - return pynutil.insert(f"{self.name} {{ ") + fst + pynutil.insert(" }") - - def delete_tokens(self, fst) -> 'pynini.FstLike': - """ - Deletes class name wrap around output of given fst - - Args: - fst: input fst - - Returns: - Fst: fst - """ - res = ( - pynutil.delete(f"{self.name}") - + delete_space - + pynutil.delete("{") - + delete_space - + fst - + delete_space - + pynutil.delete("}") - ) - return res @ pynini.cdrewrite(pynini.cross(u"\u00A0", " "), "", "", NEMO_SIGMA) diff --git a/nemo_text_processing/inverse_text_normalization/fr/taggers/__init__.py b/nemo_text_processing/inverse_text_normalization/fr/taggers/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/taggers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/fr/taggers/cardinal.py b/nemo_text_processing/inverse_text_normalization/fr/taggers/cardinal.py deleted file mode 100644 index 2c8fd6c0c16f..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/taggers/cardinal.py +++ /dev/null @@ -1,278 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import ( - NEMO_CHAR, - NEMO_DIGIT, - NEMO_NOT_SPACE, - NEMO_SIGMA, - NEMO_SPACE, - GraphFst, - delete_hyphen, -) -from nemo_text_processing.inverse_text_normalization.fr.utils import get_abs_path -from pynini.lib import pynutil - - -def rewrite(cardinal: 'pynini.FstLike') -> 'pynini.FstLike': - """ - Function to rewrite cardinals written in traditional orthograph (no '-' for numbers >100) - to current orthography ('-' between all words in number string) - e.g. deux mille cent vingt-trois -> deux-mille-cent-vingt-trois. - In cases where original orthography is current, or string is mixture of two orthographies, - will render invalid form that will not pass through CardinalFst - e.g. deux-mille cent-vingt-trois -> "deux##vingt-trois" ('#' is not accepted in cardinal FST and will fail to convert.) - e.g. deux - - Args: - cardinal: cardinal FST - """ - - # Traditional orthography does not hyphenate numbers > 100, this will insert hyphens in - # those contexts. - targets = pynini.string_map( - [ - "et", # for 'et un/onze' - "cent", - "mille", - "million", - "milliard", - "billion", - "billiard", - "trillion", - "trilliard", - ] - ) - targets += pynini.accep("s").ques - - no_spaces = pynini.closure(NEMO_NOT_SPACE) - - # Valid numbers in reformed orthography will have no spaces. - new_orthography_sigma = no_spaces - - # Old orthography will not have these strings. Replacing with character to mark. - targets_for_filtering = ("-" + targets) | ("-" + targets + "-") | (targets + "-") - - filter = pynini.cdrewrite(pynini.cross(targets_for_filtering, "#"), "", "", NEMO_SIGMA) # Invalid for cardinal - - old_orthography_sigma = pynini.difference(NEMO_CHAR, "#") # Marked character removed from sigma_star. - old_orthography_sigma.closure() - - # Only accept strings that occur in old orthography. (This avoids tying two non-related numbers together.) - # e.g. mille cent-une -> mille-cent-une - filter @= old_orthography_sigma - - # Now know replacements will only work around targets - replace_left = pynini.cdrewrite(pynini.cross(" ", "-"), "", targets, NEMO_SIGMA) - - replace_right = pynini.cdrewrite(pynini.cross(" ", "-"), targets, "", NEMO_SIGMA) - - replace = replace_left @ replace_right - - graph = new_orthography_sigma | (filter @ replace) - - return graph @ cardinal - - -class CardinalFst(GraphFst): - """ - Finite state transducer for classifying cardinals - e.g. mois vingt-trois -> cardinal { negative: "-" integer: "23"} - This class converts cardinals up to (but not including) "un-quatrillion", - i.e up to "one septillion" in English (10^{24}). - Cardinals below nine are not converted (in order to avoid - "j'ai un pomme." --> "j'ai 1 pomme" and any other odd conversions.) - This transducer accomodates both traditional hyphenation of numbers ('-' for most numbers <100) - and current hyphenation (all elements of number are hyphenated), prioritizing the latter. - e.g cent cinquante et un -> cardinal { integer: "151"} - cent-cinquante-et-un -> cardinal { integer: "151"} - This is done through a context dependent rewrite that attempts to map old spelling to new. - e.g. cent cinquante et un -> cent-cinquante-et-un - """ - - def __init__(self): - super().__init__(name="cardinal", kind="classify") - graph_zero = pynini.string_file(get_abs_path("data/numbers/zero.tsv")) - graph_digit = pynini.string_file(get_abs_path("data/numbers/digit.tsv")) - graph_teens = pynini.string_file(get_abs_path("data/numbers/teen.tsv")) - graph_ties = pynini.string_file(get_abs_path("data/numbers/ties.tsv")) - graph_ties_unique = pynini.string_file(get_abs_path("data/numbers/ties_unique.tsv")) - - # Tens components - graph_tens_component = graph_ties + ((delete_hyphen + graph_digit) | pynutil.insert("0")) - graph_tens_component = pynini.union(graph_tens_component, graph_teens, graph_ties_unique) - - graph_tens_component_with_leading_zeros = pynini.union( - graph_tens_component, (pynutil.insert("0") + (graph_digit | pynutil.insert("0", weight=0.01))) - ) - - # Hundreds components - graph_cent_singular = pynutil.delete("cent") # Used in hundreds place - graph_cent_plural = pynini.cross( - "cents", "00" - ) # Only used as terminus of hundred sequence. deux cents -> 200, deux cent un -> 201 - - graph_digit_no_one = pynini.project(pynini.union("un", "une"), 'input') - graph_digit_no_one = (pynini.project(graph_digit, "input") - graph_digit_no_one.arcsort()) @ graph_digit - - graph_hundreds_component_singular = ( - graph_digit_no_one + delete_hyphen + graph_cent_singular - ) # Regular way: [1-9] * 100 - - graph_hundreds_component_singular = pynini.union(graph_hundreds_component_singular, pynini.cross("cent", "1")) - graph_hundreds_component_singular += delete_hyphen - graph_hundreds_component_singular += graph_tens_component_with_leading_zeros - - graph_hundreds_component_plural = graph_digit_no_one + delete_hyphen + graph_cent_plural - - graph_hundreds_component = pynini.union( - graph_hundreds_component_singular, - graph_hundreds_component_plural, - pynutil.insert("0") + graph_tens_component_with_leading_zeros, - ) - - graph_hundreds_component_at_least_one_none_zero_digit = graph_hundreds_component @ ( - pynini.closure(NEMO_DIGIT) + (NEMO_DIGIT - "0") + pynini.closure(NEMO_DIGIT) - ) - self.graph_hundreds_component_at_least_one_none_zero_digit = rewrite( - graph_hundreds_component_at_least_one_none_zero_digit - ).optimize() - - # Graph thousands (we'll need this for cases of mille millions, mille milliards...) - graph_tens_of_hundreds_component_singular = ( - graph_tens_component + delete_hyphen + graph_cent_singular - ) # Tens of hundreds. e.g. 1900 = nineteen hundred/ 'dix neuf cents" - graph_tens_of_hundreds_component_singular += delete_hyphen + graph_tens_component_with_leading_zeros - graph_tens_of_hundreds_component_plural = graph_tens_component + delete_hyphen + graph_cent_plural - graph_tens_of_hundred_component = ( - graph_tens_of_hundreds_component_plural | graph_tens_of_hundreds_component_singular - ) - - graph_thousands = pynini.union( - graph_hundreds_component_at_least_one_none_zero_digit + delete_hyphen + pynutil.delete("mille"), - pynutil.insert("001") + pynutil.delete("mille"), # because 'mille', not 'un mille' - pynutil.insert("000", weight=0.1), - ) - - # All other large amounts - graph_millions = pynini.union( - graph_hundreds_component_at_least_one_none_zero_digit - + delete_hyphen - + (pynutil.delete("million") | pynutil.delete("millions")), - pynutil.insert("000", weight=0.1), - ) - - graph_milliards = pynini.union( # French for English 'billion' - graph_hundreds_component_at_least_one_none_zero_digit - + delete_hyphen - + (pynutil.delete("milliard") | pynutil.delete("milliards")), - pynutil.insert("000", weight=0.1), - ) - - graph_billions = pynini.union( # NOTE: this is English 'trillion.' - graph_hundreds_component_at_least_one_none_zero_digit - + delete_hyphen - + (pynutil.delete("billions") | pynutil.delete("billion")), - pynutil.insert("000", weight=0.1), - ) - - graph_mille_billion = pynini.union( - graph_hundreds_component_at_least_one_none_zero_digit + delete_hyphen + pynutil.delete("mille"), - pynutil.insert("001") + pynutil.delete("mille"), # because we say 'mille', not 'un mille' - ) - graph_mille_billion += delete_hyphen + ( - graph_millions | pynutil.insert("000") + pynutil.delete("billions") - ) # allow for 'mil millones' - graph_mille_billion |= pynutil.insert("000000", weight=0.1) - - graph_billiards = pynini.union( - graph_hundreds_component_at_least_one_none_zero_digit - + delete_hyphen - + (pynutil.delete("billiards") | pynutil.delete("billiard")), - pynutil.insert("000", weight=0.1), - ) - - graph_trillions = pynini.union( # One thousand English trillions. - graph_hundreds_component_at_least_one_none_zero_digit - + delete_hyphen - + (pynutil.delete("trillions") | pynutil.delete("trillion")), - pynutil.insert("000", weight=0.1), - ) - - graph_trilliards = pynini.union( - graph_hundreds_component_at_least_one_none_zero_digit - + delete_hyphen - + (pynutil.delete("trilliards") | pynutil.delete("trilliard")), - pynutil.insert("000", weight=0.1), - ) - - graph = pynini.union( - graph_trilliards - + delete_hyphen - + graph_trillions - + delete_hyphen - + graph_billiards - + delete_hyphen - + graph_billions - + delete_hyphen - + graph_milliards - + delete_hyphen - + graph_millions - + delete_hyphen - + graph_thousands - + delete_hyphen - + graph_hundreds_component, - graph_tens_of_hundred_component, - graph_zero, - ) - - graph = graph @ pynini.union( - pynutil.delete(pynini.closure("0")) + pynini.difference(NEMO_DIGIT, "0") + pynini.closure(NEMO_DIGIT), "0" - ) - - graph = rewrite(graph) - - self.graph_no_exception = graph.optimize() - - # save self.numbers_up_to_thousand for use in DecimalFst - digits_up_to_thousand = NEMO_DIGIT | (NEMO_DIGIT ** 2) | (NEMO_DIGIT ** 3) - numbers_up_to_thousand = pynini.compose(graph, digits_up_to_thousand).optimize() - self.numbers_up_to_thousand = numbers_up_to_thousand - - # save self.numbers_up_to_million for use in DecimalFst - digits_up_to_million = ( - NEMO_DIGIT - | (NEMO_DIGIT ** 2) - | (NEMO_DIGIT ** 3) - | (NEMO_DIGIT ** 4) - | (NEMO_DIGIT ** 5) - | (NEMO_DIGIT ** 6) - ) - numbers_up_to_million = pynini.compose(graph, digits_up_to_million).optimize() - self.numbers_up_to_million = numbers_up_to_million - - # don't convert cardinals from zero to nine inclusive - graph_exception = pynini.project(pynini.union(graph_digit, graph_zero), 'input') - - self.graph = (pynini.project(graph, "input") - graph_exception.arcsort()) @ graph - - optional_minus_graph = pynini.closure( - pynutil.insert("negative: ") + pynini.cross("moins", "\"-\"") + NEMO_SPACE, 0, 1 - ) - - final_graph = optional_minus_graph + pynutil.insert("integer: \"") + self.graph + pynutil.insert("\"") - - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/fr/taggers/date.py b/nemo_text_processing/inverse_text_normalization/fr/taggers/date.py deleted file mode 100644 index 1412111b42b2..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/taggers/date.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import GraphFst, delete_extra_space -from nemo_text_processing.inverse_text_normalization.fr.utils import get_abs_path -from pynini.lib import pynutil - - -class DateFst(GraphFst): - """ - Finite state transducer for classifying date, in the form of (day) month (year) or year - e.g. le vingt-quatre juillet deux-mille-treize -> date { day: "24" month: "juli" year: "2013" preserve_order: true } - e.g. le vingt-quatre juillet deux-mille-treize -> date { day: "24" month: "juli" year: "2013" preserve_order: true } - e.g. le premier janvier -> date { day: "1" month: "janvier" preserve_order: true } - - Also will convert colloquialism of spelling in which tens of hundreds are used to express date. (e.g. nineteen hundred and four) - e.g. le vingt mais dix-neuf-cent-quatre -> date { day: "20" month: "mais" year: "1904" preserve_order: true } - - Args: - cardinal: CardinalFst - """ - - def __init__(self, cardinal: GraphFst): - super().__init__(name="date", kind="classify") - - self.cardinal = cardinal.graph_no_exception - - year_graph = self.cardinal - - month_graph = pynini.string_file(get_abs_path("data/months.tsv")) - month_graph = pynutil.insert("month: \"") + month_graph + pynutil.insert("\"") - - day_graph = self.cardinal | pynini.cross("premier", "1") # Premier is only ordinal used for dates - day_graph = pynutil.insert("day: \"") + day_graph + pynutil.insert("\"") - optional_graph_year = pynini.closure( - delete_extra_space + pynutil.insert("year: \"") + year_graph + pynutil.insert("\""), 0, 1, - ) - graph_dmy = day_graph + delete_extra_space + month_graph + optional_graph_year - - final_graph = graph_dmy - final_graph += pynutil.insert(" preserve_order: true") - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/fr/taggers/decimal.py b/nemo_text_processing/inverse_text_normalization/fr/taggers/decimal.py deleted file mode 100644 index 035d70fab857..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/taggers/decimal.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import ( - NEMO_DIGIT, - GraphFst, - delete_extra_space, - delete_hyphen, - delete_space, -) -from nemo_text_processing.inverse_text_normalization.fr.utils import get_abs_path -from pynini.lib import pynutil - - -def get_quantity(decimal: 'pynini.FstLike', cardinal_up_to_thousand: 'pynini.FstLike') -> 'pynini.FstLike': - """ - Returns FST that transforms either a cardinal or decimal followed by a quantity into a numeral, - e.g. one million -> integer_part: "1" quantity: "million" - e.g. one point five million -> integer_part: "1" fractional_part: "5" quantity: "million" - - Will tag cases up to denominations of tens of hundreds of thousand. 'douze cent mille millions' -> 1 200 000 millions - - Args: - decimal: decimal FST - cardinal_up_to_million: cardinal FST - """ - numbers = cardinal_up_to_thousand @ ( - pynutil.delete(pynini.closure("0")) + pynini.difference(NEMO_DIGIT, "0") + pynini.closure(NEMO_DIGIT) - ) - - suffix = pynini.union( - "million", - "millions", - "milliard", - "milliards", - "billion", - "billions", - "billiard", - "billiards", - "trillion", - "trillions", - "trilliard", - "trilliards", - ) - res = ( - pynutil.insert("integer_part: \"") - + numbers - + pynutil.insert("\"") - + ( - pynini.union(delete_hyphen, delete_extra_space) - ) # Can be written either as 'deux-millions' or 'deux millions' depending on whether it registers as a noun or part of cardinal. - + pynutil.insert(" quantity: \"") - + suffix - + pynutil.insert("\"") - ) - res |= decimal + delete_extra_space + pynutil.insert(" quantity: \"") + suffix + pynutil.insert("\"") - return res - - -class DecimalFst(GraphFst): - """ - Finite state transducer for classifying decimal - Decimal point is "," (virgule). - e.g. moins un virgule deux six -> decimal { negative: "true" integer_part: "1" fractional_part: "26" } - - This decimal rule assumes that decimals can be pronounced as: - (a cardinal) + ('virgule') plus (any sequence of cardinals <1 million, including 'zero') - - Also writes large numbers in shortened form, e.g. - e.g. un virgule deux-six-million -> decimal { negative: "false" integer_part: "1" fractional_part: "26" quantity: "million" } - e.g. deux-million -> decimal { negative: "false" integer_part: "2" quantity: "millions" } - e.g. moins cent-vingt-quatre-millions -> decimal { negative: "true" integer_part: "124" quantity: "millions" } - Args: - cardinal: CardinalFst - - """ - - def __init__(self, cardinal: GraphFst): - super().__init__(name="decimal", kind="classify") - - # number after decimal point can be any series of cardinals <1 million, including 'zero' - graph_decimal = cardinal.numbers_up_to_million - graph_decimal = pynini.closure(graph_decimal + delete_space) + graph_decimal - self.graph = graph_decimal - - # decimal point is denote by virgule - graph_fractional_separator = pynutil.delete("virgule") - - # Possible negatives - optional_graph_negative = pynutil.insert("negative: ") + pynini.cross("moins", "\"true\"") + delete_extra_space - optional_graph_negative = optional_graph_negative.ques - - # Fractional portion - graph_fractional = pynutil.insert("fractional_part: \"") + graph_decimal + pynutil.insert("\"") - - # Integers - cardinal_graph = cardinal.graph_no_exception | pynini.string_file(get_abs_path("data/numbers/zero.tsv")) - graph_integer = pynutil.insert("integer_part: \"") + cardinal_graph + pynutil.insert("\"") - - # Final graphs - final_graph_wo_sign = ( - pynini.closure(graph_integer + delete_extra_space, 0, 1) - + graph_fractional_separator - + delete_extra_space - + graph_fractional - ) - final_graph = optional_graph_negative + final_graph_wo_sign - - self.final_graph_wo_negative = final_graph_wo_sign | get_quantity( - final_graph_wo_sign, cardinal.graph_hundreds_component_at_least_one_none_zero_digit - ) - final_graph |= optional_graph_negative + get_quantity( - final_graph_wo_sign, cardinal.graph_hundreds_component_at_least_one_none_zero_digit - ) - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/fr/taggers/electronic.py b/nemo_text_processing/inverse_text_normalization/fr/taggers/electronic.py deleted file mode 100644 index 15d82665001a..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/taggers/electronic.py +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import NEMO_ALPHA, GraphFst, insert_space -from nemo_text_processing.inverse_text_normalization.fr.utils import get_abs_path -from pynini.lib import pynutil - - -class ElectronicFst(GraphFst): - """ - Finite state transducer for classifying 'electronic' semiotic classes, i.e. - email address (which get converted to "username" and "domain" fields), - and URLS (which get converted to a "protocol" field). - e.g. c d f une arobase a b c point e d u -> tokens { electronic { username: "cdf1" domain: "abc.edu" } } - e.g. double vé double vé double vé a b c point e d u -> tokens { electronic { protocol: "www.abc.edu" } } - """ - - def __init__(self): - super().__init__(name="electronic", kind="classify") - - delete_extra_space = pynutil.delete(" ") - alpha_num = ( - NEMO_ALPHA - | pynini.string_file(get_abs_path("data/numbers/digit.tsv")) - | pynini.string_file(get_abs_path("data/numbers/zero.tsv")) - ) - - symbols = pynini.string_file(get_abs_path("data/electronic/symbols.tsv")) - ampersand = pynini.string_map([("arobase"), ("chez"), ("at"), ("à")]) - - accepted_username = alpha_num | symbols - process_dot = pynini.cross("point", ".") - username = ( - pynutil.insert("username: \"") - + alpha_num - + delete_extra_space - + pynini.closure(accepted_username + delete_extra_space) - + alpha_num - + pynutil.insert("\"") - ) - single_alphanum = pynini.closure(alpha_num + delete_extra_space) + alpha_num - server = single_alphanum | pynini.string_file(get_abs_path("data/electronic/server_name.tsv")) - domain = single_alphanum | pynini.string_file(get_abs_path("data/electronic/domain.tsv")) - domain_graph = ( - pynutil.insert("domain: \"") - + server - + delete_extra_space - + process_dot - + delete_extra_space - + domain - + pynutil.insert("\"") - ) - graph = ( - username - + delete_extra_space - + pynutil.delete(ampersand) - + insert_space - + delete_extra_space - + domain_graph - ) - - ############# url ### - protocol_end = pynini.cross(pynini.union("www", "w w w", "double vé double vé double vé"), "www") - protocol_start = pynini.cross(pynini.union("http", "h t t p", "ache té té pé"), "http") - protocol_start |= pynini.cross(pynini.union("https", "h t t p s", "ache té té pé esse"), "https") - protocol_start += pynini.cross( - pynini.union( - " deux-points barre oblique barre oblique ", - " deux-points barre barre ", - " deux-points double barre ", - " deux-points slash slash ", - ), - "://", - ) - - # e.g. .com, .es - ending = ( - delete_extra_space - + symbols - + delete_extra_space - + (domain | pynini.closure(accepted_username + delete_extra_space) + accepted_username) - ) - - protocol = ( - pynini.closure(protocol_start, 0, 1) - + protocol_end - + delete_extra_space - + process_dot - + delete_extra_space - + (pynini.closure(delete_extra_space + accepted_username, 1) | server) - + pynini.closure(ending, 1) - ) - protocol = pynutil.insert("protocol: \"") + protocol + pynutil.insert("\"") - graph |= protocol - ######## - - final_graph = self.add_tokens(graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/fr/taggers/fraction.py b/nemo_text_processing/inverse_text_normalization/fr/taggers/fraction.py deleted file mode 100644 index e053237246ce..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/taggers/fraction.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import ( - NEMO_CHAR, - GraphFst, - delete_extra_space, - delete_space, -) -from nemo_text_processing.inverse_text_normalization.fr.utils import get_abs_path -from pynini.lib import pynutil - - -class FractionFst(GraphFst): - """ - Finite state transducer for classifying fraction - e.g. demi -> tokens { fraction { numerator: "1" denominator: "2" } } - e.g. un et demi -> tokens { fraction { integer_part: "1" numerator: "1" denominator: "2" } } - e.g. trois et deux centième -> tokens { fraction { integer_part: "3" numerator: "2" denominator: "100" } } - - Args: - cardinal: OrdinalFst - """ - - def __init__(self, cardinal: GraphFst): - super().__init__(name="fraction", kind="classify") - # integer_part # numerator # denominator - - graph_cardinal = cardinal.graph_no_exception - graph_strip_undo_root_change = pynini.string_file(get_abs_path("data/fractions.tsv")) # add in absolute path - - graph_strip_no_root_change = pynutil.delete("ième") # For no change to root - graph_strip_no_root_change += pynutil.delete("s").ques # for plurals - - graph_strip = graph_strip_no_root_change | graph_strip_undo_root_change - - self.fractional = ((pynini.closure(NEMO_CHAR) + graph_strip) @ graph_cardinal).optimize() - - integer = pynutil.insert("integer_part: \"") + graph_cardinal + pynutil.insert("\" ") - integer += delete_space - integer += pynutil.delete("et") # used to demarcate integer and fractional parts - - numerator = pynutil.insert("numerator: \"") + graph_cardinal + pynutil.insert("\"") - denominator = pynutil.insert(" denominator: \"") + self.fractional + pynutil.insert("\"") - - # Demi (half) can occur alone without explicit numerator. - graph_demi_component = pynutil.delete("demi") + pynutil.delete("e").ques + pynutil.delete("s").ques - graph_demi_component += pynutil.insert("numerator: \"1\" denominator: \"2\"") - - graph_fraction_component = numerator + delete_space + denominator - graph_fraction_component |= graph_demi_component - self.graph_fraction_component = graph_fraction_component - - graph = pynini.closure(integer + delete_space, 0, 1) + graph_fraction_component - graph = graph.optimize() - self.final_graph_wo_negative = graph - - optional_graph_negative = pynini.closure( - pynutil.insert("negative: ") + pynini.cross("moins", "\"true\"") + delete_extra_space, 0, 1 - ) - graph = optional_graph_negative + graph - final_graph = self.add_tokens(graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/fr/taggers/measure.py b/nemo_text_processing/inverse_text_normalization/fr/taggers/measure.py deleted file mode 100644 index d9636286c6fa..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/taggers/measure.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import ( - GraphFst, - delete_extra_space, - delete_space, - get_singulars, -) -from nemo_text_processing.inverse_text_normalization.fr.utils import get_abs_path -from pynini.lib import pynutil - - -class MeasureFst(GraphFst): - """ - Finite state transducer for classifying measure. Allows for plural form for unit. - e.g. moins onze kilogramme -> measure { negative: "true" cardinal { integer: "11" } units: "kg" } - e.g. trois heures -> measure { cardinal { integer: "3" } units: "h" } - e.g. demi gramme -> measure { fraction { numerator: "1" denominator: "2" } units: "g" } - - Args: - cardinal: CardinalFst - decimal: DecimalFst - fraction: FractionFst - """ - - def __init__(self, cardinal: GraphFst, decimal: GraphFst, fraction: GraphFst): - super().__init__(name="measure", kind="classify") - - cardinal_graph = cardinal.graph_no_exception - - graph_prefix = pynini.string_file(get_abs_path("data/measurements/magnitudes.tsv")) - graph_unit_singular = pynini.string_file(get_abs_path("data/measurements/measurements.tsv")) - - unit = get_singulars(graph_unit_singular) | graph_unit_singular - unit = graph_prefix.ques + unit - - optional_graph_negative = pynini.closure( - pynutil.insert("negative: ") + pynini.cross("moins", "\"true\"") + delete_extra_space, 0, 1 - ) - - unit_misc = pynutil.insert("/") + (pynutil.delete("par") | pynutil.delete("à")) + delete_space + unit - - unit = ( - pynutil.insert("units: \"") - + (unit | unit_misc | pynutil.add_weight(unit + delete_space + unit_misc, 0.01)) - + pynutil.insert("\"") - ) - - subgraph_decimal = ( - pynutil.insert("decimal { ") - + optional_graph_negative - + decimal.final_graph_wo_negative - + pynutil.insert(" }") - + delete_extra_space - + unit - ) - - subgraph_fraction = ( - pynutil.insert("fraction { ") - + optional_graph_negative - + fraction.final_graph_wo_negative - + pynutil.insert(" }") - + delete_extra_space - + unit - ) - - subgraph_cardinal = ( - pynutil.insert("cardinal { ") - + optional_graph_negative - + pynutil.insert("integer: \"") - + cardinal_graph - + pynutil.insert("\"") - + pynutil.insert(" }") - + delete_extra_space - + unit - ) - final_graph = subgraph_decimal | subgraph_cardinal | subgraph_fraction - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/fr/taggers/money.py b/nemo_text_processing/inverse_text_normalization/fr/taggers/money.py deleted file mode 100644 index 17f473aa2bb9..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/taggers/money.py +++ /dev/null @@ -1,134 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import ( - NEMO_DIGIT, - GraphFst, - delete_extra_space, - delete_space, -) -from nemo_text_processing.inverse_text_normalization.fr.utils import get_abs_path -from pynini.lib import pynutil - - -class MoneyFst(GraphFst): - """ - Finite state transducer for classifying money - e.g. douze euro cinq -> money { integer_part: "12" currency: "€" fractional_part: 05} - e.g. zéro euro cinq -> money { integer_part: "0" currency: "€" fractional_part: 05} - e.g. cinq centimes -> money { integer_part: "0" currency: "€" fractional_part: 05} - - Note, the currency symbol seems more common for exact amounts and quantities less than 'un million' - For 'round' quantities of >=million (milliard, billion), the symbol is dropped. This allows - use of the 'de' preposition. - e.g. cinq millions d'euros -> money { integer_part: "5" currency: "d'euros" fractional_part: 00} - e.g. un milliard d'euro -> money { integer_part: "5" currency: "d'euro" fractional_part: 00} - e.g. trois virgule trois millions d'euros -> money { integer_part: "3" currency: "d'euros" fractional_part: 3} - - Currency is included for uniform tagging. - - Args: - cardinal: CardinalFst - decimal: DecimalFst - """ - - def __init__(self, cardinal: GraphFst, decimal: GraphFst): - super().__init__(name="money", kind="classify") - # quantity, integer_part, fractional_part, currency - - # quantities - cardinal_graph = cardinal.graph_no_exception - graph_decimal = decimal.final_graph_wo_negative - - # Converts currency names to symbols - convert_currency_major = pynini.string_file( - get_abs_path("data/money/currency_major.tsv") - ) # major denominations - convert_currency_minor = pynini.string_file( - get_abs_path("data/money/currency_minor.tsv") - ) # minor denominations to major symbol. (e.g. 5 cents -> 0.05 $ ) - - accept_all_currency = (convert_currency_major | convert_currency_minor).project( - "input" - ) # recognizes all currencies - - # Graphs for large round amounts ('deux billiards d'euros', 'un milliard de dollars') - graph_de = pynini.union("de ", "des ", "d'") # the use of de/d'only occurs with round amounts - graph_currency_component_large_round_amounts = graph_de + accept_all_currency - graph_currency_component_large_round_amounts = ( - pynutil.insert(" currency: \"") + graph_currency_component_large_round_amounts + pynutil.insert("\"") - ) - - graph_money_large_round_amounts = ( - graph_decimal + delete_space - ) # graph_decimal includes tags and quantities already - graph_money_large_round_amounts += graph_currency_component_large_round_amounts - - # For standard currency - add_leading_zero_to_double_digit = (NEMO_DIGIT + NEMO_DIGIT) | (pynutil.insert("0") + NEMO_DIGIT) - - # Graphs integer denomination for large denominations (e.g. $) - graph_integer_component_major = pynutil.insert("integer_part: \"") + cardinal_graph + pynutil.insert("\"") - graph_integer_component_major += delete_space - - graph_currency_component_major = ( - pynutil.insert(" currency: \"") + convert_currency_major + pynutil.insert("\"") - ) - - graph_decimal_component_major = ( - delete_space - + pynutil.insert(" fractional_part: \"") - + (cardinal_graph @ add_leading_zero_to_double_digit) - + pynutil.insert("\"") - ) - - # Rare cases where 'et' will separate major and minor denominations. - delete_minor_currency = pynini.project(convert_currency_minor, "input") - delete_minor_currency = delete_extra_space + pynutil.delete(delete_minor_currency) - - delete_et = delete_extra_space + pynutil.delete("et") - - graph_money_major = ( - graph_integer_component_major - + graph_currency_component_major - + delete_et.ques - + graph_decimal_component_major.ques - + delete_minor_currency.ques - ) - - # For cases when only small denominations are used. - graph_integer_component_minor = pynutil.insert("integer_part: \"0\"") - - graph_decimal_component_minor = ( - pynutil.insert(" fractional_part: \"") - + (cardinal_graph @ add_leading_zero_to_double_digit) - + pynutil.insert("\"") - ) - graph_decimal_component_minor += delete_extra_space - - graph_currency_component_minor = ( - pynutil.insert(" currency: \"") + convert_currency_minor + pynutil.insert("\"") - ) - - graph_money_minor = ( - graph_integer_component_minor + graph_decimal_component_minor + graph_currency_component_minor - ) - - graph_money_standard_amounts = graph_money_major | graph_money_minor - - final_graph = graph_money_large_round_amounts | graph_money_standard_amounts - final_graph = self.add_tokens(final_graph) - - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/fr/taggers/ordinal.py b/nemo_text_processing/inverse_text_normalization/fr/taggers/ordinal.py deleted file mode 100644 index 29da19b85c0a..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/taggers/ordinal.py +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import NEMO_SIGMA, GraphFst, delete_space -from nemo_text_processing.inverse_text_normalization.fr.utils import get_abs_path -from pynini.lib import pynutil - - -class OrdinalFst(GraphFst): - """ - Finite state transducer for classifying ordinal - vingt-deuxième -> ordinal { integer: "22" morphosyntactic_features: "e" } - - Also notes specific nouns that have unique normalization conventions. - For instance, 'siècles' are rendered in roman numerals when given an ordinal adjective. - e.g. dix-neuvième siècle -> XIXe - - Args: - cardinal: CardinalFst - """ - - def __init__(self, cardinal: GraphFst): - super().__init__(name="ordinal", kind="classify") - - graph_cardinal = cardinal.graph_no_exception - graph_undo_root_change = pynini.string_file( - get_abs_path("data/ordinals/digits_root_change.tsv") - ) # Returns base number to normal after root change. - graph_firsts = pynini.string_file(get_abs_path("data/ordinals/firsts.tsv")) - graph_second = pynini.string_file(get_abs_path("data/ordinals/second.tsv")) - graph_special_ordinals = pynini.string_file(get_abs_path("data/ordinals/key_nouns.tsv")) - - # Removes morpheme - graph_no_root_change = pynutil.delete("ième") # For no change to root - - graph_strip_morpheme = pynini.union(graph_no_root_change, graph_undo_root_change) - graph_strip_morpheme = NEMO_SIGMA + graph_strip_morpheme - - graph_integer_component = graph_strip_morpheme @ graph_cardinal - - graph_morpheme_component = pynutil.insert("e") # Put the superscript in. - graph_morpheme_component += pynini.accep("s").ques # In case of plurals. - - # Concatenate with cardinal graph. - graph_ordinal = pynutil.insert("integer: \"") + graph_integer_component + pynutil.insert("\"") - graph_ordinal += ( - pynutil.insert(" morphosyntactic_features: \"") + graph_morpheme_component - ) # Leave open in case further morphems occur - - # Primer has a different subscript depending on gender, need to take note if - # 'premier' or 'première' - graph_firsts = pynutil.insert("integer: \"1\" morphosyntactic_features: \"") + graph_firsts - - # Second used 'd' as a superscript. - graph_second = pynutil.insert("integer: \"2\" morphosyntactic_features: \"") + graph_second - - graph = graph_firsts | graph_second | graph_ordinal - - # For roman numerals. Carries over designation to verbalizer - graph_special_ordinals = pynutil.insert("/") + delete_space + graph_special_ordinals - - graph += graph_special_ordinals.ques + pynutil.insert("\"") - - final_graph = self.add_tokens(graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/fr/taggers/punctuation.py b/nemo_text_processing/inverse_text_normalization/fr/taggers/punctuation.py deleted file mode 100644 index 458497aa6064..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/taggers/punctuation.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import GraphFst -from pynini.lib import pynutil - - -class PunctuationFst(GraphFst): - """ - Finite state transducer for classifying punctuation - e.g. a, -> tokens { name: "a" } tokens { name: "," } - """ - - def __init__(self): - super().__init__(name="punctuation", kind="classify") - - s = "!#$%&\'()*+,-./:;<=>?@^_`{|}~" - guillemets = "\u00AB" + "\u00BB" # quotation marks in French. - s += guillemets - punct = pynini.union(*s) - - graph = pynutil.insert("name: \"") + punct + pynutil.insert("\"") - - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/fr/taggers/telephone.py b/nemo_text_processing/inverse_text_normalization/fr/taggers/telephone.py deleted file mode 100644 index 9d18fb71f1f3..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/taggers/telephone.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import ( - GraphFst, - delete_hyphen, - delete_space, - insert_space, -) -from nemo_text_processing.inverse_text_normalization.fr.utils import get_abs_path -from pynini.lib import pynutil - - -class TelephoneFst(GraphFst): - """ - Finite state transducer for classifying telephone numbers. Assumes conventional grouping for Metropolitan France (and overseas departments) - (two number sequences are grouped as individual cardinals) or digit by digit (chiffre-par-chiffre) e.g. - "zero un quatre-vingt-deux zero deux vingt-deux cinquante" -> { number_part: "01 42 02 22 50" } - "zero un quatre deux zero deux deux deux cinq zero" -> { number_part: "01 42 02 22 50" } - - In cases where only one digit of the first pairing is admitted, assumes that the 0 was skipped. - "une vingt-trois quatre-vingt zero six dix-sept" -> { number_part: "01 23 40 06 17" } - """ - - def __init__(self): - super().__init__(name="telephone", kind="classify") - - # create `single_digits` and `double_digits` graphs as these will be - # the building blocks of possible telephone numbers - graph_digit = pynini.string_file(get_abs_path("data/numbers/digit.tsv")) - graph_ties = pynini.string_file(get_abs_path("data/numbers/ties.tsv")) - graph_ties_unique = pynini.string_file((get_abs_path("data/numbers/ties_unique.tsv"))) - graph_teen = pynini.string_file(get_abs_path("data/numbers/teen.tsv")) - graph_zero = pynini.string_file(get_abs_path("data/numbers/zero.tsv")) - - double_digits = pynini.union( - graph_teen, - graph_ties_unique, - (graph_ties + pynutil.insert("0")), - (graph_ties + delete_hyphen + graph_digit), - ) - - graph_first_pair = graph_zero + delete_space + graph_digit - graph_first_pair |= pynutil.insert("0") + graph_digit # if zero is omitted - graph_first_pair += ( - delete_space + insert_space - ) # delete_space since closure allows possible gaps to be removed - - # All digits - single_digits = graph_digit | graph_zero - - graph_pair_all_digits = single_digits + delete_space - graph_pair_all_digits += single_digits - - graph_all_digits = pynini.closure(graph_pair_all_digits + delete_space + insert_space, 3, 3) - graph_all_digits = graph_first_pair + graph_all_digits + graph_pair_all_digits - - # Paired digits - graph_pair_digits_and_ties = double_digits | graph_pair_all_digits - - graph_digits_and_ties = pynini.closure(graph_pair_digits_and_ties + delete_space + insert_space, 3, 3) - graph_digits_and_ties = graph_first_pair + graph_digits_and_ties + graph_pair_digits_and_ties - - number_part = pynini.union(graph_all_digits, graph_digits_and_ties) - - number_part = pynutil.insert("number_part: \"") + number_part + pynutil.insert("\"") - - graph = number_part - final_graph = self.add_tokens(graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/fr/taggers/time.py b/nemo_text_processing/inverse_text_normalization/fr/taggers/time.py deleted file mode 100644 index 499701a1ea8e..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/taggers/time.py +++ /dev/null @@ -1,115 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import GraphFst, delete_space -from nemo_text_processing.inverse_text_normalization.fr.utils import get_abs_path -from pynini.lib import pynutil - - -class TimeFst(GraphFst): - """ - Finite state transducer for classifying time - e.g. huit heures -> time { hours: "8" minutes: "00" } - e.g. treize heures -> time { hours: "13" minutes: "00" } - e.g. treize heures dix -> time { hours: "13" minutes: "10" } - e.g. huit heures du matin -> time { hours: "8" minutes: "00" suffix: "avant mid"} - e.g. huite heures du après midi -> time { hours: "8" minutes: "00" suffix: "après-midi"} - e.g. douze heures moins qart -> time { hours: "11" minutes: "45" } - e.g. douze heures et qart -> time { hours: "12" minutes: "15" } - e.g. midi et qart -> time { hours: "12" minutes: "15" } - e.g. minuit et medi -> time { hours: "0" minutes: "30" } - e.g. douze heures moins medi -> time { hours: "11" minutes: "30" } - e.g. douze heures moins trois -> time { hours: "11" minutes: "57" } - """ - - def __init__(self): - super().__init__(name="time", kind="classify") - # hours, minutes, seconds, suffix, zone, style, speak_period - - # time_zone = pynini.invert(pynini.string_file(get_abs_path("data/time/time_zone.tsv"))) - graph_hours_to = pynini.string_file(get_abs_path("data/time/hours_to.tsv")) - graph_minutes_to = pynini.string_file(get_abs_path("data/time/minutes_to.tsv")) - graph_hours = pynini.string_file(get_abs_path("data/time/hours.tsv")) - graph_minutes = pynini.string_file(get_abs_path("data/time/minutes.tsv")) - graph_suffix_am = pynini.string_file(get_abs_path("data/time/time_suffix_am.tsv")) - graph_suffix_pm = pynini.string_file(get_abs_path("data/time/time_suffix_pm.tsv")) - - graph_suffix = pynini.cross(graph_suffix_am, "am") | pynini.cross(graph_suffix_pm, "pm") - - # Mapping 'heures' - graph_heures = pynini.accep("heure") + pynini.accep("s").ques - graph_heures = pynutil.delete(graph_heures) - - graph_hours += delete_space + graph_heures - - # Midi and minuit - graph_midi = pynini.cross("midi", "12") - graph_minuit = pynini.cross("minuit", "0") - - # Mapping 'et demi' and 'et qart' - graph_et = pynutil.delete("et") + delete_space - - graph_demi = pynini.accep("demi") - graph_demi += pynini.accep("e").ques # people vary on feminine or masculine form - graph_demi = pynini.cross(graph_demi, "30") - - graph_quart = pynini.accep('quart') - graph_quart = pynini.accep("le ").ques + graph_quart # sometimes used - graph_quart = pynini.cross(graph_quart, '15') - graph_trois_quart = pynini.cross("trois quarts", "45") - - graph_fractions = pynini.union(graph_demi, graph_quart, graph_trois_quart) - - graph_et_fractions = graph_et + graph_fractions - - # Hours component is usually just a cardinal + 'heures' (ignored in case of 'midi/minuit'). - graph_hours_component = pynini.union(graph_hours, graph_midi, graph_minuit) - graph_hours_component = pynutil.insert("hours: \"") + graph_hours_component + pynutil.insert("\"") - graph_hours_component += delete_space - - # Minutes component - graph_minutes_component = ( - pynutil.insert(" minutes: \"") + pynini.union(graph_minutes, graph_et_fractions) + pynutil.insert("\"") - ) - - # Hour and minutes together. For 'demi' and 'qart', 'et' is used as a conjunction. - graph_time_standard = graph_hours_component + graph_minutes_component.ques - - # For time until hour. "quatre heures moins qart" -> 4 h 00 - 0 h 15 = 3 h 45 - graph_moins = pynutil.delete("moins") - graph_moins += delete_space - - graph_hours_to_component = graph_hours | graph_midi | graph_minuit - graph_hours_to_component @= graph_hours_to - graph_hours_to_component = pynutil.insert("hours: \"") + graph_hours_to_component + pynutil.insert("\"") - graph_hours_to_component += delete_space - - graph_minutes_to_component = pynini.union(graph_minutes, graph_fractions) - graph_minutes_to_component @= graph_minutes_to - graph_minutes_to_component = pynutil.insert(" minutes: \"") + graph_minutes_to_component + pynutil.insert("\"") - - graph_time_to = graph_hours_to_component + graph_moins + graph_minutes_to_component - - graph_time_no_suffix = graph_time_standard | graph_time_to - - graph_suffix_component = pynutil.insert(" suffix: \"") + graph_suffix + pynutil.insert("\"") - graph_suffix_component = delete_space + graph_suffix_component - graph_suffix_component = graph_suffix_component.ques - - final_graph = graph_time_no_suffix + graph_suffix_component - - final_graph = self.add_tokens(final_graph) - - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/fr/taggers/tokenize_and_classify.py b/nemo_text_processing/inverse_text_normalization/fr/taggers/tokenize_and_classify.py deleted file mode 100644 index 16b8f56f8d94..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/taggers/tokenize_and_classify.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import ( - GraphFst, - delete_extra_space, - delete_space, - generator_main, -) -from nemo_text_processing.inverse_text_normalization.fr.taggers.cardinal import CardinalFst -from nemo_text_processing.inverse_text_normalization.fr.taggers.date import DateFst -from nemo_text_processing.inverse_text_normalization.fr.taggers.decimal import DecimalFst -from nemo_text_processing.inverse_text_normalization.fr.taggers.electronic import ElectronicFst -from nemo_text_processing.inverse_text_normalization.fr.taggers.fraction import FractionFst -from nemo_text_processing.inverse_text_normalization.fr.taggers.measure import MeasureFst -from nemo_text_processing.inverse_text_normalization.fr.taggers.money import MoneyFst -from nemo_text_processing.inverse_text_normalization.fr.taggers.ordinal import OrdinalFst -from nemo_text_processing.inverse_text_normalization.fr.taggers.punctuation import PunctuationFst -from nemo_text_processing.inverse_text_normalization.fr.taggers.telephone import TelephoneFst -from nemo_text_processing.inverse_text_normalization.fr.taggers.time import TimeFst -from nemo_text_processing.inverse_text_normalization.fr.taggers.whitelist import WhiteListFst -from nemo_text_processing.inverse_text_normalization.fr.taggers.word import WordFst -from pynini.lib import pynutil - -from nemo.utils import logging - - -class ClassifyFst(GraphFst): - """ - Final class that composes all other classification grammars. This class can process an entire sentence, that is lower cased. - For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - - Args: - cache_dir: path to a dir with .far grammar file. Set to None to avoid using cache. - overwrite_cache: set to True to overwrite .far files - """ - - def __init__(self, cache_dir: str = None, overwrite_cache: bool = False): - super().__init__(name="tokenize_and_classify", kind="classify") - - far_file = None - if cache_dir is not None and cache_dir != "None": - os.makedirs(cache_dir, exist_ok=True) - far_file = os.path.join(cache_dir, "_fr_itn.far") - if not overwrite_cache and far_file and os.path.exists(far_file): - self.fst = pynini.Far(far_file, mode="r")["tokenize_and_classify"] - logging.info(f"ClassifyFst.fst was restored from {far_file}.") - else: - logging.info(f"Creating ClassifyFst grammars.") - - cardinal = CardinalFst() - cardinal_graph = cardinal.fst - - fraction = FractionFst(cardinal) - fraction_graph = fraction.fst - - ordinal = OrdinalFst(cardinal) - ordinal_graph = ordinal.fst - - decimal = DecimalFst(cardinal) - decimal_graph = decimal.fst - - measure_graph = MeasureFst(cardinal=cardinal, decimal=decimal, fraction=fraction).fst - date_graph = DateFst(cardinal).fst - word_graph = WordFst().fst - time_graph = TimeFst().fst - money_graph = MoneyFst(cardinal, decimal).fst - whitelist_graph = WhiteListFst().fst - punct_graph = PunctuationFst().fst - electronic_graph = ElectronicFst().fst - telephone_graph = TelephoneFst().fst - - classify = ( - pynutil.add_weight(whitelist_graph, 1.01) - | pynutil.add_weight(time_graph, 1.05) - | pynutil.add_weight(date_graph, 1.09) - | pynutil.add_weight(decimal_graph, 1.08) - | pynutil.add_weight(measure_graph, 1.1) - | pynutil.add_weight(cardinal_graph, 1.1) - | pynutil.add_weight(ordinal_graph, 1.1) - | pynutil.add_weight(fraction_graph, 1.09) - | pynutil.add_weight(money_graph, 1.07) - | pynutil.add_weight(telephone_graph, 1.1) - | pynutil.add_weight(electronic_graph, 1.1) - | pynutil.add_weight(word_graph, 100) - ) - - punct = pynutil.insert("tokens { ") + pynutil.add_weight(punct_graph, weight=1.1) + pynutil.insert(" }") - token = pynutil.insert("tokens { ") + classify + pynutil.insert(" }") - token_plus_punct = ( - pynini.closure(punct + pynutil.insert(" ")) + token + pynini.closure(pynutil.insert(" ") + punct) - ) - - graph = token_plus_punct + pynini.closure(delete_extra_space + token_plus_punct) - graph = delete_space + graph + delete_space - - self.fst = graph.optimize() - - if far_file: - generator_main(far_file, {"tokenize_and_classify": self.fst}) - logging.info(f"ClassifyFst grammars are saved to {far_file}.") diff --git a/nemo_text_processing/inverse_text_normalization/fr/taggers/whitelist.py b/nemo_text_processing/inverse_text_normalization/fr/taggers/whitelist.py deleted file mode 100644 index 54826162c07a..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/taggers/whitelist.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import GraphFst, convert_space -from nemo_text_processing.inverse_text_normalization.fr.utils import get_abs_path -from pynini.lib import pynutil - - -class WhiteListFst(GraphFst): - """ - Finite state transducer for classifying whitelisted tokens - e.g. misses -> tokens { name: "mrs." } - This class has highest priority among all classifier grammars. Whitelisted tokens are defined and loaded from "data/whitelist.tsv". - """ - - def __init__(self): - super().__init__(name="whitelist", kind="classify") - - whitelist = pynini.string_file(get_abs_path("data/whitelist.tsv")) - graph = pynutil.insert("name: \"") + convert_space(whitelist) + pynutil.insert("\"") - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/fr/taggers/word.py b/nemo_text_processing/inverse_text_normalization/fr/taggers/word.py deleted file mode 100644 index 95d05ce6aa7a..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/taggers/word.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import NEMO_NOT_SPACE, GraphFst -from pynini.lib import pynutil - - -class WordFst(GraphFst): - """ - Finite state transducer for classifying plain tokens, that do not belong to any special class. This can be considered as the default class. - e.g. sleep -> tokens { name: "sleep" } - """ - - def __init__(self): - super().__init__(name="word", kind="classify") - word = pynutil.insert("name: \"") + pynini.closure(NEMO_NOT_SPACE, 1) + pynutil.insert("\"") - self.fst = word.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/fr/utils.py b/nemo_text_processing/inverse_text_normalization/fr/utils.py deleted file mode 100644 index f6e06f793c07..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/utils.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - - -def get_abs_path(rel_path): - """ - Get absolute path - - Args: - rel_path: relative path to this file - - Returns absolute path - """ - return os.path.dirname(os.path.abspath(__file__)) + '/' + rel_path diff --git a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/__init__.py b/nemo_text_processing/inverse_text_normalization/fr/verbalizers/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/cardinal.py b/nemo_text_processing/inverse_text_normalization/fr/verbalizers/cardinal.py deleted file mode 100644 index 4aca2bac2c9f..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/cardinal.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class CardinalFst(GraphFst): - """ - Finite state transducer for verbalizing cardinal - e.g. cardinal { negative: "-" integer: "23" } -> -23 - """ - - def __init__(self): - super().__init__(name="cardinal", kind="verbalize") - optional_sign = pynini.closure( - pynutil.delete("negative:") - + delete_space - + pynutil.delete("\"") - + NEMO_NOT_QUOTE - + pynutil.delete("\"") - + delete_space, - 0, - 1, - ) - graph = ( - pynutil.delete("integer:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - self.numbers = graph - graph = optional_sign + graph - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/date.py b/nemo_text_processing/inverse_text_normalization/fr/verbalizers/date.py deleted file mode 100644 index 47304c1e1be0..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/date.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import ( - NEMO_NOT_QUOTE, - GraphFst, - delete_extra_space, - delete_space, -) -from pynini.lib import pynutil - - -class DateFst(GraphFst): - """ - Finite state transducer for verbalizing date, e.g. - date { day: "1" month: "janvier" preserve_order: true } -> 1 de enero - """ - - def __init__(self): - super().__init__(name="date", kind="verbalize") - - convert_primer = pynini.cross('1', '1ᵉʳ') - day = ( - pynutil.delete("day:") - + delete_space - + pynutil.delete("\"") - + ( - pynini.closure(NEMO_NOT_QUOTE, 1) | pynutil.add_weight(convert_primer, -1) - ) # first of the month is ordinal - + pynutil.delete("\"") - ) - month = ( - pynutil.delete("month:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - year = ( - pynutil.delete("year:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - - # day month - graph_dm = day + delete_extra_space + month - graph_dmy = graph_dm + delete_extra_space + year - - optional_preserve_order = pynini.closure( - pynutil.delete("preserve_order:") + delete_space + pynutil.delete("true") + delete_space - | pynutil.delete("field_order:") - + delete_space - + pynutil.delete("\"") - + NEMO_NOT_QUOTE - + pynutil.delete("\"") - + delete_space - ) - - final_graph = (graph_dm | graph_dmy) + delete_space + optional_preserve_order - - delete_tokens = self.delete_tokens(final_graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/decimal.py b/nemo_text_processing/inverse_text_normalization/fr/verbalizers/decimal.py deleted file mode 100644 index dc00842cd1c7..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/decimal.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import ( - NEMO_DIGIT, - NEMO_NON_BREAKING_SPACE, - NEMO_NOT_QUOTE, - GraphFst, - delete_space, -) -from pynini.lib import pynutil - - -class NumberParser(GraphFst): - """ - Finite state transducer for parsing strings of digis. Breaks up digit strings into groups of three for - strings of digits of four or more (inclusive). Groupings are separated by non-breaking space. - e.g. '1000' -> '1 000' - e.g. '1000,33333' -> '1 000,333 33 - """ - - def __init__(self): - super().__init__(name="parser", kind="verbalize") - - -class DecimalFst(GraphFst): - """ - Finite state transducer for verbalizing decimal, e.g. - decimal { negative: "true" integer_part: "12" fractional_part: "5006" quantity: "billion" } -> -12.5006 billion - """ - - def __init__(self): - super().__init__(name="decimal", kind="verbalize") - - # Need parser to group digits by threes - exactly_three_digits = NEMO_DIGIT ** 3 - at_most_three_digits = pynini.closure(NEMO_DIGIT, 1, 3) - - space_every_three_integer = ( - at_most_three_digits + (pynutil.insert(NEMO_NON_BREAKING_SPACE) + exactly_three_digits).closure() - ) - space_every_three_decimal = ( - pynini.accep(",") - + (exactly_three_digits + pynutil.insert(NEMO_NON_BREAKING_SPACE)).closure() - + at_most_three_digits - ) - group_by_threes = space_every_three_integer | space_every_three_decimal - self.group_by_threes = group_by_threes - - optional_sign = pynini.closure(pynini.cross("negative: \"true\"", "-") + delete_space, 0, 1) - integer = ( - pynutil.delete("integer_part:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - integer = integer @ group_by_threes - optional_integer = pynini.closure(integer + delete_space, 0, 1) - fractional = ( - pynutil.insert(",") - + pynutil.delete("fractional_part:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - fractional = fractional @ group_by_threes - optional_fractional = pynini.closure(fractional + delete_space, 0, 1) - quantity = ( - pynutil.delete("quantity:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - optional_quantity = pynini.closure(pynutil.insert(" ") + quantity + delete_space, 0, 1) - graph = (optional_integer + optional_fractional + optional_quantity).optimize() - self.numbers = graph - graph = optional_sign + graph - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/electronic.py b/nemo_text_processing/inverse_text_normalization/fr/verbalizers/electronic.py deleted file mode 100644 index e4cc75cbd6ef..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/electronic.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class ElectronicFst(GraphFst): - """ - Finite state transducer for verbalizing electronic - e.g. tokens { electronic { username: "cdf1" domain: "abc.edu" } } -> cdf1@abc.edu - """ - - def __init__(self): - super().__init__(name="electronic", kind="verbalize") - user_name = ( - pynutil.delete("username:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - domain = ( - pynutil.delete("domain:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - - graph = user_name + delete_space + pynutil.insert("@") + domain - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/fraction.py b/nemo_text_processing/inverse_text_normalization/fr/verbalizers/fraction.py deleted file mode 100644 index dce25d9497c8..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/fraction.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import ( - NEMO_NOT_QUOTE, - GraphFst, - delete_space, - insert_space, -) -from pynini.lib import pynutil - - -class FractionFst(GraphFst): - """ - Finite state transducer for verbalizing fraction - e.g. fraction { integer_part: "1" numerator: "2" denominator: "3" } } -> 1 2/3 - - """ - - def __init__(self): - super().__init__(name="fraction", kind="verbalize") - optional_sign = pynini.closure(pynini.cross("negative: \"true\"", "-") + delete_space, 0, 1) - integer = ( - pynutil.delete("integer_part: \"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - + insert_space - ) - numerator = pynutil.delete("numerator: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - - denominator = ( - pynutil.insert('/') - + pynutil.delete("denominator: \"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - - graph = (pynini.closure(integer + delete_space, 0, 1) + numerator + delete_space + denominator).optimize() - self.numbers = graph - delete_tokens = self.delete_tokens(optional_sign + graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/measure.py b/nemo_text_processing/inverse_text_normalization/fr/verbalizers/measure.py deleted file mode 100644 index ad0f2f98c1b0..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/measure.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import NEMO_CHAR, GraphFst, delete_space -from pynini.lib import pynutil - - -class MeasureFst(GraphFst): - """ - Finite state transducer for verbalizing measure, e.g. - measure { negative: "true" cardinal { integer: "12" } units: "kg" } -> -12 kg - - Args: - decimal: DecimalFst - cardinal: CardinalFst - fraction: FractionFst - """ - - def __init__(self, decimal: GraphFst, cardinal: GraphFst, fraction: GraphFst): - super().__init__(name="measure", kind="verbalize") - optional_sign = pynini.closure(pynini.cross("negative: \"true\"", "-"), 0, 1) - unit = ( - pynutil.delete("units:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_CHAR - " ", 1) - + pynutil.delete("\"") - + delete_space - ) - graph_decimal = ( - pynutil.delete("decimal {") - + delete_space - + optional_sign - + delete_space - + decimal.numbers - + delete_space - + pynutil.delete("}") - ) - graph_cardinal = ( - pynutil.delete("cardinal {") - + delete_space - + optional_sign - + delete_space - + cardinal.numbers @ decimal.group_by_threes # measurements most obey three by three spacing - + delete_space - + pynutil.delete("}") - ) - graph_fraction = ( - pynutil.delete("fraction {") - + delete_space - + optional_sign - + delete_space - + fraction.numbers - + delete_space - + pynutil.delete("}") - ) - - graph = (graph_cardinal | graph_decimal | graph_fraction) + delete_space + pynutil.insert(" ") + unit - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/money.py b/nemo_text_processing/inverse_text_normalization/fr/verbalizers/money.py deleted file mode 100644 index db47256841a7..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/money.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import ( - NEMO_NOT_QUOTE, - GraphFst, - delete_extra_space, - delete_space, -) -from pynini.lib import pynutil - - -class MoneyFst(GraphFst): - """ - Finite state transducer for verbalizing money, e.g. - money { integer_part: "12" fractional_part: "05" currency: "$" } -> 12.05 $ - - Args: - decimal: DecimalFst - """ - - def __init__(self, decimal: GraphFst): - super().__init__(name="money", kind="verbalize") - unit = ( - pynutil.delete("currency:") - + delete_extra_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - graph = decimal.numbers + delete_space + unit - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/ordinal.py b/nemo_text_processing/inverse_text_normalization/fr/verbalizers/ordinal.py deleted file mode 100644 index 96d8c24030d2..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/ordinal.py +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import ( - NEMO_DIGIT, - NEMO_NOT_QUOTE, - GraphFst, - delete_space, -) -from nemo_text_processing.inverse_text_normalization.fr.utils import get_abs_path -from pynini.lib import pynutil - - -class OrdinalFst(GraphFst): - """ - Finite state transducer for verbalizing ordinal, e.g. - ordinal { integer: "13" morphosyntactic_features: "e" } -> 13ᵉ - - Given 'special' terms for ordinals (e.g. siècle), renders - amount in conventional format. e.g. - - ordinal { integer: "13" morphosyntactic_features: "e/siècle" } -> XIIIᵉ - """ - - def __init__(self): - super().__init__(name="ordinal", kind="verbalize") - graph_integer = ( - pynutil.delete("integer:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - - replace_suffix = pynini.union( - pynini.cross("e", "ᵉ"), # only delete first quote since there may be more features - pynini.cross("d", "ᵈ"), - pynini.cross("r", "ʳ"), - pynini.cross("s", "ˢ"), - ) - replace_suffix = pynutil.delete(" morphosyntactic_features: \"") + replace_suffix.plus - - graph_arabic = graph_integer + replace_suffix.plus - - # For roman. - graph_roman_digits = pynini.string_file(get_abs_path("data/roman/digits_large.tsv")).invert() - graph_roman_ties = pynini.string_file(get_abs_path("data/roman/ties_large.tsv")).invert() - graph_roman_hundreds = pynini.string_file(get_abs_path("data/roman/hundreds_large.tsv")).invert() - graph_roman_zero_digit = pynutil.delete("0") - - graph_roman_hundreds = NEMO_DIGIT ** 3 @ ( - graph_roman_hundreds - + pynini.union(graph_roman_ties, graph_roman_zero_digit) - + pynini.union(graph_roman_digits, graph_roman_zero_digit) - ) - graph_roman_ties = NEMO_DIGIT ** 2 @ ( - graph_roman_ties + pynini.union(graph_roman_digits, graph_roman_zero_digit) - ) - graph_roman_digits = NEMO_DIGIT @ graph_roman_digits - - graph_roman_integers = graph_roman_hundreds | graph_roman_ties | graph_roman_digits - - graph_roman = (graph_integer @ graph_roman_integers) + replace_suffix - graph_roman += pynini.cross("/", " ") + "siècle" - - graph = (graph_roman | graph_arabic) + pynutil.delete("\"") - - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/telephone.py b/nemo_text_processing/inverse_text_normalization/fr/verbalizers/telephone.py deleted file mode 100644 index c204bcc3897d..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/telephone.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import NEMO_NOT_QUOTE, GraphFst -from pynini.lib import pynutil - - -class TelephoneFst(GraphFst): - """ - Finite state transducer for verbalizing telephone, e.g. - telephone { number_part: "02 33 43 53 22" } - -> 02 33 43 53 22 - """ - - def __init__(self): - super().__init__(name="telephone", kind="verbalize") - - number_part = pynutil.delete("number_part: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - delete_tokens = self.delete_tokens(number_part) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/time.py b/nemo_text_processing/inverse_text_normalization/fr/verbalizers/time.py deleted file mode 100644 index 93e4886e58ed..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/time.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import ( - NEMO_DIGIT, - GraphFst, - delete_extra_space, - delete_space, -) -from nemo_text_processing.inverse_text_normalization.fr.utils import get_abs_path -from pynini.lib import pynutil - - -class TimeFst(GraphFst): - """ - Finite state transducer for verbalizing time, e.g. - time { hours: "8" minutes: "30" suffix: "du matin"} -> 8 h 30 - time { hours: "8" minutes: "30" } -> 8 h 30 - time { hours: "8" minutes: "30" suffix: "du soir"} -> 20 h 30 - """ - - def __init__(self): - super().__init__(name="time", kind="verbalize") - - hour_to_night = pynini.string_file(get_abs_path("data/time/hour_to_night.tsv")) - - day_suffixes = pynutil.delete("suffix: \"am\"") - night_suffixes = pynutil.delete("suffix: \"pm\"") - - hour = ( - pynutil.delete("hours:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_DIGIT, 1, 2) - + pynutil.delete("\"") - ) - minute = ( - pynutil.delete("minutes:") - + delete_extra_space - + pynutil.delete("\"") - + pynini.closure(NEMO_DIGIT, 1, 2) - + pynutil.delete("\"") - ) - - graph = hour + delete_extra_space + pynutil.insert("h") + minute.ques + delete_space + day_suffixes.ques - - graph |= ( - hour @ hour_to_night - + delete_extra_space - + pynutil.insert("h") - + minute.ques - + delete_space - + night_suffixes - ) - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/verbalize.py b/nemo_text_processing/inverse_text_normalization/fr/verbalizers/verbalize.py deleted file mode 100644 index 9eec8eff90b8..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/verbalize.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import GraphFst -from nemo_text_processing.inverse_text_normalization.fr.verbalizers.cardinal import CardinalFst -from nemo_text_processing.inverse_text_normalization.fr.verbalizers.date import DateFst -from nemo_text_processing.inverse_text_normalization.fr.verbalizers.decimal import DecimalFst -from nemo_text_processing.inverse_text_normalization.fr.verbalizers.electronic import ElectronicFst -from nemo_text_processing.inverse_text_normalization.fr.verbalizers.fraction import FractionFst -from nemo_text_processing.inverse_text_normalization.fr.verbalizers.measure import MeasureFst -from nemo_text_processing.inverse_text_normalization.fr.verbalizers.money import MoneyFst -from nemo_text_processing.inverse_text_normalization.fr.verbalizers.ordinal import OrdinalFst -from nemo_text_processing.inverse_text_normalization.fr.verbalizers.telephone import TelephoneFst -from nemo_text_processing.inverse_text_normalization.fr.verbalizers.time import TimeFst -from nemo_text_processing.inverse_text_normalization.fr.verbalizers.whitelist import WhiteListFst - - -class VerbalizeFst(GraphFst): - """ - Composes other verbalizer grammars. - For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - """ - - def __init__(self): - super().__init__(name="verbalize", kind="verbalize") - cardinal = CardinalFst() - cardinal_graph = cardinal.fst - ordinal_graph = OrdinalFst().fst - decimal = DecimalFst() - decimal_graph = decimal.fst - fraction = FractionFst() - fraction_graph = fraction.fst - measure_graph = MeasureFst(decimal=decimal, cardinal=cardinal, fraction=fraction).fst - money_graph = MoneyFst(decimal=decimal).fst - time_graph = TimeFst().fst - date_graph = DateFst().fst - whitelist_graph = WhiteListFst().fst - telephone_graph = TelephoneFst().fst - electronic_graph = ElectronicFst().fst - graph = ( - time_graph - | date_graph - | money_graph - | measure_graph - | fraction_graph - | ordinal_graph - | decimal_graph - | cardinal_graph - | whitelist_graph - | telephone_graph - | electronic_graph - ) - self.fst = graph diff --git a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/verbalize_final.py b/nemo_text_processing/inverse_text_normalization/fr/verbalizers/verbalize_final.py deleted file mode 100644 index 9753b785561b..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/verbalize_final.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import GraphFst, delete_extra_space, delete_space -from nemo_text_processing.inverse_text_normalization.fr.verbalizers.verbalize import VerbalizeFst -from nemo_text_processing.inverse_text_normalization.fr.verbalizers.word import WordFst -from pynini.lib import pynutil - - -class VerbalizeFinalFst(GraphFst): - """ - Finite state transducer that verbalizes an entire sentence, e.g. - tokens { name: "its" } tokens { time { hours: "12" minutes: "30" } } tokens { name: "now" } -> its 12:30 now - """ - - def __init__(self): - super().__init__(name="verbalize_final", kind="verbalize") - verbalize = VerbalizeFst().fst - word = WordFst().fst - types = verbalize | word - graph = ( - pynutil.delete("tokens") - + delete_space - + pynutil.delete("{") - + delete_space - + types - + delete_space - + pynutil.delete("}") - ) - graph = delete_space + pynini.closure(graph + delete_extra_space) + graph + delete_space - self.fst = graph diff --git a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/whitelist.py b/nemo_text_processing/inverse_text_normalization/fr/verbalizers/whitelist.py deleted file mode 100644 index 6b350062d8d5..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/whitelist.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import ( - NEMO_CHAR, - NEMO_SIGMA, - GraphFst, - delete_space, -) -from pynini.lib import pynutil - - -class WhiteListFst(GraphFst): - """ - Finite state transducer for verbalizing whitelist - e.g. tokens { name: "mrs." } -> mrs. - """ - - def __init__(self): - super().__init__(name="whitelist", kind="verbalize") - graph = ( - pynutil.delete("name:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_CHAR - " ", 1) - + pynutil.delete("\"") - ) - graph = graph @ pynini.cdrewrite(pynini.cross(u"\u00A0", " "), "", "", NEMO_SIGMA) - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/word.py b/nemo_text_processing/inverse_text_normalization/fr/verbalizers/word.py deleted file mode 100644 index fa8f0f39d3d4..000000000000 --- a/nemo_text_processing/inverse_text_normalization/fr/verbalizers/word.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.fr.graph_utils import ( - NEMO_CHAR, - NEMO_SIGMA, - GraphFst, - delete_space, -) -from pynini.lib import pynutil - - -class WordFst(GraphFst): - """ - Finite state transducer for verbalizing plain tokens - e.g. tokens { name: "sleep" } -> sleep - """ - - def __init__(self): - super().__init__(name="word", kind="verbalize") - chars = pynini.closure(NEMO_CHAR - " ", 1) - char = pynutil.delete("name:") + delete_space + pynutil.delete("\"") + chars + pynutil.delete("\"") - graph = char @ pynini.cdrewrite(pynini.cross(u"\u00A0", " "), "", "", NEMO_SIGMA) - - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/inverse_normalize.py b/nemo_text_processing/inverse_text_normalization/inverse_normalize.py deleted file mode 100644 index a39131440cec..000000000000 --- a/nemo_text_processing/inverse_text_normalization/inverse_normalize.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from argparse import ArgumentParser -from time import perf_counter -from typing import List - -from nemo_text_processing.text_normalization.data_loader_utils import load_file, write_file -from nemo_text_processing.text_normalization.normalize import Normalizer -from nemo_text_processing.text_normalization.token_parser import TokenParser - - -class InverseNormalizer(Normalizer): - """ - Inverse normalizer that converts text from spoken to written form. Useful for ASR postprocessing. - Input is expected to have no punctuation outside of approstrophe (') and dash (-) and be lower cased. - - Args: - lang: language specifying the ITN - cache_dir: path to a dir with .far grammar file. Set to None to avoid using cache. - overwrite_cache: set to True to overwrite .far files - """ - - def __init__(self, lang: str = 'en', cache_dir: str = None, overwrite_cache: bool = False): - - if lang == 'en': - from nemo_text_processing.inverse_text_normalization.en.taggers.tokenize_and_classify import ClassifyFst - from nemo_text_processing.inverse_text_normalization.en.verbalizers.verbalize_final import ( - VerbalizeFinalFst, - ) - - elif lang == 'es': - from nemo_text_processing.inverse_text_normalization.es.taggers.tokenize_and_classify import ClassifyFst - from nemo_text_processing.inverse_text_normalization.es.verbalizers.verbalize_final import ( - VerbalizeFinalFst, - ) - - elif lang == 'pt': - from nemo_text_processing.inverse_text_normalization.pt.taggers.tokenize_and_classify import ClassifyFst - from nemo_text_processing.inverse_text_normalization.pt.verbalizers.verbalize_final import ( - VerbalizeFinalFst, - ) - - elif lang == 'ru': - from nemo_text_processing.inverse_text_normalization.ru.taggers.tokenize_and_classify import ClassifyFst - from nemo_text_processing.inverse_text_normalization.ru.verbalizers.verbalize_final import ( - VerbalizeFinalFst, - ) - - elif lang == 'de': - from nemo_text_processing.inverse_text_normalization.de.taggers.tokenize_and_classify import ClassifyFst - from nemo_text_processing.inverse_text_normalization.de.verbalizers.verbalize_final import ( - VerbalizeFinalFst, - ) - elif lang == 'fr': - from nemo_text_processing.inverse_text_normalization.fr.taggers.tokenize_and_classify import ClassifyFst - from nemo_text_processing.inverse_text_normalization.fr.verbalizers.verbalize_final import ( - VerbalizeFinalFst, - ) - elif lang == 'vi': - from nemo_text_processing.inverse_text_normalization.vi.taggers.tokenize_and_classify import ClassifyFst - from nemo_text_processing.inverse_text_normalization.vi.verbalizers.verbalize_final import ( - VerbalizeFinalFst, - ) - - self.tagger = ClassifyFst(cache_dir=cache_dir, overwrite_cache=overwrite_cache) - self.verbalizer = VerbalizeFinalFst() - self.parser = TokenParser() - self.lang = lang - - def inverse_normalize_list(self, texts: List[str], verbose=False) -> List[str]: - """ - NeMo inverse text normalizer - - Args: - texts: list of input strings - verbose: whether to print intermediate meta information - - Returns converted list of input strings - """ - return self.normalize_list(texts=texts, verbose=verbose) - - def inverse_normalize(self, text: str, verbose: bool) -> str: - """ - Main function. Inverse normalizes tokens from spoken to written form - e.g. twelve kilograms -> 12 kg - - Args: - text: string that may include semiotic classes - verbose: whether to print intermediate meta information - - Returns: written form - """ - return self.normalize(text=text, verbose=verbose) - - -def parse_args(): - parser = ArgumentParser() - input = parser.add_mutually_exclusive_group() - input.add_argument("--text", dest="input_string", help="input string", type=str) - input.add_argument("--input_file", dest="input_file", help="input file path", type=str) - parser.add_argument('--output_file', dest="output_file", help="output file path", type=str) - parser.add_argument( - "--language", help="language", choices=['en', 'de', 'es', 'pt', 'ru', 'fr', 'vi'], default="en", type=str - ) - parser.add_argument("--verbose", help="print info for debugging", action='store_true') - parser.add_argument("--overwrite_cache", help="set to True to re-create .far grammar files", action="store_true") - parser.add_argument( - "--cache_dir", - help="path to a dir with .far grammar file. Set to None to avoid using cache", - default=None, - type=str, - ) - return parser.parse_args() - - -if __name__ == "__main__": - args = parse_args() - start_time = perf_counter() - inverse_normalizer = InverseNormalizer( - lang=args.language, cache_dir=args.cache_dir, overwrite_cache=args.overwrite_cache - ) - print(f'Time to generate graph: {round(perf_counter() - start_time, 2)} sec') - - if args.input_string: - print(inverse_normalizer.inverse_normalize(args.input_string, verbose=args.verbose)) - elif args.input_file: - print("Loading data: " + args.input_file) - data = load_file(args.input_file) - - print("- Data: " + str(len(data)) + " sentences") - prediction = inverse_normalizer.inverse_normalize_list(data, verbose=args.verbose) - if args.output_file: - write_file(args.output_file, prediction) - print(f"- Denormalized. Writing out to {args.output_file}") - else: - print(prediction) diff --git a/nemo_text_processing/inverse_text_normalization/pt/__init__.py b/nemo_text_processing/inverse_text_normalization/pt/__init__.py deleted file mode 100644 index c1586debd25f..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from nemo_text_processing.inverse_text_normalization.pt.taggers.tokenize_and_classify import ClassifyFst -from nemo_text_processing.inverse_text_normalization.pt.verbalizers.verbalize import VerbalizeFst -from nemo_text_processing.inverse_text_normalization.pt.verbalizers.verbalize_final import VerbalizeFinalFst diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/__init__.py b/nemo_text_processing/inverse_text_normalization/pt/data/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/currency_plural.tsv b/nemo_text_processing/inverse_text_normalization/pt/data/currency_plural.tsv deleted file mode 100644 index a89a763093ea..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/currency_plural.tsv +++ /dev/null @@ -1,5 +0,0 @@ -€ euros -£ libras esterlinas -US$ dólares americanos -$ dólares -R$ reais \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/currency_singular.tsv b/nemo_text_processing/inverse_text_normalization/pt/data/currency_singular.tsv deleted file mode 100644 index 9ec77dc35654..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/currency_singular.tsv +++ /dev/null @@ -1,5 +0,0 @@ -€ euro -£ libra esterlina -US$ dólar americano -$ dólar -R$ real \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/electronic/__init__.py b/nemo_text_processing/inverse_text_normalization/pt/data/electronic/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/electronic/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/electronic/domain.tsv b/nemo_text_processing/inverse_text_normalization/pt/data/electronic/domain.tsv deleted file mode 100644 index ea547b890119..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/electronic/domain.tsv +++ /dev/null @@ -1,26 +0,0 @@ -com -es -uk -fr -net -br -in -ru -de -it -edu -co -ar -bo -cl -co -ec -fk -gf -fy -pe -py -sr -ve -uy -pt \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/electronic/server_name.tsv b/nemo_text_processing/inverse_text_normalization/pt/data/electronic/server_name.tsv deleted file mode 100644 index 34ab709bb308..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/electronic/server_name.tsv +++ /dev/null @@ -1,11 +0,0 @@ -gmail g mail -gmail -nvidia n vidia -nvidia -outlook -hotmail -yahoo -aol -live -msn -live \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/electronic/symbols.tsv b/nemo_text_processing/inverse_text_normalization/pt/data/electronic/symbols.tsv deleted file mode 100644 index 690a9ca427f1..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/electronic/symbols.tsv +++ /dev/null @@ -1,6 +0,0 @@ -. ponto -- traço -- hífen -_ traço baixo -_ underscore -/ barra \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/measurements_plural.tsv b/nemo_text_processing/inverse_text_normalization/pt/data/measurements_plural.tsv deleted file mode 100755 index 6d2684afdf8b..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/measurements_plural.tsv +++ /dev/null @@ -1,56 +0,0 @@ -h horas -min minutos -s segundos -ms milissegundos -ns nanossegundos -μs microssegundos -t toneladas -kg quilos -kg quilogramas -g gramas -mg miligramas -μm micrômetros -nm nanômetros -mm milímetros -cm centímetros -cm² centímetros quadrado -cm³ centímetros cúbico -m metros -m² metros quadrados -m³ metros cúbicos -km quilômetros -km² quilômetros quadrados -ha hectares -kph quilômetros por hora -mph milhas por hora -m/s metros por segundo -l litros -ml mililitros -kgf quilogramas forças -kgf quilogramas força -% por cento -°F fahrenheit -°C celsius -°F graus fahrenheit -°C graus celsius -Hz hertz -kHz quilo hertz -MHz mega hertz -GHz giga hertz -W watts -kW quilowatts -MW megawatts -GW gigawatts -Wh watts hora -kWh quilowatts hora -MWh megawatts hora -GWh gigawatts hora -kV quilovolts -V volts -mV milivolts -A amperes -mA miliamperes -rpm rotações por minuto -db decibéis -cal calorias -kcal quilocalorias diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/measurements_singular.tsv b/nemo_text_processing/inverse_text_normalization/pt/data/measurements_singular.tsv deleted file mode 100755 index bf7320e6242c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/measurements_singular.tsv +++ /dev/null @@ -1,55 +0,0 @@ -h hora -min minuto -s segundo -ms milissegundo -ns nanossegundo -μs microssegundo -t tonelada -kg quilo -kg quilograma -g grama -mg miligrama -μm micrômetro -nm nanômetro -mm milímetro -cm centímetro -cm² centímetro quadrado -cm³ centímetro cúbico -m metro -m² metro quadrado -m³ metro cúbico -km quilômetro -km² quilômetro quadrado -ha hectare -kph quilômetro por hora -mph milha por hora -m/s metro por segundo -l litro -ml mililitro -kgf quilograma força -% por cento -°F fahrenheit -°C celsius -°F grau fahrenheit -°C grau celsius -Hz hertz -kHz quilo hertz -MHz mega hertz -GHz giga hertz -W watt -kW quilowatt -MW megawatt -GW gigawatt -Wh watt hora -kWh quilowatt hora -MWh megawatt hora -GWh gigawatt hora -kV quilovolt -V volt -mV milivolt -A ampere -mA miliampere -rpm rotação por minuto -db decibel -cal caloria -kcal quilocaloria diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/months.tsv b/nemo_text_processing/inverse_text_normalization/pt/data/months.tsv deleted file mode 100644 index ed1cf8d4f78c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/months.tsv +++ /dev/null @@ -1,12 +0,0 @@ -janeiro -fevereiro -março -abril -maio -junho -julho -agosto -setembro -outubro -novembro -dezembro diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/numbers/__init__.py b/nemo_text_processing/inverse_text_normalization/pt/data/numbers/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/numbers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/numbers/digit.tsv b/nemo_text_processing/inverse_text_normalization/pt/data/numbers/digit.tsv deleted file mode 100644 index fda1b633b2fb..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/numbers/digit.tsv +++ /dev/null @@ -1,11 +0,0 @@ -um 1 -uma 1 -dois 2 -duas 2 -três 3 -quatro 4 -cinco 5 -seis 6 -sete 7 -oito 8 -nove 9 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/numbers/hundreds.tsv b/nemo_text_processing/inverse_text_normalization/pt/data/numbers/hundreds.tsv deleted file mode 100644 index ff06089d3e67..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/numbers/hundreds.tsv +++ /dev/null @@ -1,17 +0,0 @@ -cento 1 -duzentos 2 -duzentas 2 -trezentos 3 -trezentas 3 -quatrocentos 4 -quatrocentas 4 -quinhentos 5 -quinhentas 5 -seiscentos 6 -seiscentas 6 -setecentos 7 -setecentas 7 -oitocentos 8 -oitocentas 8 -novecentos 9 -novecentas 9 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/numbers/onehundred.tsv b/nemo_text_processing/inverse_text_normalization/pt/data/numbers/onehundred.tsv deleted file mode 100644 index 1b5f9fa05302..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/numbers/onehundred.tsv +++ /dev/null @@ -1 +0,0 @@ -cem 100 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/numbers/teen.tsv b/nemo_text_processing/inverse_text_normalization/pt/data/numbers/teen.tsv deleted file mode 100644 index 6bc21cccfc30..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/numbers/teen.tsv +++ /dev/null @@ -1,11 +0,0 @@ -dez 10 -onze 11 -doze 12 -treze 13 -catorze 14 -quatorze 14 -quinze 15 -dezesseis 16 -dezessete 17 -dezoito 18 -dezenove 19 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/numbers/ties.tsv b/nemo_text_processing/inverse_text_normalization/pt/data/numbers/ties.tsv deleted file mode 100644 index 63ff93c83220..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/numbers/ties.tsv +++ /dev/null @@ -1,8 +0,0 @@ -vinte 2 -trinta 3 -quarenta 4 -cinquenta 5 -sessenta 6 -setenta 7 -oitenta 8 -noventa 9 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/numbers/twenties.tsv b/nemo_text_processing/inverse_text_normalization/pt/data/numbers/twenties.tsv deleted file mode 100644 index c72178c15ed1..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/numbers/twenties.tsv +++ /dev/null @@ -1,9 +0,0 @@ -vinte um 21 -vinte dois 22 -vinte três 23 -vinte quatro 24 -vinte cinco 25 -vinte seis 26 -vinte sete 27 -vinte oito 28 -vinte nove 29 diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/numbers/zero.tsv b/nemo_text_processing/inverse_text_normalization/pt/data/numbers/zero.tsv deleted file mode 100644 index c479272d4039..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/numbers/zero.tsv +++ /dev/null @@ -1 +0,0 @@ -zero 0 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/ordinals/__init__.py b/nemo_text_processing/inverse_text_normalization/pt/data/ordinals/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/ordinals/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/ordinals/digit.tsv b/nemo_text_processing/inverse_text_normalization/pt/data/ordinals/digit.tsv deleted file mode 100644 index ad97cc411414..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/ordinals/digit.tsv +++ /dev/null @@ -1,18 +0,0 @@ -primeiro 1 -primeira 1 -segundo 2 -segunda 2 -terceiro 3 -terceira 3 -quarto 4 -quarta 4 -quinto 5 -quinta 5 -sexto 6 -sexta 6 -sétimo 7 -sétima 7 -oitavo 8 -oitava 8 -nono 9 -nona 9 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/ordinals/hundreds.tsv b/nemo_text_processing/inverse_text_normalization/pt/data/ordinals/hundreds.tsv deleted file mode 100644 index b7b15ee92488..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/ordinals/hundreds.tsv +++ /dev/null @@ -1,28 +0,0 @@ -centésimo 1 -centésima 1 -ducentésimo 2 -ducentésima 2 -tricentésimo 3 -tricentésima 3 -trecentésimo 3 -trecentésima 3 -quadringentésimo 4 -quadringentésima 4 -quingentésimo 5 -quingentésima 5 -sexcentésimo 6 -sexcentésima 6 -seiscentésimo 6 -seiscentésima 6 -septingentésimo 7 -septingentésima 7 -setingentésimo 7 -setingentésima 7 -octingentésimo 8 -octingentésima 8 -octogentésimo 8 -octogentésima 8 -noningentésimo 9 -noningentésima 9 -nongentésimo 9 -nongentésima 9 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/ordinals/ties.tsv b/nemo_text_processing/inverse_text_normalization/pt/data/ordinals/ties.tsv deleted file mode 100644 index 55c4c4ee2fa3..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/ordinals/ties.tsv +++ /dev/null @@ -1,20 +0,0 @@ -décimo 1 -décima 1 -vigésimo 2 -vigésima 2 -trigésimo 3 -trigésima 3 -quadragésimo 4 -quadragésima 4 -quinquagésimo 5 -quinquagésima 5 -sexagésimo 6 -sexagésima 6 -septuagésimo 7 -septuagésima 7 -setuagésimo 7 -setuagésima 7 -octogésimo 8 -octogésima 8 -nonagésimo 9 -nonagésima 9 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/time/__init__.py b/nemo_text_processing/inverse_text_normalization/pt/data/time/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/time/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/time/hour_to_am.tsv b/nemo_text_processing/inverse_text_normalization/pt/data/time/hour_to_am.tsv deleted file mode 100644 index c22366ba8665..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/time/hour_to_am.tsv +++ /dev/null @@ -1 +0,0 @@ -1 0 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/time/hour_to_pm.tsv b/nemo_text_processing/inverse_text_normalization/pt/data/time/hour_to_pm.tsv deleted file mode 100644 index 548045d94c60..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/time/hour_to_pm.tsv +++ /dev/null @@ -1 +0,0 @@ -1 12 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/time/hours_to.tsv b/nemo_text_processing/inverse_text_normalization/pt/data/time/hours_to.tsv deleted file mode 100644 index 5742d596b64d..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/time/hours_to.tsv +++ /dev/null @@ -1,23 +0,0 @@ -0 23 -2 1 -3 2 -4 3 -5 4 -6 5 -7 6 -8 7 -9 8 -10 9 -11 10 -12 11 -13 12 -14 13 -15 14 -16 15 -17 16 -18 17 -19 18 -20 19 -21 20 -22 21 -23 22 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/time/minutes_to.tsv b/nemo_text_processing/inverse_text_normalization/pt/data/time/minutes_to.tsv deleted file mode 100644 index d8516a9f83ce..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/time/minutes_to.tsv +++ /dev/null @@ -1,59 +0,0 @@ -01 59 -02 58 -03 57 -04 56 -05 55 -06 54 -07 53 -08 52 -09 51 -10 50 -11 49 -12 48 -13 47 -14 46 -15 45 -16 44 -17 43 -18 42 -19 41 -20 40 -21 39 -22 38 -23 37 -24 36 -25 35 -26 34 -27 33 -28 32 -29 31 -30 30 -31 29 -32 28 -33 27 -34 26 -35 25 -36 24 -37 23 -38 22 -39 21 -40 20 -41 19 -42 18 -43 17 -44 16 -45 15 -46 14 -47 13 -48 12 -49 11 -50 10 -51 09 -52 08 -53 07 -54 06 -55 05 -56 04 -57 03 -58 02 -59 01 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/time/time_suffix_am.tsv b/nemo_text_processing/inverse_text_normalization/pt/data/time/time_suffix_am.tsv deleted file mode 100644 index 95394d7a6145..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/time/time_suffix_am.tsv +++ /dev/null @@ -1,2 +0,0 @@ -da madrugada da madrugada -da manhã da manhã \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/time/time_suffix_pm.tsv b/nemo_text_processing/inverse_text_normalization/pt/data/time/time_suffix_pm.tsv deleted file mode 100644 index 18c7c994b020..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/time/time_suffix_pm.tsv +++ /dev/null @@ -1,2 +0,0 @@ -da tarde da tarde -da noite da noite \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/pt/data/whitelist.tsv b/nemo_text_processing/inverse_text_normalization/pt/data/whitelist.tsv deleted file mode 100644 index bd82088f7990..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/data/whitelist.tsv +++ /dev/null @@ -1,5 +0,0 @@ -segunda-feira segunda feira -terça-feira terça feira -quarta-feira quarta feira -quinta-feira quinta feira -sexta-feira sexta feira \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/pt/taggers/__init__.py b/nemo_text_processing/inverse_text_normalization/pt/taggers/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/taggers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/pt/taggers/cardinal.py b/nemo_text_processing/inverse_text_normalization/pt/taggers/cardinal.py deleted file mode 100644 index 5ff16a6e0f46..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/taggers/cardinal.py +++ /dev/null @@ -1,385 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.inverse_text_normalization.pt.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_ALPHA, - NEMO_DIGIT, - NEMO_SIGMA, - NEMO_SPACE, - NEMO_WHITE_SPACE, - GraphFst, - delete_space, -) -from pynini.lib import pynutil - - -class CardinalFst(GraphFst): - """ - Finite state transducer for classifying cardinals - e.g. menos veintitrés -> cardinal { negative: "-" integer: "23"} - This class converts cardinals up to (but not including) "un cuatrillón", - i.e up to "one septillion" in English (10^{24}). - Cardinals below ten are not converted (in order to avoid - "vivo em uma casa" --> "vivo em 1 casa" and any other odd conversions.) - - Although technically Portuguese grammar requires that "e" only comes after - "10s" numbers (ie. "trinta", ..., "noventa"), these rules will convert - numbers even with "e" in an ungrammatical place (because "e" is ignored - inside cardinal numbers). - e.g. "mil e uma" -> cardinal { integer: "1001"} - e.g. "cento e uma" -> cardinal { integer: "101"} - """ - - def __init__(self, use_strict_e=False): - """ - :param use_strict_e: When True forces to have the separator "e" in the right places - """ - super().__init__(name="cardinal", kind="classify") - graph_zero = pynini.string_file(get_abs_path("data/numbers/zero.tsv")) - graph_digit = pynini.string_file(get_abs_path("data/numbers/digit.tsv")) - graph_ties = pynini.string_file(get_abs_path("data/numbers/ties.tsv")) - graph_teen = pynini.string_file(get_abs_path("data/numbers/teen.tsv")) - graph_twenties = pynini.string_file(get_abs_path("data/numbers/twenties.tsv")) - graph_one_hundred = pynini.string_file(get_abs_path("data/numbers/onehundred.tsv")) - graph_hundreds = pynini.string_file(get_abs_path("data/numbers/hundreds.tsv")) - - graph = None - - if not use_strict_e: - graph_hundred_component = graph_hundreds | pynutil.insert("0") - graph_hundred_component += delete_space - graph_hundred_component += pynini.union( - graph_twenties | graph_teen | pynutil.insert("00"), - (graph_ties | pynutil.insert("0")) + delete_space + (graph_digit | pynutil.insert("0")), - ) - graph_hundred_component = pynini.union(graph_hundred_component, graph_one_hundred) - - graph_hundred_component_at_least_one_none_zero_digit = graph_hundred_component @ ( - pynini.closure(NEMO_DIGIT) + (NEMO_DIGIT - "0") + pynini.closure(NEMO_DIGIT) - ) - - graph_thousands = pynini.union( - graph_hundred_component_at_least_one_none_zero_digit + delete_space + pynutil.delete("mil"), - pynutil.insert("001") + pynutil.delete("mil"), # because we say 'mil', not 'hum mil' - pynutil.insert("000", weight=0.01), - ) - - graph_milhoes = pynini.union( - graph_hundred_component_at_least_one_none_zero_digit - + delete_space - + (pynutil.delete("milhão") | pynutil.delete("milhões")), - pynutil.insert("000", weight=0.01), - ) - - graph_bilhoes = pynini.union( - graph_hundred_component_at_least_one_none_zero_digit - + delete_space - + (pynutil.delete("bilhão") | pynutil.delete("bilhões")), - pynutil.insert("000", weight=0.01), - ) - - graph_trilhoes = pynini.union( - graph_hundred_component_at_least_one_none_zero_digit - + delete_space - + (pynutil.delete("trilhão") | pynutil.delete("trilhões")), - pynutil.insert("000", weight=0.01), - ) - - graph_quatrilhoes = pynini.union( - graph_hundred_component_at_least_one_none_zero_digit - + delete_space - + (pynutil.delete("quatrilhão") | pynutil.delete("quatrilhões")), - pynutil.insert("000", weight=0.01), - ) - - graph_quintilhoes = pynini.union( - graph_hundred_component_at_least_one_none_zero_digit - + delete_space - + (pynutil.delete("quintilhão") | pynutil.delete("quintilhões")), - pynutil.insert("000", weight=0.01), - ) - - graph_sextilhoes = pynini.union( - graph_hundred_component_at_least_one_none_zero_digit - + delete_space - + (pynutil.delete("sextilhão") | pynutil.delete("sextilhões")), - pynutil.insert("000", weight=0.01), - ) - - graph = pynini.union( - graph_sextilhoes - + delete_space - + graph_quintilhoes - + delete_space - + graph_quatrilhoes - + delete_space - + graph_trilhoes - + delete_space - + graph_bilhoes - + delete_space - + graph_milhoes - + delete_space - + graph_thousands - + delete_space - + graph_hundred_component, - graph_zero, - ) - - graph = graph @ pynini.union( - pynutil.delete(pynini.closure("0")) + pynini.difference(NEMO_DIGIT, "0") + pynini.closure(NEMO_DIGIT), - "0", - ) - - graph = ( - pynini.cdrewrite(pynutil.delete("e"), NEMO_SPACE, NEMO_SPACE, NEMO_SIGMA) - @ (NEMO_ALPHA + NEMO_SIGMA) - @ graph - ) - - else: - graph_e = ( - pynutil.delete(NEMO_WHITE_SPACE.plus) + pynutil.delete("e") + pynutil.delete(NEMO_WHITE_SPACE.plus) - ) - - graph_ties_component = pynini.union( - graph_teen | graph_twenties, - graph_ties + ((graph_e + graph_digit) | pynutil.insert("0")), - pynutil.add_weight(pynutil.insert("0") + graph_digit, 0.1), - ) @ (pynini.closure(NEMO_DIGIT) + (NEMO_DIGIT - "0") + pynini.closure(NEMO_DIGIT)) - - graph_hundreds_except_hundred = (pynini.project(graph_hundreds, "input") - "cento") @ graph_hundreds - - graph_hundred_component_prefix_e = pynini.union( - graph_one_hundred, - pynutil.add_weight(graph_hundreds_except_hundred + pynutil.insert("00"), 0.1), - pynutil.insert("0") + graph_ties_component, - ) @ (pynini.closure(NEMO_DIGIT) + (NEMO_DIGIT - "0") + pynini.closure(NEMO_DIGIT)) - graph_hundred_component_prefix_e = graph_hundred_component_prefix_e.optimize() - - graph_hundred_component_no_prefix = pynini.union(graph_hundreds + graph_e + graph_ties_component,) @ ( - pynini.closure(NEMO_DIGIT) + (NEMO_DIGIT - "0") + pynini.closure(NEMO_DIGIT) - ) - graph_hundred_component_no_prefix = graph_hundred_component_no_prefix.optimize() - - graph_mil_prefix_e = pynini.union( - # because we say 'mil', not 'hum mil' - ( - (graph_hundred_component_prefix_e + delete_space + pynutil.delete("mil")) - | (pynutil.insert("001", weight=0.1) + pynutil.delete("mil")) - ) - + ( - (graph_e + graph_hundred_component_prefix_e) - | (delete_space + graph_hundred_component_no_prefix) - | pynutil.insert("000", weight=0.1) - ) - ) - - graph_mil_no_prefix = pynini.union( - ( - (graph_hundred_component_no_prefix + delete_space + pynutil.delete("mil")) - | pynutil.insert("000", weight=0.1) - ) - + ( - (graph_e + graph_hundred_component_prefix_e) - | (delete_space + graph_hundred_component_no_prefix) - | pynutil.insert("000", weight=0.1) - ) - ) - - graph_milhao_prefix_e = pynini.union( - ( - graph_hundred_component_prefix_e - + delete_space - + (pynutil.delete("milhão") | pynutil.delete("milhões")) - ) - + ((graph_e + graph_mil_prefix_e) | (delete_space + graph_mil_no_prefix)) - ) - - graph_milhao_no_prefix = pynini.union( - ( - ( - graph_hundred_component_no_prefix - + delete_space - + (pynutil.delete("milhão") | pynutil.delete("milhões")) - ) - | pynutil.insert("000", weight=0.1) - ) - + ((graph_e + graph_mil_prefix_e) | (delete_space + graph_mil_no_prefix)) - ) - - graph_bilhao_prefix_e = pynini.union( - ( - graph_hundred_component_prefix_e - + delete_space - + (pynutil.delete("bilhão") | pynutil.delete("bilhões")) - ) - + ((graph_e + graph_milhao_prefix_e) | (delete_space + graph_milhao_no_prefix)) - ) - - graph_bilhao_no_prefix = pynini.union( - ( - ( - graph_hundred_component_no_prefix - + delete_space - + (pynutil.delete("bilhão") | pynutil.delete("bilhões")) - ) - | pynutil.insert("000", weight=0.1) - ) - + ((graph_e + graph_milhao_prefix_e) | (delete_space + graph_milhao_no_prefix)) - ) - - graph_trilhao_prefix_e = pynini.union( - ( - graph_hundred_component_prefix_e - + delete_space - + (pynutil.delete("trilhão") | pynutil.delete("trilhões")) - ) - + ((graph_e + graph_bilhao_prefix_e) | (delete_space + graph_bilhao_no_prefix)) - ) - - graph_trilhao_no_prefix = pynini.union( - ( - ( - graph_hundred_component_no_prefix - + delete_space - + (pynutil.delete("trilhão") | pynutil.delete("trilhões")) - ) - | pynutil.insert("000", weight=0.1) - ) - + ((graph_e + graph_bilhao_prefix_e) | (delete_space + graph_bilhao_no_prefix)) - ) - - graph_quatrilhao_prefix_e = pynini.union( - ( - graph_hundred_component_prefix_e - + delete_space - + (pynutil.delete("quatrilhão") | pynutil.delete("quatrilhões")) - ) - + ((graph_e + graph_trilhao_prefix_e) | (delete_space + graph_trilhao_no_prefix)) - ) - - graph_quatrilhao_no_prefix = pynini.union( - ( - ( - graph_hundred_component_no_prefix - + delete_space - + (pynutil.delete("quatrilhão") | pynutil.delete("quatrilhões")) - ) - | pynutil.insert("000", weight=0.1) - ) - + ((graph_e + graph_trilhao_prefix_e) | (delete_space + graph_trilhao_no_prefix)) - ) - - graph_quintilhao_prefix_e = pynini.union( - ( - graph_hundred_component_prefix_e - + delete_space - + (pynutil.delete("quintilhão") | pynutil.delete("quintilhões")) - ) - + ((graph_e + graph_quatrilhao_prefix_e) | (delete_space + graph_quatrilhao_no_prefix)) - ) - - graph_quintilhao_no_prefix = pynini.union( - ( - ( - graph_hundred_component_no_prefix - + delete_space - + (pynutil.delete("quintilhão") | pynutil.delete("quintilhões")) - ) - | pynutil.insert("000", weight=0.1) - ) - + ((graph_e + graph_quatrilhao_prefix_e) | (delete_space + graph_quatrilhao_no_prefix)) - ) - - graph_sextilhao_prefix_e = pynini.union( - ( - graph_hundred_component_prefix_e - + delete_space - + (pynutil.delete("sextilhão") | pynutil.delete("sextilhões")) - ) - + ((graph_e + graph_quintilhao_prefix_e) | (delete_space + graph_quintilhao_no_prefix)) - ) - - graph_sextilhao_no_prefix = pynini.union( - ( - ( - graph_hundred_component_no_prefix - + delete_space - + (pynutil.delete("sextilhão") | pynutil.delete("sextilhões")) - ) - | pynutil.insert("000", weight=0.1) - ) - + ((graph_e + graph_quintilhao_prefix_e) | (delete_space + graph_quintilhao_no_prefix)) - ) - - graph = pynini.union( - graph_sextilhao_no_prefix, - graph_sextilhao_prefix_e, - graph_quintilhao_prefix_e, - graph_quatrilhao_prefix_e, - graph_trilhao_prefix_e, - graph_bilhao_prefix_e, - graph_milhao_prefix_e, - graph_mil_prefix_e, - graph_hundred_component_prefix_e, - graph_ties_component, - graph_zero, - ).optimize() - - graph = graph @ pynini.union( - pynutil.delete(pynini.closure("0")) + pynini.difference(NEMO_DIGIT, "0") + pynini.closure(NEMO_DIGIT), - "0", - ) - - graph = graph.optimize() - self.graph_no_exception = graph - - # save self.numbers_up_to_thousand for use in DecimalFst - digits_up_to_thousand = NEMO_DIGIT | (NEMO_DIGIT ** 2) | (NEMO_DIGIT ** 3) - numbers_up_to_thousand = pynini.compose(graph, digits_up_to_thousand).optimize() - self.numbers_up_to_thousand = numbers_up_to_thousand - - # save self.numbers_up_to_million for use in DecimalFst - digits_up_to_million = ( - NEMO_DIGIT - | (NEMO_DIGIT ** 2) - | (NEMO_DIGIT ** 3) - | (NEMO_DIGIT ** 4) - | (NEMO_DIGIT ** 5) - | (NEMO_DIGIT ** 6) - ) - numbers_up_to_million = pynini.compose(graph, digits_up_to_million).optimize() - self.numbers_up_to_million = numbers_up_to_million - - # save self.digits_from_year for use in DateFst - digits_1_2099 = [str(digits) for digits in range(1, 2100)] - digits_from_year = (numbers_up_to_million @ pynini.union(*digits_1_2099)).optimize() - self.digits_from_year = digits_from_year - - # don't convert cardinals from zero to nine inclusive - graph_exception = pynini.project(pynini.union(graph_digit, graph_zero), 'input') - - self.graph = (pynini.project(graph, "input") - graph_exception.arcsort()) @ graph - - optional_minus_graph = pynini.closure( - pynutil.insert("negative: ") + pynini.cross("menos", "\"-\"") + NEMO_SPACE, 0, 1 - ) - - final_graph = optional_minus_graph + pynutil.insert("integer: \"") + self.graph + pynutil.insert("\"") - - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/pt/taggers/date.py b/nemo_text_processing/inverse_text_normalization/pt/taggers/date.py deleted file mode 100644 index 4b891ea98a42..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/taggers/date.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.pt.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst, delete_extra_space, delete_space -from pynini.lib import pynutil - - -class DateFst(GraphFst): - """ - Finite state transducer for classifying date, - e.g. primeiro de janeiro -> date { day: "1" month: "janeiro" } - e.g. um de janeiro -> date { day: "1" month: "janeiro" } - """ - - def __init__(self, cardinal: GraphFst): - super().__init__(name="date", kind="classify") - - digits_from_year = cardinal.digits_from_year - - graph_digit = pynini.string_file(get_abs_path("data/numbers/digit.tsv")) - graph_ties = pynini.string_file(get_abs_path("data/numbers/ties.tsv")) - graph_teen = pynini.string_file(get_abs_path("data/numbers/teen.tsv")) - graph_twenties = pynini.string_file(get_abs_path("data/numbers/twenties.tsv")) - - graph_1_to_100 = pynini.union( - pynutil.insert("0") + graph_digit, - graph_twenties, - graph_teen, - (graph_ties + pynutil.insert("0")), - (graph_ties + pynutil.delete(" e ") + graph_digit), - ) - - digits_1_to_31 = [str("{:0>2d}").format(digits) for digits in range(1, 32)] - graph_1_to_31 = graph_1_to_100 @ pynini.union(*digits_1_to_31) - # can use "primeiro" for 1st day of the month - graph_1_to_31 = pynini.union(graph_1_to_31, pynini.cross("primeiro", "01")) - - day_graph = pynutil.insert("day: \"") + graph_1_to_31 + pynutil.insert("\"") - - month_name_graph = pynini.string_file(get_abs_path("data/months.tsv")) - month_name_graph = pynutil.insert("month: \"") + month_name_graph + pynutil.insert("\"") - - # vinte do oito -> 20/08 - digits_1_to_12 = [str("{:0>2d}").format(digits) for digits in range(1, 13)] - graph_1_to_12 = graph_1_to_100 @ pynini.union(*digits_1_to_12) - month_number_graph = pynutil.insert("month: \"") + graph_1_to_12 + pynutil.insert("\"") - - graph_dm = day_graph + delete_space + pynutil.delete("de") + delete_extra_space + month_name_graph - - graph_dm |= ( - day_graph - + delete_space - + pynutil.delete("do") - + delete_extra_space - + month_number_graph - + pynutil.insert(" morphosyntactic_features: \"/\"") - ) - - graph_year = ( - delete_space - + pynutil.delete("de") - + delete_extra_space - + pynutil.insert("year: \"") - + digits_from_year - + pynutil.insert("\"") - ) - graph_dmy = graph_dm + graph_year.ques - - final_graph = graph_dmy - final_graph += pynutil.insert(" preserve_order: true") - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/pt/taggers/decimal.py b/nemo_text_processing/inverse_text_normalization/pt/taggers/decimal.py deleted file mode 100644 index dab779965ed3..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/taggers/decimal.py +++ /dev/null @@ -1,119 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.pt.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_DIGIT, - GraphFst, - delete_extra_space, - delete_space, -) -from pynini.lib import pynutil - - -def get_quantity(decimal: 'pynini.FstLike', cardinal_up_to_million: 'pynini.FstLike') -> 'pynini.FstLike': - """ - Returns FST that transforms either a cardinal or decimal followed by a quantity into a numeral, - e.g. one million -> integer_part: "1" quantity: "million" - e.g. one point five million -> integer_part: "1" fractional_part: "5" quantity: "million" - - Args: - decimal: decimal FST - cardinal_up_to_million: cardinal FST - """ - numbers = cardinal_up_to_million @ ( - pynutil.delete(pynini.closure("0")) + pynini.difference(NEMO_DIGIT, "0") + pynini.closure(NEMO_DIGIT) - ) - - suffix = pynini.union( - "milhão", - "milhões", - "bilhão", - "bilhões", - "trilhão", - "trilhões", - "quatrilhão", - "quatrilhões", - "quintilhão", - "quintilhões", - "sextilhão", - "sextilhões", - ) - res = ( - pynutil.insert("integer_part: \"") - + numbers - + pynutil.insert("\"") - + delete_extra_space - + pynutil.insert("quantity: \"") - + suffix - + pynutil.insert("\"") - ) - res |= decimal + delete_extra_space + pynutil.insert("quantity: \"") + suffix + pynutil.insert("\"") - return res - - -class DecimalFst(GraphFst): - """ - Finite state transducer for classifying decimal - Decimal point is either "." or ",", determined by whether "ponto" or "vírgula" is spoken. - e.g. menos um vírgula dois seis -> decimal { negative: "true" integer_part: "1" morphosyntactic_features: "," fractional_part: "26" } - e.g. menos um ponto dois seis -> decimal { negative: "true" integer_part: "1" morphosyntactic_features: "." fractional_part: "26" } - - This decimal rule assumes that decimals can be pronounced as: - (a cardinal) + ('vírgula' or 'ponto') plus (any sequence of cardinals <1000, including 'zero') - - Also writes large numbers in shortened form, e.g. - e.g. um vírgula dois seis milhões -> decimal { negative: "false" integer_part: "1" morphosyntactic_features: "," fractional_part: "26" quantity: "milhões" } - e.g. dois milhões -> decimal { negative: "false" integer_part: "2" quantity: "milhões" } - e.g. mil oitcentos e vinte e quatro milhões -> decimal { negative: "false" integer_part: "1824" quantity: "milhões" } - Args: - cardinal: CardinalFst - - """ - - def __init__(self, cardinal: GraphFst): - super().__init__(name="decimal", kind="classify") - - # number after decimal point can be any series of cardinals <1000, including 'zero' - graph_decimal = cardinal.numbers_up_to_thousand - graph_decimal = pynini.closure(graph_decimal + delete_space) + graph_decimal - self.graph = graph_decimal - - # decimal point can be denoted by 'vírgula' or 'ponto' - decimal_point = pynini.cross("vírgula", "morphosyntactic_features: \",\"") - decimal_point |= pynini.cross("ponto", "morphosyntactic_features: \".\"") - - optional_graph_negative = pynini.closure( - pynutil.insert("negative: ") + pynini.cross("menos", "\"true\"") + delete_extra_space, 0, 1 - ) - - graph_fractional = pynutil.insert("fractional_part: \"") + graph_decimal + pynutil.insert("\"") - - cardinal_graph = cardinal.graph_no_exception | pynini.string_file(get_abs_path("data/numbers/zero.tsv")) - graph_integer = pynutil.insert("integer_part: \"") + cardinal_graph + pynutil.insert("\"") - final_graph_wo_sign = ( - pynini.closure(graph_integer + delete_extra_space, 0, 1) - + decimal_point - + delete_extra_space - + graph_fractional - ) - final_graph = optional_graph_negative + final_graph_wo_sign - - self.final_graph_wo_negative = final_graph_wo_sign | get_quantity( - final_graph_wo_sign, cardinal.numbers_up_to_million - ) - final_graph |= optional_graph_negative + get_quantity(final_graph_wo_sign, cardinal.numbers_up_to_million) - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/pt/taggers/electronic.py b/nemo_text_processing/inverse_text_normalization/pt/taggers/electronic.py deleted file mode 100644 index aa152b116a20..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/taggers/electronic.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.pt.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_ALPHA, GraphFst, insert_space -from pynini.lib import pynutil - - -class ElectronicFst(GraphFst): - """ - Finite state transducer for classifying 'electronic' semiotic classes, i.e. - email address (which get converted to "username" and "domain" fields), - and URLS (which get converted to a "protocol" field). - e.g. c d f um arroba a b c ponto e d u -> tokens { electronic { username: "cdf1" domain: "abc.edu" } } - e.g. dáblio dáblio dáblio a b c ponto e d u -> tokens { electronic { protocol: "www.abc.edu" } } - """ - - def __init__(self): - super().__init__(name="electronic", kind="classify") - - delete_extra_space = pynutil.delete(" ") - alpha_num = ( - NEMO_ALPHA - | pynini.string_file(get_abs_path("data/numbers/digit.tsv")) - | pynini.string_file(get_abs_path("data/numbers/zero.tsv")) - ) - - symbols = pynini.string_file(get_abs_path("data/electronic/symbols.tsv")).invert() - - accepted_username = alpha_num | symbols - process_dot = pynini.cross("ponto", ".") - username = ( - pynutil.insert("username: \"") - + alpha_num - + delete_extra_space - + pynini.closure(accepted_username + delete_extra_space) - + alpha_num - + pynutil.insert("\"") - ) - single_alphanum = pynini.closure(alpha_num + delete_extra_space) + alpha_num - server = single_alphanum | pynini.string_file(get_abs_path("data/electronic/server_name.tsv")).invert() - domain = single_alphanum | pynini.string_file(get_abs_path("data/electronic/domain.tsv")).invert() - domain_graph = ( - pynutil.insert("domain: \"") - + server - + delete_extra_space - + process_dot - + delete_extra_space - + domain - + pynutil.insert("\"") - ) - graph = ( - username + delete_extra_space + pynutil.delete("arroba") + insert_space + delete_extra_space + domain_graph - ) - - ############# url ### - protocol_end = pynini.cross(pynini.union("www", "w w w", "dáblio dáblio dáblio"), "www") - protocol_start = pynini.cross(pynini.union("http", "h t t p", "agá tê tê pê"), "http") - protocol_start |= pynini.cross(pynini.union("https", "h t t p s", "agá tê tê pê ésse"), "https") - protocol_start += pynini.cross(" dois pontos barra barra ", "://") - - # e.g. .com, .es - ending = ( - delete_extra_space - + symbols - + delete_extra_space - + (domain | pynini.closure(accepted_username + delete_extra_space) + accepted_username) - ) - - protocol = ( - pynini.closure(protocol_start, 0, 1) - + protocol_end - + delete_extra_space - + process_dot - + delete_extra_space - + (pynini.closure(delete_extra_space + accepted_username, 1) | server) - + pynini.closure(ending, 1) - ) - protocol = pynutil.insert("protocol: \"") + protocol + pynutil.insert("\"") - graph |= protocol - ######## - - final_graph = self.add_tokens(graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/pt/taggers/measure.py b/nemo_text_processing/inverse_text_normalization/pt/taggers/measure.py deleted file mode 100644 index 7b6f1015ad79..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/taggers/measure.py +++ /dev/null @@ -1,95 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.pt.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_SIGMA, - GraphFst, - convert_space, - delete_extra_space, - delete_space, -) -from pynini.lib import pynutil - - -class MeasureFst(GraphFst): - """ - Finite state transducer for classifying measure - e.g. menos doze quilogramas -> measure { cardinal { negative: "true" integer: "12" } units: "kg" } - - Args: - cardinal: CardinalFst - decimal: DecimalFst - """ - - def __init__(self, cardinal: GraphFst, decimal: GraphFst): - super().__init__(name="measure", kind="classify") - - cardinal_graph = cardinal.graph_no_exception - - graph_unit_singular = pynini.string_file(get_abs_path("data/measurements_singular.tsv")).invert() - graph_unit_plural = pynini.string_file(get_abs_path("data/measurements_plural.tsv")).invert() - - optional_graph_negative = pynini.closure( - pynutil.insert("negative: ") + pynini.cross("menos", "\"true\"") + delete_extra_space, 0, 1 - ) - - unit_singular = convert_space(graph_unit_singular) - unit_plural = convert_space(graph_unit_plural) - unit_misc = pynutil.insert("/") + pynutil.delete("por") + delete_space + convert_space(graph_unit_singular) - - unit_singular = ( - pynutil.insert("units: \"") - + (unit_singular | unit_misc | pynutil.add_weight(unit_singular + delete_space + unit_misc, 0.01)) - + pynutil.insert("\"") - ) - unit_plural = ( - pynutil.insert("units: \"") - + (unit_plural | unit_misc | pynutil.add_weight(unit_plural + delete_space + unit_misc, 0.01)) - + pynutil.insert("\"") - ) - - subgraph_decimal = ( - pynutil.insert("decimal { ") - + optional_graph_negative - + decimal.final_graph_wo_negative - + pynutil.insert(" }") - + delete_extra_space - + unit_plural - ) - subgraph_cardinal = ( - pynutil.insert("cardinal { ") - + optional_graph_negative - + pynutil.insert("integer: \"") - + ((NEMO_SIGMA - "um" - "uma") @ cardinal_graph) - + pynutil.insert("\"") - + pynutil.insert(" }") - + delete_extra_space - + unit_plural - ) - subgraph_cardinal |= ( - pynutil.insert("cardinal { ") - + optional_graph_negative - + pynutil.insert("integer: \"") - + (pynini.cross("um", "1") | pynini.cross("uma", "1")) - + pynutil.insert("\"") - + pynutil.insert(" }") - + delete_extra_space - + unit_singular - ) - - final_graph = subgraph_decimal | subgraph_cardinal - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/pt/taggers/money.py b/nemo_text_processing/inverse_text_normalization/pt/taggers/money.py deleted file mode 100644 index cc3639438b83..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/taggers/money.py +++ /dev/null @@ -1,127 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.pt.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_DIGIT, - NEMO_SIGMA, - GraphFst, - convert_space, - delete_extra_space, - delete_space, - insert_space, -) -from pynini.lib import pynutil - - -class MoneyFst(GraphFst): - """ - Finite state transducer for classifying money - e.g. doze dólares e cinco centavos -> money { integer_part: "12" fractional_part: "05" currency: "$" } - - Args: - cardinal: CardinalFst - decimal: DecimalFst - """ - - def __init__(self, cardinal: GraphFst, decimal: GraphFst): - super().__init__(name="money", kind="classify") - # quantity, integer_part, fractional_part, currency - - cardinal_graph = cardinal.graph_no_exception - graph_decimal_final = decimal.final_graph_wo_negative - - unit_singular = pynini.string_file(get_abs_path("data/currency_singular.tsv")).invert() - unit_plural = pynini.string_file(get_abs_path("data/currency_plural.tsv")).invert() - - graph_unit_singular = pynutil.insert("currency: \"") + convert_space(unit_singular) + pynutil.insert("\"") - graph_unit_plural = pynutil.insert("currency: \"") + convert_space(unit_plural) + pynutil.insert("\"") - - add_leading_zero_to_double_digit = (NEMO_DIGIT + NEMO_DIGIT) | (pynutil.insert("0") + NEMO_DIGIT) - # twelve dollars (and) fifty cents, zero cents - cents_standalone = ( - pynutil.insert("morphosyntactic_features: \",\"") # always use a comma in the decimal - + insert_space - + pynutil.insert("fractional_part: \"") - + pynini.union( - pynutil.add_weight(((NEMO_SIGMA - "um" - "uma") @ cardinal_graph), -0.7) - @ add_leading_zero_to_double_digit - + delete_space - + pynutil.delete(pynini.union("centavos")), - pynini.cross("um", "01") + delete_space + pynutil.delete(pynini.union("centavo")), - ) - + pynutil.insert("\"") - ) - - optional_cents_standalone = pynini.closure( - delete_space - + pynini.closure((pynutil.delete("com") | pynutil.delete('e')) + delete_space, 0, 1) - + insert_space - + cents_standalone, - 0, - 1, - ) - - # twelve dollars fifty, only after integer - # setenta e cinco dólares com sessenta e três ~ $75,63 - optional_cents_suffix = pynini.closure( - delete_extra_space - + pynutil.insert("morphosyntactic_features: \",\"") # always use a comma in the decimal - + insert_space - + pynutil.insert("fractional_part: \"") - + pynini.closure((pynutil.delete("com") | pynutil.delete('e')) + delete_space, 0, 1) - + pynutil.add_weight(cardinal_graph @ add_leading_zero_to_double_digit, -0.7) - + pynutil.insert("\""), - 0, - 1, - ) - - graph_integer = ( - pynutil.insert("integer_part: \"") - + ((NEMO_SIGMA - "um" - "uma") @ cardinal_graph) - + pynutil.insert("\"") - + delete_extra_space - + graph_unit_plural - + (optional_cents_standalone | optional_cents_suffix) - ) - graph_integer |= ( - pynutil.insert("integer_part: \"") - + (pynini.cross("um", "1") | pynini.cross("uma", "1")) - + pynutil.insert("\"") - + delete_extra_space - + graph_unit_singular - + (optional_cents_standalone | optional_cents_suffix) - ) - - graph_cents_standalone = pynini.union( - pynutil.insert("currency: \"R$\" integer_part: \"0\" ") + cents_standalone, - pynutil.add_weight( - pynutil.insert("integer_part: \"0\" ") - + cents_standalone - + delete_extra_space - + pynutil.delete("de") - + delete_space - + graph_unit_singular, - -0.1, - ), - ) - - graph_decimal = ( - graph_decimal_final + delete_extra_space + (pynutil.delete("de") + delete_space).ques + graph_unit_plural - ) - graph_decimal |= graph_cents_standalone - final_graph = graph_integer | graph_decimal - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/pt/taggers/ordinal.py b/nemo_text_processing/inverse_text_normalization/pt/taggers/ordinal.py deleted file mode 100644 index ff7f3fbf02fa..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/taggers/ordinal.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.pt.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_SIGMA, GraphFst, delete_space -from pynini.lib import pynutil - - -class OrdinalFst(GraphFst): - """ - Finite state transducer for classifying ordinal - vigésimo primeiro -> ordinal { integer: "21" morphosyntactic_features: "o" } - This class converts ordinal up to "milésimo" (one thousandth) exclusive. - - Cardinals below ten are not converted (in order to avoid - e.g. "primero fez ..." -> "1º fez...", "segunda guerra mundial" -> "2ª guerra mundial" - and any other odd conversions.) - - This FST also records the ending of the ordinal (called "morphosyntactic_features"): - either "o" or "a". - - Args: - cardinal: CardinalFst - """ - - def __init__(self): - super().__init__(name="ordinal", kind="classify") - - graph_digit = pynini.string_file(get_abs_path("data/ordinals/digit.tsv")) - graph_ties = pynini.string_file(get_abs_path("data/ordinals/ties.tsv")) - graph_hundreds = pynini.string_file(get_abs_path("data/ordinals/hundreds.tsv")) - - ordinal_graph_union = pynini.union( - pynutil.add_weight(graph_digit, 0.4), - pynutil.add_weight(graph_ties + ((delete_space + graph_digit) | pynutil.insert("0")), 0.2), - graph_hundreds - + ((delete_space + graph_ties) | pynutil.insert("0")) - + ((delete_space + graph_digit) | pynutil.insert("0")), - ) - - accept_o_endings = NEMO_SIGMA + pynini.accep("o") - accept_a_endings = NEMO_SIGMA + pynini.accep("a") - - ordinal_graph_o = accept_o_endings @ ordinal_graph_union - ordinal_graph_a = accept_a_endings @ ordinal_graph_union - - # 'optional_numbers_in_front' have negative weight so we always - # include them if they're there - optional_in_front = (pynutil.add_weight(ordinal_graph_union, -0.1) + delete_space.closure()).closure() - graph_o_suffix = optional_in_front + ordinal_graph_o - graph_a_suffix = optional_in_front + ordinal_graph_a - - # don't convert ordinals from one to nine inclusive - graph_exception = pynini.project(pynini.union(graph_digit), 'input') - graph_o_suffix = (pynini.project(graph_o_suffix, "input") - graph_exception.arcsort()) @ graph_o_suffix - graph_a_suffix = (pynini.project(graph_a_suffix, "input") - graph_exception.arcsort()) @ graph_a_suffix - - graph = ( - pynutil.insert("integer: \"") - + graph_o_suffix - + pynutil.insert("\"") - + pynutil.insert(" morphosyntactic_features: \"o\"") - ) - graph |= ( - pynutil.insert("integer: \"") - + graph_a_suffix - + pynutil.insert("\"") - + pynutil.insert(" morphosyntactic_features: \"a\"") - ) - - final_graph = self.add_tokens(graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/pt/taggers/punctuation.py b/nemo_text_processing/inverse_text_normalization/pt/taggers/punctuation.py deleted file mode 100644 index cb5285452954..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/taggers/punctuation.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst -from pynini.lib import pynutil - - -class PunctuationFst(GraphFst): - """ - Finite state transducer for classifying punctuation - e.g. a, -> tokens { name: "a" } tokens { name: "," } - """ - - def __init__(self): - super().__init__(name="punctuation", kind="classify") - - s = "!#$%&\'()*+,-./:;<=>?@^_`{|}~" - punct = pynini.union(*s) - - graph = pynutil.insert("name: \"") + punct + pynutil.insert("\"") - - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/pt/taggers/telephone.py b/nemo_text_processing/inverse_text_normalization/pt/taggers/telephone.py deleted file mode 100755 index a1ad2d07585d..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/taggers/telephone.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.pt.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst, delete_space, insert_space -from pynini.lib import pynutil - - -class TelephoneFst(GraphFst): - """ - Finite state transducer for classifying telephone numbers, e.g. - um dois um dois três quatro cinco seis sete oito nove -> { number_part: "(12) 12345-6789" }. - If 11 digits are spoken, they are grouped as 2+5+4 (eg. (12) 34567-8901). - If 10 digits are spoken, they are grouped as 2+4+4 (eg. (12) 3456-7890). - If 9 digits are spoken, they are grouped as 5+4 (eg. 12345-6789). - If 8 digits are spoken, they are grouped as 4+4 (eg. 1234-5678). - In portuguese, digits are generally spoken individually, or as 2-digit numbers, - eg. "trinta e quatro oitenta e dois" = "3482", - "meia sete vinte" = "6720". - """ - - def __init__(self): - super().__init__(name="telephone", kind="classify") - - # create `single_digits` and `double_digits` graphs as these will be - # the building blocks of possible telephone numbers - graph_digit = pynini.string_file(get_abs_path("data/numbers/digit.tsv")) - graph_ties = pynini.string_file(get_abs_path("data/numbers/ties.tsv")) - graph_twenties = pynini.string_file(get_abs_path("data/numbers/twenties.tsv")) - graph_teen = pynini.string_file(get_abs_path("data/numbers/teen.tsv")) - graph_zero = pynini.string_file(get_abs_path("data/numbers/zero.tsv")) - graph_half = pynini.cross("meia", "6") - - graph_all_digits = pynini.union(graph_digit, graph_half, graph_zero) - - single_digits = pynini.invert(graph_all_digits).optimize() - - double_digits = ( - pynini.union( - graph_teen | graph_twenties, - (graph_ties + pynutil.insert("0")), - (graph_ties + delete_space + pynutil.delete("e") + delete_space + graph_digit), - (graph_all_digits + delete_space + graph_all_digits), - ) - .invert() - .optimize() - ) - - # define `eleven_digit_graph`, `ten_digit_graph`, `nine_digit_graph`, `eight_digit_graph` - # which accept telephone numbers spoken (1) only with single digits, - # or (2) spoken with double digits (and sometimes single digits) - - # 11-digit option (2): (2) + (1+2+2) + (2+2) digits - eleven_digit_graph = ( - pynutil.delete("(") - + double_digits - + insert_space - + pynutil.delete(") ") - + single_digits - + insert_space - + double_digits - + insert_space - + double_digits - + insert_space - + pynutil.delete("-") - + double_digits - + insert_space - + double_digits - ) - - # 10-digit option (2): (2) + (2+2) + (2+2) digits - ten_digit_graph = ( - pynutil.delete("(") - + double_digits - + insert_space - + pynutil.delete(") ") - + double_digits - + insert_space - + double_digits - + insert_space - + pynutil.delete("-") - + double_digits - + insert_space - + double_digits - ) - - # 9-digit option (2): (1+2+2) + (2+2) digits - nine_digit_graph = ( - single_digits - + insert_space - + double_digits - + insert_space - + double_digits - + insert_space - + pynutil.delete("-") - + double_digits - + insert_space - + double_digits - ) - - # 8-digit option (2): (2+2) + (2+2) digits - eight_digit_graph = ( - double_digits - + insert_space - + double_digits - + insert_space - + pynutil.delete("-") - + double_digits - + insert_space - + double_digits - ) - - number_part = pynini.union(eleven_digit_graph, ten_digit_graph, nine_digit_graph, eight_digit_graph) - - number_part = pynutil.insert("number_part: \"") + pynini.invert(number_part) + pynutil.insert("\"") - - graph = number_part - final_graph = self.add_tokens(graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/pt/taggers/time.py b/nemo_text_processing/inverse_text_normalization/pt/taggers/time.py deleted file mode 100755 index 1e209ec963ee..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/taggers/time.py +++ /dev/null @@ -1,214 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.pt.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst, delete_space, insert_space -from pynini.lib import pynutil - - -class TimeFst(GraphFst): - """ - Finite state transducer for classifying time - e.g. quinze pro meio dia -> time { hours: "11" minutes: "45" } - e.g. quinze pra meia noite -> time { hours: "23" minutes: "45" } - e.g. quinze pra uma -> time { hours: "12" minutes: "45" } - e.g. dez pras duas -> time { hours: "1" minutes: "50" } - e.g. quinze pras duas -> time { hours: "1" minutes: "45" } - e.g. ao meio dia -> time { hours: "12" minutes: "00" morphosyntactic_features: "ao" } - e.g. ao meio dia e meia -> time { hours: "12" minutes: "30" morphosyntactic_features: "ao" } - e.g. ao meio dia e meio -> time { hours: "12" minutes: "30" morphosyntactic_features: "ao" } - e.g. à meia noite e quinze -> time { hours: "0" minutes: "15" morphosyntactic_features: "à" } - e.g. à meia noite e meia -> time { hours: "0" minutes: "30" morphosyntactic_features: "à" } - e.g. à uma e trinta -> time { hours: "1" minutes: "30" morphosyntactic_features: "à" } - e.g. às onze e trinta -> time { hours: "11" minutes: "30" morphosyntactic_features: "às" } - e.g. às três horas e trinta minutos -> time { hours: "3" minutes: "30" morphosyntactic_features: "às" } - """ - - def __init__(self): - super().__init__(name="time", kind="classify") - - # graph_hour_to_am = pynini.string_file(get_abs_path("data/time/hour_to_am.tsv")) - # graph_hour_to_pm = pynini.string_file(get_abs_path("data/time/hour_to_pm.tsv")) - graph_hours_to = pynini.string_file(get_abs_path("data/time/hours_to.tsv")) - graph_minutes_to = pynini.string_file(get_abs_path("data/time/minutes_to.tsv")) - graph_suffix_am = pynini.string_file(get_abs_path("data/time/time_suffix_am.tsv")) - graph_suffix_pm = pynini.string_file(get_abs_path("data/time/time_suffix_pm.tsv")) - - graph_digit = pynini.string_file(get_abs_path("data/numbers/digit.tsv")) - graph_ties = pynini.string_file(get_abs_path("data/numbers/ties.tsv")) - graph_teen = pynini.string_file(get_abs_path("data/numbers/teen.tsv")) - graph_twenties = pynini.string_file(get_abs_path("data/numbers/twenties.tsv")) - - graph_1_to_100 = pynini.union( - graph_digit, - graph_twenties, - graph_teen, - (graph_ties + pynutil.insert("0")), - (graph_ties + pynutil.delete(" e ") + graph_digit), - ) - - # note that graph_hour will start from 2 hours - # "1 o'clock" will be treated differently because it - # is singular - digits_2_to_23 = [str(digits) for digits in range(2, 24)] - digits_1_to_59 = [str(digits) for digits in range(1, 60)] - - graph_2_to_23 = graph_1_to_100 @ pynini.union(*digits_2_to_23) - graph_1_to_59 = graph_1_to_100 @ pynini.union(*digits_1_to_59) - graph_uma = pynini.cross("uma", "1") - - # Mapping 'horas' - graph_hour = pynutil.delete(pynini.accep("hora") + pynini.accep("s").ques) - graph_minute = pynutil.delete(pynini.accep("minuto") + pynini.accep("s").ques) - - # Mapping 'meio dia' and 'meia noite' - graph_meio_dia = pynini.cross("meio dia", "12") - graph_meia_noite = pynini.cross("meia noite", "0") - - # Mapping 'e meia' - graph_e = delete_space + pynutil.delete(" e ") + delete_space - graph_e_meia = graph_e + pynini.cross("meia", "30") - graph_e_meio = graph_e + pynini.cross("meio", "30") - - # à uma e meia -> 1:30 - # às três e meia -> 3:30 - graph_hours_at_prefix_singular = ( - pynutil.insert("morphosyntactic_features: \"") - + (pynini.cross("à", "à") | pynini.cross("a", "à")) - + pynutil.insert("\" ") - + delete_space - ) - graph_hours_at_singular = ( - graph_hours_at_prefix_singular - + pynutil.insert("hours: \"") - + graph_uma - + pynutil.insert("\"") - + (delete_space + graph_hour).ques - ) - graph_hours_at_prefix_plural = ( - pynutil.insert("morphosyntactic_features: \"") - + (pynini.cross("às", "às") | pynini.cross("as", "às")) - + pynutil.insert("\" ") - + delete_space - ) - graph_hours_at_plural = ( - graph_hours_at_prefix_plural - + pynutil.insert("hours: \"") - + graph_2_to_23 - + pynutil.insert("\"") - + (delete_space + graph_hour).ques - ) - final_graph_hour_at = graph_hours_at_singular | graph_hours_at_plural - - graph_minutes_component_without_zero = graph_e + graph_1_to_59 + (delete_space + graph_minute).ques - graph_minutes_component_without_zero |= graph_e_meia + pynutil.delete(delete_space + pynini.accep("hora")).ques - final_graph_minute = ( - pynutil.insert(" minutes: \"") + graph_minutes_component_without_zero + pynutil.insert("\"") - ) - - graph_hm = final_graph_hour_at + final_graph_minute - - # à uma hora -> 1:00 - graph_hours_at_singular_with_hour = ( - graph_hours_at_prefix_singular - + pynutil.insert("hours: \"") - + graph_uma - + pynutil.insert("\"") - + delete_space - + graph_hour - ) - - graph_hours_at_plural_with_hour = ( - graph_hours_at_prefix_plural - + pynutil.insert("hours: \"") - + graph_2_to_23 - + pynutil.insert("\"") - + delete_space - + graph_hour - ) - - graph_hm |= (graph_hours_at_singular_with_hour | graph_hours_at_plural_with_hour) + pynutil.insert( - " minutes: \"00\"", weight=0.2 - ) - - # meio dia e meia -> 12:30 - # meia noite e meia -> 0:30 - graph_minutes_without_zero = ( - pynutil.insert(" minutes: \"") + graph_minutes_component_without_zero + pynutil.insert("\"") - ) - graph_meio_min = ( - pynutil.insert("hours: \"") - + (graph_meio_dia | graph_meia_noite) - + pynutil.insert("\"") - + graph_minutes_without_zero - ) - graph_meio_min |= ( - pynutil.insert("hours: \"") - + graph_meio_dia - + pynutil.insert("\" minutes: \"") - + graph_e_meio - + pynutil.insert("\"") - ) - graph_hm |= graph_meio_min - - # às quinze para as quatro -> às 3:45 - # NOTE: case 'para à uma' ('to one') could be either 0:XX or 12:XX - # leading to wrong reading ('meio dia e ...' or 'meia noite e ...') - graph_para_a = ( - pynutil.delete("para") - | pynutil.delete("para a") - | pynutil.delete("para as") - | pynutil.delete("pra") - | pynutil.delete("pras") - ) - graph_para_o = pynutil.delete("para") | pynutil.delete("para o") | pynutil.delete("pro") - - graph_pra_min = ( - pynutil.insert("morphosyntactic_features: \"") - + (pynini.cross("à", "à") | pynini.cross("às", "às") | pynini.cross("a", "à") | pynini.cross("as", "às")) - + pynutil.insert("\" ") - + delete_space - ) - graph_pra_min += ( - pynutil.insert("minutes: \"") - + (graph_1_to_59 @ graph_minutes_to) - + pynutil.insert("\" ") - + (delete_space + graph_minute).ques - ) - graph_pra_hour = ( - pynutil.insert("hours: \"") - + (graph_2_to_23 @ graph_hours_to) - + pynutil.insert("\"") - + (delete_space + graph_hour).ques - ) - graph_pra_hour |= pynutil.insert("hours: \"") + (graph_meia_noite @ graph_hours_to) + pynutil.insert("\"") - - graph_pra = graph_pra_min + delete_space + graph_para_a + delete_space + graph_pra_hour - - # às quinze pro meio dia -> às 11:45 - graph_pro = graph_pra_min + delete_space + graph_para_o + delete_space - graph_pro += pynutil.insert(" hours: \"") + (graph_meio_dia @ graph_hours_to) + pynutil.insert("\"") - - graph_mh = graph_pra | graph_pro - - # optional suffix - final_suffix = pynutil.insert("suffix: \"") + (graph_suffix_am | graph_suffix_pm) + pynutil.insert("\"") - final_suffix_optional = pynini.closure(delete_space + insert_space + final_suffix, 0, 1) - - final_graph = pynini.union((graph_hm | graph_mh) + final_suffix_optional).optimize() - - final_graph = self.add_tokens(final_graph) - - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/pt/taggers/tokenize_and_classify.py b/nemo_text_processing/inverse_text_normalization/pt/taggers/tokenize_and_classify.py deleted file mode 100644 index a7a3fd6a3244..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/taggers/tokenize_and_classify.py +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -import pynini -from nemo_text_processing.inverse_text_normalization.pt.taggers.cardinal import CardinalFst -from nemo_text_processing.inverse_text_normalization.pt.taggers.date import DateFst -from nemo_text_processing.inverse_text_normalization.pt.taggers.decimal import DecimalFst -from nemo_text_processing.inverse_text_normalization.pt.taggers.electronic import ElectronicFst -from nemo_text_processing.inverse_text_normalization.pt.taggers.measure import MeasureFst -from nemo_text_processing.inverse_text_normalization.pt.taggers.money import MoneyFst -from nemo_text_processing.inverse_text_normalization.pt.taggers.ordinal import OrdinalFst -from nemo_text_processing.inverse_text_normalization.pt.taggers.punctuation import PunctuationFst -from nemo_text_processing.inverse_text_normalization.pt.taggers.telephone import TelephoneFst -from nemo_text_processing.inverse_text_normalization.pt.taggers.time import TimeFst -from nemo_text_processing.inverse_text_normalization.pt.taggers.whitelist import WhiteListFst -from nemo_text_processing.inverse_text_normalization.pt.taggers.word import WordFst -from nemo_text_processing.text_normalization.en.graph_utils import ( - GraphFst, - delete_extra_space, - delete_space, - generator_main, -) -from pynini.lib import pynutil - -from nemo.utils import logging - - -class ClassifyFst(GraphFst): - """ - Final class that composes all other classification grammars. This class can process an entire sentence, that is lower cased. - For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - - Args: - cache_dir: path to a dir with .far grammar file. Set to None to avoid using cache. - overwrite_cache: set to True to overwrite .far files - """ - - def __init__(self, cache_dir: str = None, overwrite_cache: bool = False): - super().__init__(name="tokenize_and_classify", kind="classify") - - far_file = None - if cache_dir is not None and cache_dir != "None": - os.makedirs(cache_dir, exist_ok=True) - far_file = os.path.join(cache_dir, "_pt_itn.far") - if not overwrite_cache and far_file and os.path.exists(far_file): - self.fst = pynini.Far(far_file, mode="r")["tokenize_and_classify"] - logging.info(f"ClassifyFst.fst was restored from {far_file}.") - else: - logging.info(f"Creating ClassifyFst grammars.") - - cardinal = CardinalFst(use_strict_e=True) - cardinal_graph = cardinal.fst - - ordinal_graph = OrdinalFst().fst - - decimal = DecimalFst(cardinal) - decimal_graph = decimal.fst - - measure_graph = MeasureFst(cardinal=cardinal, decimal=decimal).fst - date_graph = DateFst(cardinal=cardinal).fst - word_graph = WordFst().fst - time_graph = TimeFst().fst - money_graph = MoneyFst(cardinal=cardinal, decimal=decimal).fst - whitelist_graph = WhiteListFst().fst - punct_graph = PunctuationFst().fst - electronic_graph = ElectronicFst().fst - telephone_graph = TelephoneFst().fst - - classify = ( - pynutil.add_weight(whitelist_graph, 1.01) - | pynutil.add_weight(time_graph, 1.09) - | pynutil.add_weight(date_graph, 1.09) - | pynutil.add_weight(decimal_graph, 1.09) - | pynutil.add_weight(measure_graph, 1.1) - | pynutil.add_weight(cardinal_graph, 1.1) - | pynutil.add_weight(ordinal_graph, 1.1) - | pynutil.add_weight(money_graph, 1.1) - | pynutil.add_weight(telephone_graph, 1.1) - | pynutil.add_weight(electronic_graph, 1.1) - | pynutil.add_weight(word_graph, 100) - ) - - punct = pynutil.insert("tokens { ") + pynutil.add_weight(punct_graph, weight=1.1) + pynutil.insert(" }") - token = pynutil.insert("tokens { ") + classify + pynutil.insert(" }") - token_plus_punct = ( - pynini.closure(punct + pynutil.insert(" ")) + token + pynini.closure(pynutil.insert(" ") + punct) - ) - - graph = token_plus_punct + pynini.closure(delete_extra_space + token_plus_punct) - graph = delete_space + graph + delete_space - - self.fst = graph.optimize() - - if far_file: - generator_main(far_file, {"tokenize_and_classify": self.fst}) - logging.info(f"ClassifyFst grammars are saved to {far_file}.") diff --git a/nemo_text_processing/inverse_text_normalization/pt/taggers/whitelist.py b/nemo_text_processing/inverse_text_normalization/pt/taggers/whitelist.py deleted file mode 100644 index 6965ccbb8e70..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/taggers/whitelist.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.pt.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst, convert_space -from pynini.lib import pynutil - - -class WhiteListFst(GraphFst): - """ - Finite state transducer for classifying whitelisted tokens - e.g. usted -> tokens { name: "ud." } - This class has highest priority among all classifier grammars. Whitelisted tokens are defined and loaded from "data/whitelist.tsv". - """ - - def __init__(self): - super().__init__(name="whitelist", kind="classify") - - whitelist = pynini.string_file(get_abs_path("data/whitelist.tsv")).invert() - graph = pynutil.insert("name: \"") + convert_space(whitelist) + pynutil.insert("\"") - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/pt/taggers/word.py b/nemo_text_processing/inverse_text_normalization/pt/taggers/word.py deleted file mode 100644 index 7908397d52ad..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/taggers/word.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_SPACE, GraphFst -from pynini.lib import pynutil - - -class WordFst(GraphFst): - """ - Finite state transducer for classifying plain tokens, that do not belong to any special class. This can be considered as the default class. - e.g. sleep -> tokens { name: "sleep" } - """ - - def __init__(self): - super().__init__(name="word", kind="classify") - word = pynutil.insert("name: \"") + pynini.closure(NEMO_NOT_SPACE, 1) + pynutil.insert("\"") - self.fst = word.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/pt/utils.py b/nemo_text_processing/inverse_text_normalization/pt/utils.py deleted file mode 100644 index a73b7d9ddb39..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/utils.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - - -def get_abs_path(rel_path): - """ - Get absolute path - - Args: - rel_path: relative path to this file - - Returns absolute path - """ - return os.path.dirname(os.path.abspath(__file__)) + '/' + rel_path diff --git a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/__init__.py b/nemo_text_processing/inverse_text_normalization/pt/verbalizers/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/cardinal.py b/nemo_text_processing/inverse_text_normalization/pt/verbalizers/cardinal.py deleted file mode 100644 index 928a259d3897..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/cardinal.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class CardinalFst(GraphFst): - """ - Finite state transducer for verbalizing cardinal - e.g. cardinal { negative: "-" integer: "23" } -> -23 - """ - - def __init__(self): - super().__init__(name="cardinal", kind="verbalize") - optional_sign = pynini.closure( - pynutil.delete("negative:") - + delete_space - + pynutil.delete("\"") - + NEMO_NOT_QUOTE - + pynutil.delete("\"") - + delete_space, - 0, - 1, - ) - graph = ( - pynutil.delete("integer:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - self.numbers = graph - graph = optional_sign + graph - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/date.py b/nemo_text_processing/inverse_text_normalization/pt/verbalizers/date.py deleted file mode 100644 index 7c0034099ba2..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/date.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_NOT_QUOTE, - GraphFst, - delete_extra_space, - delete_space, - insert_space, -) -from pynini.lib import pynutil - - -class DateFst(GraphFst): - """ - Finite state transducer for verbalizing date, e.g. - date { day: "1" month: "enero" preserve_order: true } -> 1 de enero - """ - - def __init__(self): - super().__init__(name="date", kind="verbalize") - month = ( - pynutil.delete("month:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - day = ( - pynutil.delete("day:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - year = ( - pynutil.delete("year:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - - # day month - graph_dmy = ( - day - + delete_extra_space - + pynutil.insert("de") - + insert_space - + month - + (delete_extra_space + pynutil.insert("de") + insert_space + year).ques - ) - graph_dmy |= ( - day - + delete_space - + pynutil.insert("/") - + month - + pynutil.delete(" morphosyntactic_features: \"/\"") - + (delete_space + pynutil.insert("/") + year).ques - ) - - optional_preserve_order = pynini.closure( - pynutil.delete("preserve_order:") + delete_space + pynutil.delete("true") + delete_space - | pynutil.delete("field_order:") - + delete_space - + pynutil.delete("\"") - + NEMO_NOT_QUOTE - + pynutil.delete("\"") - + delete_space - ) - - final_graph = graph_dmy + delete_space + optional_preserve_order - - delete_tokens = self.delete_tokens(final_graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/decimal.py b/nemo_text_processing/inverse_text_normalization/pt/verbalizers/decimal.py deleted file mode 100644 index 58fc76ea63e6..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/decimal.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class DecimalFst(GraphFst): - """ - Finite state transducer for verbalizing decimal, - e.g. decimal { negative: "true" integer_part: "1" morphosyntactic_features: "," fractional_part: "26" } -> -1,26 - e.g. decimal { negative: "true" integer_part: "1" morphosyntactic_features: "." fractional_part: "26" } -> -1.26 - e.g. decimal { negative: "false" integer_part: "1" morphosyntactic_features: "," fractional_part: "26" quantity: "millón" } -> 1,26 millón - e.g. decimal { negative: "false" integer_part: "2" quantity: "millones" } -> 2 millones - """ - - def __init__(self): - super().__init__(name="decimal", kind="verbalize") - optionl_sign = pynini.closure(pynini.cross("negative: \"true\"", "-") + delete_space, 0, 1) - integer = ( - pynutil.delete("integer_part:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - optional_integer = pynini.closure(integer + delete_space, 0, 1) - - decimal_point = pynini.cross("morphosyntactic_features: \",\"", ",") - decimal_point |= pynini.cross("morphosyntactic_features: \".\"", ".") - - fractional = ( - decimal_point - + delete_space - + pynutil.delete("fractional_part:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - optional_fractional = pynini.closure(fractional + delete_space, 0, 1) - quantity = ( - pynutil.delete("quantity:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - optional_quantity = pynini.closure(pynutil.insert(" ") + quantity + delete_space, 0, 1) - graph = optional_integer + optional_fractional + optional_quantity - self.numbers = graph - graph = optionl_sign + graph - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/electronic.py b/nemo_text_processing/inverse_text_normalization/pt/verbalizers/electronic.py deleted file mode 100644 index 11b2706a3562..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/electronic.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class ElectronicFst(GraphFst): - """ - Finite state transducer for verbalizing electronic - e.g. tokens { electronic { username: "cdf1" domain: "abc.edu" } } -> cdf1@abc.edu - e.g. tokens { electronic { protocol: "www.abc.edu" } } -> www.abc.edu - """ - - def __init__(self): - super().__init__(name="electronic", kind="verbalize") - user_name = ( - pynutil.delete("username:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - domain = ( - pynutil.delete("domain:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - - protocol = ( - pynutil.delete("protocol:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - - graph = user_name + delete_space + pynutil.insert("@") + domain - graph |= protocol - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/measure.py b/nemo_text_processing/inverse_text_normalization/pt/verbalizers/measure.py deleted file mode 100644 index 057ade696d11..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/measure.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_CHAR, GraphFst, delete_space -from pynini.lib import pynutil - - -class MeasureFst(GraphFst): - """ - Finite state transducer for verbalizing measure, e.g. - measure { cardinal { negative: "true" integer: "12" } units: "kg" } -> -12 kg - - Args: - decimal: DecimalFst - cardinal: CardinalFst - """ - - def __init__(self, decimal: GraphFst, cardinal: GraphFst): - super().__init__(name="measure", kind="verbalize") - optional_sign = pynini.closure(pynini.cross("negative: \"true\"", "-"), 0, 1) - unit = ( - pynutil.delete("units:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_CHAR - " ", 1) - + pynutil.delete("\"") - + delete_space - ) - graph_decimal = ( - pynutil.delete("decimal {") - + delete_space - + optional_sign - + delete_space - + decimal.numbers - + delete_space - + pynutil.delete("}") - ) - graph_cardinal = ( - pynutil.delete("cardinal {") - + delete_space - + optional_sign - + delete_space - + cardinal.numbers - + delete_space - + pynutil.delete("}") - ) - graph = (graph_cardinal | graph_decimal) + delete_space + pynutil.insert(" ") + unit - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/money.py b/nemo_text_processing/inverse_text_normalization/pt/verbalizers/money.py deleted file mode 100644 index 54a9b1038337..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/money.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_CHAR, GraphFst, delete_space, insert_space -from pynini.lib import pynutil - - -class MoneyFst(GraphFst): - """ - Finite state transducer for verbalizing money, e.g. - money { integer_part: "12" morphosyntactic_features: "," fractional_part: "05" currency: "$" } -> $12,05 - - Args: - decimal: DecimalFst - """ - - def __init__(self, decimal: GraphFst): - super().__init__(name="money", kind="verbalize") - unit = ( - pynutil.delete("currency:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_CHAR - " ", 1) - + pynutil.delete("\"") - ) - graph = unit + delete_space + insert_space + decimal.numbers - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/ordinal.py b/nemo_text_processing/inverse_text_normalization/pt/verbalizers/ordinal.py deleted file mode 100644 index fe3454e15e71..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/ordinal.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class OrdinalFst(GraphFst): - """ - Finite state transducer for verbalizing ordinal, e.g. - ordinal { integer: "13" morphosyntactic_features: "o" } -> 13º - """ - - def __init__(self): - super().__init__(name="ordinal", kind="verbalize") - graph = ( - pynutil.delete("integer:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - - replace_suffix = pynini.union( - pynini.cross(" morphosyntactic_features: \"o\"", "º"), - pynini.cross(" morphosyntactic_features: \"a\"", "ª"), - ) - - graph = graph + replace_suffix - - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/telephone.py b/nemo_text_processing/inverse_text_normalization/pt/verbalizers/telephone.py deleted file mode 100644 index 4dd0d7079889..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/telephone.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst -from pynini.lib import pynutil - - -class TelephoneFst(GraphFst): - """ - Finite state transducer for verbalizing telephone, e.g. - telephone { number_part: "123-123-5678" } - -> 123-123-5678 - """ - - def __init__(self): - super().__init__(name="telephone", kind="verbalize") - - number_part = pynutil.delete("number_part: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - delete_tokens = self.delete_tokens(number_part) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/time.py b/nemo_text_processing/inverse_text_normalization/pt/verbalizers/time.py deleted file mode 100755 index b1a04c673752..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/time.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_DIGIT, - NEMO_NOT_QUOTE, - GraphFst, - delete_space, - insert_space, -) -from pynini.lib import pynutil - - -class TimeFst(GraphFst): - """ - Finite state transducer for verbalizing time, - e.g. time { hours: "à 1" minutes: "10" } -> à 1:10 - e.g. time { hours: "às 2" minutes: "45" } -> às 2:45 - """ - - def __init__(self): - super().__init__(name="time", kind="verbalize") - add_leading_zero_to_double_digit = (NEMO_DIGIT + NEMO_DIGIT) | (pynutil.insert("0") + NEMO_DIGIT) - - prefix = ( - pynutil.delete("morphosyntactic_features:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - + delete_space - + insert_space - ) - optional_prefix = pynini.closure(prefix, 0, 1) - - hour = ( - pynutil.delete("hours:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_DIGIT, 1) - + pynutil.delete("\"") - ) - minute = ( - pynutil.delete("minutes:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_DIGIT, 1) - + pynutil.delete("\"") - ) - suffix = ( - delete_space - + insert_space - + pynutil.delete("suffix:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - optional_suffix = pynini.closure(suffix, 0, 1) - - graph = ( - optional_prefix - + hour - + delete_space - + pynutil.insert(":") - + (minute @ add_leading_zero_to_double_digit) - + optional_suffix - ) - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/verbalize.py b/nemo_text_processing/inverse_text_normalization/pt/verbalizers/verbalize.py deleted file mode 100644 index 88c04991b5f4..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/verbalize.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from nemo_text_processing.inverse_text_normalization.pt.verbalizers.cardinal import CardinalFst -from nemo_text_processing.inverse_text_normalization.pt.verbalizers.date import DateFst -from nemo_text_processing.inverse_text_normalization.pt.verbalizers.decimal import DecimalFst -from nemo_text_processing.inverse_text_normalization.pt.verbalizers.electronic import ElectronicFst -from nemo_text_processing.inverse_text_normalization.pt.verbalizers.measure import MeasureFst -from nemo_text_processing.inverse_text_normalization.pt.verbalizers.money import MoneyFst -from nemo_text_processing.inverse_text_normalization.pt.verbalizers.ordinal import OrdinalFst -from nemo_text_processing.inverse_text_normalization.pt.verbalizers.telephone import TelephoneFst -from nemo_text_processing.inverse_text_normalization.pt.verbalizers.time import TimeFst -from nemo_text_processing.inverse_text_normalization.pt.verbalizers.whitelist import WhiteListFst -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst - - -class VerbalizeFst(GraphFst): - """ - Composes other verbalizer grammars. - For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - """ - - def __init__(self): - super().__init__(name="verbalize", kind="verbalize") - cardinal = CardinalFst() - cardinal_graph = cardinal.fst - ordinal_graph = OrdinalFst().fst - decimal = DecimalFst() - decimal_graph = decimal.fst - measure_graph = MeasureFst(decimal=decimal, cardinal=cardinal).fst - money_graph = MoneyFst(decimal=decimal).fst - time_graph = TimeFst().fst - date_graph = DateFst().fst - whitelist_graph = WhiteListFst().fst - telephone_graph = TelephoneFst().fst - electronic_graph = ElectronicFst().fst - - graph = ( - time_graph - | date_graph - | money_graph - | measure_graph - | ordinal_graph - | decimal_graph - | cardinal_graph - | whitelist_graph - | telephone_graph - | electronic_graph - ) - self.fst = graph diff --git a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/verbalize_final.py b/nemo_text_processing/inverse_text_normalization/pt/verbalizers/verbalize_final.py deleted file mode 100644 index cc2e65aed46d..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/verbalize_final.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.pt.verbalizers.verbalize import VerbalizeFst -from nemo_text_processing.inverse_text_normalization.pt.verbalizers.word import WordFst -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst, delete_extra_space, delete_space -from pynini.lib import pynutil - - -class VerbalizeFinalFst(GraphFst): - """ - Finite state transducer that verbalizes an entire sentence, e.g. - tokens { name: "its" } tokens { time { hours: "12" minutes: "30" } } tokens { name: "now" } -> its 12:30 now - """ - - def __init__(self): - super().__init__(name="verbalize_final", kind="verbalize") - verbalize = VerbalizeFst().fst - word = WordFst().fst - types = verbalize | word - graph = ( - pynutil.delete("tokens") - + delete_space - + pynutil.delete("{") - + delete_space - + types - + delete_space - + pynutil.delete("}") - ) - graph = delete_space + pynini.closure(graph + delete_extra_space) + graph + delete_space - self.fst = graph diff --git a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/whitelist.py b/nemo_text_processing/inverse_text_normalization/pt/verbalizers/whitelist.py deleted file mode 100644 index f54aaea65b0a..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/whitelist.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_CHAR, NEMO_SIGMA, GraphFst, delete_space -from pynini.lib import pynutil - - -class WhiteListFst(GraphFst): - """ - Finite state transducer for verbalizing whitelist - e.g. tokens { name: "sexta feira" } -> "sexta-feira" - """ - - def __init__(self): - super().__init__(name="whitelist", kind="verbalize") - graph = ( - pynutil.delete("name:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_CHAR - " ", 1) - + pynutil.delete("\"") - ) - graph = graph @ pynini.cdrewrite(pynini.cross(u"\u00A0", " "), "", "", NEMO_SIGMA) - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/word.py b/nemo_text_processing/inverse_text_normalization/pt/verbalizers/word.py deleted file mode 100644 index 4417d8f0020c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/pt/verbalizers/word.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_CHAR, NEMO_SIGMA, GraphFst, delete_space -from pynini.lib import pynutil - - -class WordFst(GraphFst): - """ - Finite state transducer for verbalizing plain tokens - e.g. tokens { name: "sleep" } -> sleep - """ - - def __init__(self): - super().__init__(name="word", kind="verbalize") - chars = pynini.closure(NEMO_CHAR - " ", 1) - char = pynutil.delete("name:") + delete_space + pynutil.delete("\"") + chars + pynutil.delete("\"") - graph = char @ pynini.cdrewrite(pynini.cross(u"\u00A0", " "), "", "", NEMO_SIGMA) - - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/ru/__init__.py b/nemo_text_processing/inverse_text_normalization/ru/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/inverse_text_normalization/ru/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/ru/taggers/__init__.py b/nemo_text_processing/inverse_text_normalization/ru/taggers/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/ru/taggers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/ru/taggers/cardinal.py b/nemo_text_processing/inverse_text_normalization/ru/taggers/cardinal.py deleted file mode 100644 index e812be117d5d..000000000000 --- a/nemo_text_processing/inverse_text_normalization/ru/taggers/cardinal.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_DIGIT, GraphFst, insert_space -from pynini.lib import pynutil - - -class CardinalFst(GraphFst): - """ - Finite state transducer for classifying cardinals, e.g. - "тысяча один" -> cardinal { integer: "1 001" } - - Args: - tn_cardinal: Text normalization Cardinal graph - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, tn_cardinal: GraphFst, deterministic: bool = True): - super().__init__(name="cardinal", kind="classify", deterministic=deterministic) - - graph = tn_cardinal.cardinal_numbers_default - self.graph = graph.invert().optimize() - - optional_sign = pynini.closure( - pynutil.insert("negative: ") + pynini.cross("минус ", "\"-\"") + insert_space, 0, 1 - ) - - # do not invert numbers less than 10 - graph = pynini.compose(graph, NEMO_DIGIT ** (2, ...)) - graph = optional_sign + pynutil.insert("integer: \"") + graph + pynutil.insert("\"") - graph = self.add_tokens(graph) - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/ru/taggers/date.py b/nemo_text_processing/inverse_text_normalization/ru/taggers/date.py deleted file mode 100644 index 9727626c8924..000000000000 --- a/nemo_text_processing/inverse_text_normalization/ru/taggers/date.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst -from pynini.lib import pynutil - - -class DateFst(GraphFst): - """ - Finite state transducer for classifying date, e.g. - восемнадцатое июня две тысячи второго -> tokens { date { day: "18.06.2002" } } - - Args: - tn_date: Text normalization Date graph - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, tn_date: GraphFst, deterministic: bool = True): - super().__init__(name="date", kind="classify", deterministic=deterministic) - - graph = pynini.invert(tn_date.final_graph).optimize() - graph = self.add_tokens(pynutil.insert("day: \"") + graph + pynutil.insert("\"")) - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/ru/taggers/decimals.py b/nemo_text_processing/inverse_text_normalization/ru/taggers/decimals.py deleted file mode 100644 index 66e8f5f7354b..000000000000 --- a/nemo_text_processing/inverse_text_normalization/ru/taggers/decimals.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_SPACE, GraphFst, delete_extra_space -from pynini.lib import pynutil - - -class DecimalFst(GraphFst): - """ - Finite state transducer for classifying decimal - e.g. "минус три целых две десятых" -> decimal { negative: "true" integer_part: "3," fractional_part: "2" } - - Args: - tn_decimal: Text normalization Decimal graph - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, tn_decimal, deterministic: bool = False): - super().__init__(name="decimal", kind="classify", deterministic=deterministic) - - optional_graph_negative = pynini.closure( - pynutil.insert("negative: ") + pynini.cross("минус", "\"true\"") + delete_extra_space, 0, 1 - ) - - graph_fractional_part = pynini.invert(tn_decimal.graph_fractional).optimize() - graph_integer_part = pynini.invert(tn_decimal.integer_part).optimize() - optional_graph_quantity = pynini.invert(tn_decimal.optional_quantity).optimize() - - graph_fractional = pynutil.insert("fractional_part: \"") + graph_fractional_part + pynutil.insert("\"") - graph_integer = pynutil.insert("integer_part: \"") + graph_integer_part + pynutil.insert("\"") - optional_graph_quantity = pynutil.insert("quantity: \"") + optional_graph_quantity + pynutil.insert("\"") - optional_graph_quantity = pynini.closure(pynini.accep(NEMO_SPACE) + optional_graph_quantity, 0, 1) - - self.final_graph_wo_sign = ( - graph_integer + pynini.accep(NEMO_SPACE) + graph_fractional + optional_graph_quantity - ) - final_graph = optional_graph_negative + self.final_graph_wo_sign - - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/ru/taggers/electronic.py b/nemo_text_processing/inverse_text_normalization/ru/taggers/electronic.py deleted file mode 100644 index f73c8f1b8780..000000000000 --- a/nemo_text_processing/inverse_text_normalization/ru/taggers/electronic.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst -from pynini.lib import pynutil - - -class ElectronicFst(GraphFst): - """ - Finite state transducer for classifying electronic, e.g. - "эй би собака эн ди точка ру" -> electronic { username: "ab@nd.ru" } - - Args: - tn_electronic: Text normalization Electronic graph - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, tn_electronic, deterministic: bool = True): - super().__init__(name="electronic", kind="classify", deterministic=deterministic) - - graph = tn_electronic.final_graph - graph = graph.invert().optimize() - graph = pynutil.insert("username: \"") + graph + pynutil.insert("\"") - graph = self.add_tokens(graph) - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/ru/taggers/measure.py b/nemo_text_processing/inverse_text_normalization/ru/taggers/measure.py deleted file mode 100644 index ef15a61e20ce..000000000000 --- a/nemo_text_processing/inverse_text_normalization/ru/taggers/measure.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst -from pynini.lib import pynutil - - -class MeasureFst(GraphFst): - """ - Finite state transducer for classifying measure, e.g. - "два килограма" -> measure { cardinal { integer: "2 кг" } } - - Args: - tn_measure: Text normalization Cardinal graph - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, tn_measure, deterministic: bool = True): - super().__init__(name="measure", kind="classify", deterministic=deterministic) - - tn_measure = tn_measure.tagger_graph_default @ tn_measure.verbalizer_graph - graph = tn_measure.invert().optimize() - graph = pynutil.insert("cardinal { integer: \"") + graph + pynutil.insert("\" }") - graph = self.add_tokens(graph) - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/ru/taggers/money.py b/nemo_text_processing/inverse_text_normalization/ru/taggers/money.py deleted file mode 100644 index 7477350ec1cc..000000000000 --- a/nemo_text_processing/inverse_text_normalization/ru/taggers/money.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst -from pynini.lib import pynutil - - -class MoneyFst(GraphFst): - """ - Finite state transducer for classifying money, e.g. - "два рубля" -> money { integer_part: "2 руб." } - - Args: - tn_money: Text normalization Money graph - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, tn_money, deterministic: bool = True): - super().__init__(name="money", kind="classify", deterministic=deterministic) - - graph = tn_money.final_graph - graph = graph.invert().optimize() - graph = pynutil.insert("integer_part: \"") + graph + pynutil.insert("\"") - graph = self.add_tokens(graph) - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/ru/taggers/ordinal.py b/nemo_text_processing/inverse_text_normalization/ru/taggers/ordinal.py deleted file mode 100644 index 306054d63d37..000000000000 --- a/nemo_text_processing/inverse_text_normalization/ru/taggers/ordinal.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_DIGIT, GraphFst -from pynini.lib import pynutil - - -class OrdinalFst(GraphFst): - """ - Finite state transducer for classifying ordinals, e.g. - "второе" -> ordinal { integer: "2" } } - - Args: - tn_ordinal: Text normalization Ordinal graph - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, tn_ordinal: GraphFst, deterministic: bool = True): - super().__init__(name="ordinal", kind="classify", deterministic=deterministic) - - tn_ordinal = tn_ordinal.ordinal_numbers - - graph = tn_ordinal.invert().optimize() - self.graph = graph - - # do not invert numbers less than 10 - graph = pynini.compose(graph, NEMO_DIGIT ** (2, ...)) - graph = pynutil.insert("integer: \"") + graph + pynutil.insert("\"") - graph = self.add_tokens(graph) - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/ru/taggers/telephone.py b/nemo_text_processing/inverse_text_normalization/ru/taggers/telephone.py deleted file mode 100644 index 7b54ddc7e443..000000000000 --- a/nemo_text_processing/inverse_text_normalization/ru/taggers/telephone.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst -from pynini.lib import pynutil - - -class TelephoneFst(GraphFst): - """ - Finite state transducer for classifying telephone, e.g. - "восемь девятьсот тринадцать девятьсот восемьдесят три пятьдесят шесть ноль один" -> telephone { number_part: "8-913-983-56-01" } - - Args: - tn_telephone: Text normalization telephone graph - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, tn_telephone: GraphFst, deterministic: bool = True): - super().__init__(name="telephone", kind="classify", deterministic=deterministic) - - tn_telephone = tn_telephone.final_graph - graph = tn_telephone.invert().optimize() - graph = pynutil.insert("number_part: \"") + graph + pynutil.insert("\"") - graph = self.add_tokens(graph) - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/ru/taggers/time.py b/nemo_text_processing/inverse_text_normalization/ru/taggers/time.py deleted file mode 100644 index 50e12c09abb2..000000000000 --- a/nemo_text_processing/inverse_text_normalization/ru/taggers/time.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_SPACE, GraphFst -from nemo_text_processing.text_normalization.ru.verbalizers.time import TimeFst as TNTimeVerbalizer -from pynini.lib import pynutil - - -class TimeFst(GraphFst): - """ - Finite state transducer for classifying time, e.g. - "два часа пятнадцать минут" -> time { hours: "02:15" } - - Args: - tn_time: Text Normalization Time graph - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, tn_time: GraphFst, deterministic: bool = True): - super().__init__(name="time", kind="classify", deterministic=deterministic) - - tn_time_tagger = tn_time.graph_preserve_order - tn_time_verbalizer = TNTimeVerbalizer().graph - tn_time_graph_preserve_order = pynini.compose(tn_time_tagger, tn_time_verbalizer).optimize() - graph_preserve_order = pynini.invert(tn_time_graph_preserve_order).optimize() - graph_preserve_order = pynutil.insert("hours: \"") + graph_preserve_order + pynutil.insert("\"") - - # "пятнадцать минут шестого" -> 17:15 - # Requires permutations for the correct verbalization - m_next_h = ( - pynutil.insert("minutes: \"") - + pynini.invert(tn_time.minutes).optimize() - + pynutil.insert("\"") - + pynini.accep(NEMO_SPACE) - + pynutil.insert("hours: \"") - + pynini.invert(tn_time.increment_hour_ordinal).optimize() - + pynutil.insert("\"") - ).optimize() - - # "без пятнадцати минут шесть" -> 17:45 - # Requires permutation for the correct verbalization - m_to_h = ( - pynini.cross("без ", "minutes: \"") - + pynini.invert(tn_time.mins_to_h) - + pynutil.insert("\"") - + pynini.accep(NEMO_SPACE) - + pynutil.insert("hours: \"") - + pynini.invert(tn_time.increment_hour_cardinal).optimize() - + pynutil.insert("\"") - ) - - graph_reserve_order = m_next_h | m_to_h - graph = graph_preserve_order | graph_reserve_order - graph = self.add_tokens(graph) - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/ru/taggers/tokenize_and_classify.py b/nemo_text_processing/inverse_text_normalization/ru/taggers/tokenize_and_classify.py deleted file mode 100644 index 7f2a833b5230..000000000000 --- a/nemo_text_processing/inverse_text_normalization/ru/taggers/tokenize_and_classify.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -import pynini -from nemo_text_processing.inverse_text_normalization.en.taggers.punctuation import PunctuationFst -from nemo_text_processing.inverse_text_normalization.en.taggers.word import WordFst -from nemo_text_processing.inverse_text_normalization.ru.taggers.cardinal import CardinalFst -from nemo_text_processing.inverse_text_normalization.ru.taggers.date import DateFst -from nemo_text_processing.inverse_text_normalization.ru.taggers.decimals import DecimalFst -from nemo_text_processing.inverse_text_normalization.ru.taggers.electronic import ElectronicFst -from nemo_text_processing.inverse_text_normalization.ru.taggers.measure import MeasureFst -from nemo_text_processing.inverse_text_normalization.ru.taggers.money import MoneyFst -from nemo_text_processing.inverse_text_normalization.ru.taggers.ordinal import OrdinalFst -from nemo_text_processing.inverse_text_normalization.ru.taggers.telephone import TelephoneFst -from nemo_text_processing.inverse_text_normalization.ru.taggers.time import TimeFst -from nemo_text_processing.inverse_text_normalization.ru.taggers.whitelist import WhiteListFst -from nemo_text_processing.text_normalization.en.graph_utils import ( - GraphFst, - delete_extra_space, - delete_space, - generator_main, -) -from nemo_text_processing.text_normalization.ru.taggers.tokenize_and_classify import ClassifyFst as TNClassifyFst -from pynini.lib import pynutil - -from nemo.utils import logging - - -class ClassifyFst(GraphFst): - """ - Final class that composes all other classification grammars. This class can process an entire sentence, that is lower cased. - For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - - Args: - cache_dir: path to a dir with .far grammar file. Set to None to avoid using cache. - overwrite_cache: set to True to overwrite .far files - """ - - def __init__(self, cache_dir: str = None, overwrite_cache: bool = False): - super().__init__(name="tokenize_and_classify", kind="classify") - - far_file = None - if cache_dir is not None and cache_dir != "None": - os.makedirs(cache_dir, exist_ok=True) - far_file = os.path.join(cache_dir, "_ru_itn.far") - if not overwrite_cache and far_file and os.path.exists(far_file): - self.fst = pynini.Far(far_file, mode="r")["tokenize_and_classify"] - logging.info(f"ClassifyFst.fst was restored from {far_file}.") - else: - logging.info(f"Creating ClassifyFst grammars. This might take some time...") - tn_classify = TNClassifyFst( - input_case='cased', deterministic=False, cache_dir=cache_dir, overwrite_cache=True - ) - cardinal = CardinalFst(tn_cardinal=tn_classify.cardinal) - cardinal_graph = cardinal.fst - - ordinal = OrdinalFst(tn_ordinal=tn_classify.ordinal) - ordinal_graph = ordinal.fst - - decimal = DecimalFst(tn_decimal=tn_classify.decimal) - decimal_graph = decimal.fst - - measure_graph = MeasureFst(tn_measure=tn_classify.measure).fst - date_graph = DateFst(tn_date=tn_classify.date).fst - word_graph = WordFst().fst - time_graph = TimeFst(tn_time=tn_classify.time).fst - money_graph = MoneyFst(tn_money=tn_classify.money).fst - whitelist_graph = WhiteListFst().fst - punct_graph = PunctuationFst().fst - electronic_graph = ElectronicFst(tn_electronic=tn_classify.electronic).fst - telephone_graph = TelephoneFst(tn_telephone=tn_classify.telephone).fst - - classify = ( - pynutil.add_weight(whitelist_graph, 1.01) - | pynutil.add_weight(time_graph, 1.1) - | pynutil.add_weight(date_graph, 1.09) - | pynutil.add_weight(decimal_graph, 1.1) - | pynutil.add_weight(measure_graph, 1.1) - | pynutil.add_weight(ordinal_graph, 1.1) - | pynutil.add_weight(money_graph, 1.1) - | pynutil.add_weight(telephone_graph, 1.1) - | pynutil.add_weight(electronic_graph, 1.1) - | pynutil.add_weight(cardinal_graph, 1.1) - | pynutil.add_weight(word_graph, 100) - ) - - punct = pynutil.insert("tokens { ") + pynutil.add_weight(punct_graph, weight=1.1) + pynutil.insert(" }") - token = pynutil.insert("tokens { ") + classify + pynutil.insert(" }") - token_plus_punct = ( - pynini.closure(punct + pynutil.insert(" ")) + token + pynini.closure(pynutil.insert(" ") + punct) - ) - - graph = token_plus_punct + pynini.closure(pynutil.add_weight(delete_extra_space, 1.1) + token_plus_punct) - - graph = delete_space + graph + delete_space - self.fst = graph.optimize() - - if far_file: - generator_main(far_file, {"tokenize_and_classify": self.fst}) - logging.info(f"ClassifyFst grammars are saved to {far_file}.") diff --git a/nemo_text_processing/inverse_text_normalization/ru/taggers/whitelist.py b/nemo_text_processing/inverse_text_normalization/ru/taggers/whitelist.py deleted file mode 100644 index def15e5e56cd..000000000000 --- a/nemo_text_processing/inverse_text_normalization/ru/taggers/whitelist.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst, convert_space -from nemo_text_processing.text_normalization.ru.utils import get_abs_path -from pynini.lib import pynutil - - -class WhiteListFst(GraphFst): - """ - Finite state transducer for classifying whitelist, e.g. - "квартира" -> telephone { number_part: "кв." } - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="whitelist", kind="classify", deterministic=deterministic) - whitelist = pynini.string_file(get_abs_path("data/whitelist.tsv")).invert() - graph = pynutil.insert("name: \"") + convert_space(whitelist) + pynutil.insert("\"") - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/ru/verbalizers/__init__.py b/nemo_text_processing/inverse_text_normalization/ru/verbalizers/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/ru/verbalizers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/ru/verbalizers/cardinal.py b/nemo_text_processing/inverse_text_normalization/ru/verbalizers/cardinal.py deleted file mode 100644 index 12e2451f7044..000000000000 --- a/nemo_text_processing/inverse_text_normalization/ru/verbalizers/cardinal.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class CardinalFst(GraphFst): - """ - Finite state transducer for verbalizing roman numerals - e.g. cardinal { integer: "1 001" } -> 1 001 - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="cardinal", kind="verbalize", deterministic=deterministic) - - optional_sign = pynini.closure( - pynutil.delete("negative:") - + delete_space - + pynutil.delete("\"") - + NEMO_NOT_QUOTE - + pynutil.delete("\"") - + delete_space, - 0, - 1, - ) - - graph = ( - optional_sign + pynutil.delete("integer: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - ) - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/ru/verbalizers/date.py b/nemo_text_processing/inverse_text_normalization/ru/verbalizers/date.py deleted file mode 100644 index 02cce48dae10..000000000000 --- a/nemo_text_processing/inverse_text_normalization/ru/verbalizers/date.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst -from pynini.lib import pynutil - - -class DateFst(GraphFst): - """ - Finite state transducer for verbalizing date, e.g. - date { day: "02.03.89" } -> "02.03.89" - """ - - def __init__(self): - super().__init__(name="date", kind="verbalize") - graph = pynutil.delete("day: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - delete_tokens = self.delete_tokens(graph.optimize()) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/ru/verbalizers/decimal.py b/nemo_text_processing/inverse_text_normalization/ru/verbalizers/decimal.py deleted file mode 100644 index cf8cefc5d50d..000000000000 --- a/nemo_text_processing/inverse_text_normalization/ru/verbalizers/decimal.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, NEMO_SPACE, GraphFst, delete_space -from pynini.lib import pynutil - - -class DecimalFst(GraphFst): - """ - Finite state transducer for verbalizing decimal, e.g. - decimal { negative: "true" integer_part: "3," fractional_part: "2" } -> -3,2 - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="decimal", kind="verbalize", deterministic=deterministic) - - optional_sign = pynini.closure(pynini.cross("negative: \"true\" ", "-"), 0, 1) - - integer = pynutil.delete(" \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - integer_part = pynutil.delete("integer_part:") + integer - fractional_part = pynutil.delete("fractional_part:") + integer - optional_quantity = pynini.closure(pynini.accep(NEMO_SPACE) + pynutil.delete("quantity:") + integer, 0, 1) - - graph = optional_sign + integer_part + delete_space + fractional_part + optional_quantity - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/ru/verbalizers/electronic.py b/nemo_text_processing/inverse_text_normalization/ru/verbalizers/electronic.py deleted file mode 100644 index 158cd9893b87..000000000000 --- a/nemo_text_processing/inverse_text_normalization/ru/verbalizers/electronic.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst -from pynini.lib import pynutil - - -class ElectronicFst(GraphFst): - """ - Finite state transducer for verbalizing electronic - e.g. electronic { username: "ab@nd.ru" } -> "ab@nd.ru" - """ - - def __init__(self): - super().__init__(name="electronic", kind="verbalize") - - graph = pynutil.delete("username: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/ru/verbalizers/measure.py b/nemo_text_processing/inverse_text_normalization/ru/verbalizers/measure.py deleted file mode 100644 index 3cae67bac289..000000000000 --- a/nemo_text_processing/inverse_text_normalization/ru/verbalizers/measure.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class MeasureFst(GraphFst): - """ - Finite state transducer for verbalizing measure - e.g. measure { cardinal { integer: "2 кг" } } -> "2 кг" - """ - - def __init__(self): - super().__init__(name="measure", kind="verbalize") - - graph = ( - pynutil.delete(" cardinal { integer: \"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - + delete_space - + pynutil.delete("}") - ) - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/ru/verbalizers/money.py b/nemo_text_processing/inverse_text_normalization/ru/verbalizers/money.py deleted file mode 100644 index af59d33db5d9..000000000000 --- a/nemo_text_processing/inverse_text_normalization/ru/verbalizers/money.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst -from pynini.lib import pynutil - - -class MoneyFst(GraphFst): - """ - Finite state transducer for verbalizing electronic - e.g. money { integer_part: "2 руб." } -> "2 руб." - """ - - def __init__(self): - super().__init__(name="money", kind="verbalize") - - graph = pynutil.delete("integer_part: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/ru/verbalizers/ordinal.py b/nemo_text_processing/inverse_text_normalization/ru/verbalizers/ordinal.py deleted file mode 100644 index 31ff9b3c6063..000000000000 --- a/nemo_text_processing/inverse_text_normalization/ru/verbalizers/ordinal.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst -from pynini.lib import pynutil - - -class OrdinalFst(GraphFst): - """ - Finite state transducer for verbalizing ordinal numbers - e.g. ordinal { integer: "2" } -> "2" - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="ordinal", kind="verbalize", deterministic=deterministic) - - value = pynini.closure(NEMO_NOT_QUOTE) - graph = pynutil.delete("integer: \"") + value + pynutil.delete("\"") - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/ru/verbalizers/telephone.py b/nemo_text_processing/inverse_text_normalization/ru/verbalizers/telephone.py deleted file mode 100644 index 06c705dc4b61..000000000000 --- a/nemo_text_processing/inverse_text_normalization/ru/verbalizers/telephone.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst -from pynini.lib import pynutil - - -class TelephoneFst(GraphFst): - """ - Finite state transducer for verbalizing telephone - e.g. telephone { number_part: "8-913-983-56-01" } -> "8-913-983-56-01" - """ - - def __init__(self): - super().__init__(name="telephone", kind="verbalize") - - graph = pynutil.delete("number_part: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/ru/verbalizers/time.py b/nemo_text_processing/inverse_text_normalization/ru/verbalizers/time.py deleted file mode 100644 index edfe7b8988ec..000000000000 --- a/nemo_text_processing/inverse_text_normalization/ru/verbalizers/time.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class TimeFst(GraphFst): - """ - Finite state transducer for verbalizing time - e.g. time { hours: "02:15" } -> "02:15" - """ - - def __init__(self): - super().__init__(name="time", kind="verbalize") - hour = ( - pynutil.delete("hours: ") + pynutil.delete("\"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - ) - minutes = ( - pynutil.delete("minutes: ") - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - - graph_preserve_order = pynutil.delete("hours: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - - # for cases that require permutations for the correct verbalization - graph_reverse_order = hour + delete_space + pynutil.insert(":") + minutes + delete_space - - graph = graph_preserve_order | graph_reverse_order - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/ru/verbalizers/verbalize.py b/nemo_text_processing/inverse_text_normalization/ru/verbalizers/verbalize.py deleted file mode 100644 index 39a2bebe7474..000000000000 --- a/nemo_text_processing/inverse_text_normalization/ru/verbalizers/verbalize.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from nemo_text_processing.inverse_text_normalization.en.verbalizers.whitelist import WhiteListFst -from nemo_text_processing.inverse_text_normalization.ru.verbalizers.cardinal import CardinalFst -from nemo_text_processing.inverse_text_normalization.ru.verbalizers.date import DateFst -from nemo_text_processing.inverse_text_normalization.ru.verbalizers.decimal import DecimalFst -from nemo_text_processing.inverse_text_normalization.ru.verbalizers.electronic import ElectronicFst -from nemo_text_processing.inverse_text_normalization.ru.verbalizers.measure import MeasureFst -from nemo_text_processing.inverse_text_normalization.ru.verbalizers.money import MoneyFst -from nemo_text_processing.inverse_text_normalization.ru.verbalizers.ordinal import OrdinalFst -from nemo_text_processing.inverse_text_normalization.ru.verbalizers.telephone import TelephoneFst -from nemo_text_processing.inverse_text_normalization.ru.verbalizers.time import TimeFst -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst - - -class VerbalizeFst(GraphFst): - """ - Composes other verbalizer grammars. - For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - """ - - def __init__(self): - super().__init__(name="verbalize", kind="verbalize") - cardinal = CardinalFst() - cardinal_graph = cardinal.fst - ordinal = OrdinalFst() - ordinal_graph = ordinal.fst - decimal = DecimalFst() - decimal_graph = decimal.fst - whitelist_graph = WhiteListFst().fst - electronic_graph = ElectronicFst().fst - money_graph = MoneyFst().fst - date_graph = DateFst().fst - measure_graph = MeasureFst().fst - telephone_graph = TelephoneFst().fst - time_graph = TimeFst().fst - - graph = ( - whitelist_graph - | cardinal_graph - | ordinal_graph - | decimal_graph - | electronic_graph - | date_graph - | money_graph - | measure_graph - | telephone_graph - | time_graph - ) - - self.fst = graph diff --git a/nemo_text_processing/inverse_text_normalization/ru/verbalizers/verbalize_final.py b/nemo_text_processing/inverse_text_normalization/ru/verbalizers/verbalize_final.py deleted file mode 100644 index f409537d2d41..000000000000 --- a/nemo_text_processing/inverse_text_normalization/ru/verbalizers/verbalize_final.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.en.verbalizers.word import WordFst -from nemo_text_processing.inverse_text_normalization.ru.verbalizers.verbalize import VerbalizeFst -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst, delete_extra_space, delete_space -from pynini.lib import pynutil - - -class VerbalizeFinalFst(GraphFst): - """ - Finite state transducer that verbalizes an entire sentence, e.g. - tokens { name: "its" } tokens { time { hours: "12" minutes: "30" } } tokens { name: "now" } -> its 12:30 now - """ - - def __init__(self): - super().__init__(name="verbalize_final", kind="verbalize") - verbalize = VerbalizeFst().fst - word = WordFst().fst - types = verbalize | word - graph = ( - pynutil.delete("tokens") - + delete_space - + pynutil.delete("{") - + delete_space - + types - + delete_space - + pynutil.delete("}") - ) - graph = delete_space + pynini.closure(graph + delete_extra_space) + graph + delete_space - self.fst = graph diff --git a/nemo_text_processing/inverse_text_normalization/run_evaluate.py b/nemo_text_processing/inverse_text_normalization/run_evaluate.py deleted file mode 100644 index d2b3b63305ad..000000000000 --- a/nemo_text_processing/inverse_text_normalization/run_evaluate.py +++ /dev/null @@ -1,113 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from argparse import ArgumentParser - -from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer -from nemo_text_processing.text_normalization.data_loader_utils import ( - evaluate, - known_types, - load_files, - training_data_to_sentences, - training_data_to_tokens, -) - - -''' -Runs Evaluation on data in the format of : \t\t<`self` if trivial class or normalized text> -like the Google text normalization data https://www.kaggle.com/richardwilliamsproat/text-normalization-for-english-russian-and-polish -''' - - -def parse_args(): - parser = ArgumentParser() - parser.add_argument("--input", help="input file path", type=str) - parser.add_argument( - "--lang", help="language", choices=['en', 'de', 'es', 'pt', 'ru', 'fr', 'vi'], default="en", type=str - ) - parser.add_argument( - "--cat", - dest="category", - help="focus on class only (" + ", ".join(known_types) + ")", - type=str, - default=None, - choices=known_types, - ) - parser.add_argument("--filter", action='store_true', help="clean data for inverse normalization purposes") - return parser.parse_args() - - -if __name__ == "__main__": - # Example usage: - # python run_evaluate.py --input= --cat= --filter - args = parse_args() - if args.lang == 'en': - from nemo_text_processing.inverse_text_normalization.en.clean_eval_data import filter_loaded_data - file_path = args.input - inverse_normalizer = InverseNormalizer() - - print("Loading training data: " + file_path) - training_data = load_files([file_path]) - - if args.filter: - training_data = filter_loaded_data(training_data) - - if args.category is None: - print("Sentence level evaluation...") - sentences_un_normalized, sentences_normalized, _ = training_data_to_sentences(training_data) - print("- Data: " + str(len(sentences_normalized)) + " sentences") - sentences_prediction = inverse_normalizer.inverse_normalize_list(sentences_normalized) - print("- Denormalized. Evaluating...") - sentences_accuracy = evaluate( - preds=sentences_prediction, labels=sentences_un_normalized, input=sentences_normalized - ) - print("- Accuracy: " + str(sentences_accuracy)) - - print("Token level evaluation...") - tokens_per_type = training_data_to_tokens(training_data, category=args.category) - token_accuracy = {} - for token_type in tokens_per_type: - print("- Token type: " + token_type) - tokens_un_normalized, tokens_normalized = tokens_per_type[token_type] - print(" - Data: " + str(len(tokens_normalized)) + " tokens") - tokens_prediction = inverse_normalizer.inverse_normalize_list(tokens_normalized) - print(" - Denormalized. Evaluating...") - token_accuracy[token_type] = evaluate(tokens_prediction, tokens_un_normalized, input=tokens_normalized) - print(" - Accuracy: " + str(token_accuracy[token_type])) - token_count_per_type = {token_type: len(tokens_per_type[token_type][0]) for token_type in tokens_per_type} - token_weighted_accuracy = [ - token_count_per_type[token_type] * accuracy for token_type, accuracy in token_accuracy.items() - ] - print("- Accuracy: " + str(sum(token_weighted_accuracy) / sum(token_count_per_type.values()))) - - print(" - Total: " + str(sum(token_count_per_type.values())), '\n') - - for token_type in token_accuracy: - if token_type not in known_types: - raise ValueError("Unexpected token type: " + token_type) - - if args.category is None: - c1 = ['Class', 'sent level'] + known_types - c2 = ['Num Tokens', len(sentences_normalized)] + [ - token_count_per_type[known_type] if known_type in tokens_per_type else '0' for known_type in known_types - ] - c3 = ["Denormalization", sentences_accuracy] + [ - token_accuracy[known_type] if known_type in token_accuracy else '0' for known_type in known_types - ] - - for i in range(len(c1)): - print(f'{str(c1[i]):10s} | {str(c2[i]):10s} | {str(c3[i]):5s}') - else: - print(f'numbers\t{token_count_per_type[args.category]}') - print(f'Denormalization\t{token_accuracy[args.category]}') diff --git a/nemo_text_processing/inverse_text_normalization/vi/__init__.py b/nemo_text_processing/inverse_text_normalization/vi/__init__.py deleted file mode 100644 index 9af8257c226e..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from nemo_text_processing.inverse_text_normalization.vi.taggers.tokenize_and_classify import ClassifyFst -from nemo_text_processing.inverse_text_normalization.vi.verbalizers.verbalize import VerbalizeFst -from nemo_text_processing.inverse_text_normalization.vi.verbalizers.verbalize_final import VerbalizeFinalFst diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/__init__.py b/nemo_text_processing/inverse_text_normalization/vi/data/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/currency.tsv b/nemo_text_processing/inverse_text_normalization/vi/data/currency.tsv deleted file mode 100644 index ce65d4420cb6..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/currency.tsv +++ /dev/null @@ -1,11 +0,0 @@ -$ đô -$ usd -$ đô la -$ đô la mỹ -€ euro -€ ơ rô -¥ yên -₩ won -₩ uôn -RM ringgit -₫ đồng \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/electronic/__init__.py b/nemo_text_processing/inverse_text_normalization/vi/data/electronic/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/electronic/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/electronic/domain.tsv b/nemo_text_processing/inverse_text_normalization/vi/data/electronic/domain.tsv deleted file mode 100644 index c56c252adbea..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/electronic/domain.tsv +++ /dev/null @@ -1,11 +0,0 @@ -com -uk -fr -net -br -in -ru -de -it -vn -edu \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/electronic/server_name.tsv b/nemo_text_processing/inverse_text_normalization/vi/data/electronic/server_name.tsv deleted file mode 100644 index 4eadcaf5915a..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/electronic/server_name.tsv +++ /dev/null @@ -1,16 +0,0 @@ -g mail gmail -gmail -n vidia nvidia -nvidia -outlook -hotmail -yahoo -aol -gmx -msn -live -yandex -orange -wanadoo -web -comcast \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/electronic/symbols.tsv b/nemo_text_processing/inverse_text_normalization/vi/data/electronic/symbols.tsv deleted file mode 100644 index eccbe3d47512..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/electronic/symbols.tsv +++ /dev/null @@ -1,20 +0,0 @@ -. chấm -- gạch -_ gạch dưới -_ shift gạch -_ síp gạch -! chấm than -# thăng -$ đô -% phần trăm -& và -* sao -: hai chấm -+ cộng -/ sẹc -= bằng -? chấm hỏi -? hỏi chấm -^ mũ -| hoặc -, phẩy \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/magnitudes.tsv b/nemo_text_processing/inverse_text_normalization/vi/data/magnitudes.tsv deleted file mode 100644 index 727dc567011c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/magnitudes.tsv +++ /dev/null @@ -1,5 +0,0 @@ -k nghìn -k ngàn -m triệu -b tỉ -b tỷ \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/math/__init__.py b/nemo_text_processing/inverse_text_normalization/vi/data/math/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/math/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/math/symbols.tsv b/nemo_text_processing/inverse_text_normalization/vi/data/math/symbols.tsv deleted file mode 100644 index 2475c6ec39b8..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/math/symbols.tsv +++ /dev/null @@ -1,10 +0,0 @@ -% phần trăm -& và -* nhân -* sao -+ cộng -- trừ -/ chia -= bằng -^ mũ -| hoặc \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/measurements.tsv b/nemo_text_processing/inverse_text_normalization/vi/data/measurements.tsv deleted file mode 100644 index fa28177c8239..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/measurements.tsv +++ /dev/null @@ -1,185 +0,0 @@ -°f fahrenheit -°f độ f -°c celsius -°c độ c -km kilomet -km ki lô met -km ki lô mét -km kilô mét -km kilo mét -m met -m mét -cm centimet -cm cen ti mét -cm xen ti mét -cm xăng ti mét -mm millimet -mm mi li mét -mm mili mét -ha hecta -ha héc ta -mi mile -m² met vuông -m² mét vuông -km² kilomet vuông -km² ki lô met vuông -km² kilo mét vuông -km² ki lô mét vuông -ft foot -% phần trăm -hz héc -hz hẹc -kw kilowatt -kw kilo watt -kw ki lô watt -kw ki lô oát -kw ki lô goát -hp mã lực -mg milligram -mg milli gram -mg mi li gram -mg mi li gam -mg mili gam -kg kilogram -kg kilo gram -kg ki lô gram -kg kilo gam -kg ki lô gam -kg kí lô -kg kí -ghz giga hẹc -ghz giga héc -ghz gi ga héc -ghz gi ga hẹc -khz kilo hẹc -khz kilo héc -khz ki lô héc -khz ki lô hẹc -mhz mega héc -mhz mega hẹc -mhz mê ga hẹc -mhz mê ga héc -v vôn -h giờ -phút phút -s giây -nm nanomet -nm nano mét -nm na nô mét -mA milli ampe -mA mi li ampe -mA mi li am pe -kwh kilowatt giờ -kwh kilo watt giờ -kwh ki lô oát giờ -kwh ki lô goát giờ -m³ met khối -m³ mét khối -cm³ centimet khối -cm³ cen ti mét khối -cm³ xen ti mét khối -cm³ xăng ti mét khối -tw tera watt -tw terawatt -tw te ra watt -tw tê ra watt -tw tê ra oát -tw tê ra goát -mv milli vôn -mv mi li vôn -mw megawatt -mw mega watt -mw mê ga watt -mw mê ga oát -mw mê ga goát -μm micromet -μm micro mét -μm muy crô mét -μm mi crô mét -μm muy cờ rô mét -μm mi cờ rô mét -inch inch -tb terabyte -tb tera byte -tb te ra byte -tb te ra bai -tb tê ra byte -tb tê ra bai -g gram -g gam -ω ohm -ω ôm -db decibel -db deci ben -db đề xi ben -μg microgram -μg micro gram -μg muy crô gram -μg micro gam -μg muy crô gam -μg muy cờ rô gam -pg petagram -pg peta gam -pg pê ta gam -gb gigabyte -gb giga byte -gb giga bai -gb gi ga byte -gb gi ga bai -mb megabyte -mb mega byte -mb mega bai -mb me ga byte -mb me ga bai -mb mê ga byte -mb mê ga bai -kb kilobyte -kb kilo byte -kb kilo bai -kb ki lô byte -kb ki lô bai -kbps kilobit trên giây -mbps megabit trên giây -kv kilovolt -kv kilo vôn -kv ki lô vôn -mv megavolt -mv mega vôn -mv me ga vôn -mv mê ga vôn -yd yard -rad radian -gpa giga pascal -gpa gi ga pascal -gw gigawatt -gw giga watt -gw gi ga watt -gw gi ga oát -ns nano giây -ns na nô giây -μs micro giây -μs muy crô giây -μs muy cờ rô giây -pa pascal -ms milli giây -ms mi li giây -dm decimet -dm đề xi mét -dm³ đề xi mét khối -dm³ decimet khối -mm² millimet vuông -mm² mi li mét vuông -cm² centimet vuông -cm² centi mét vuông -cm² xăng ti mét vuông -cm² xen ti mét vuông -pb peta byte -pb petabyte -pb pê ta byte -kcal kilocalories -kcal kilocalo -kcal ki lô calo -l lít -ml millilit -ml mili lít -ml mi li lít \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/months.tsv b/nemo_text_processing/inverse_text_normalization/vi/data/months.tsv deleted file mode 100644 index 93da6892e2ba..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/months.tsv +++ /dev/null @@ -1,14 +0,0 @@ -một 1 -hai 2 -ba 3 -tư 4 -bốn 4 -năm 5 -sáu 6 -bảy 7 -bẩy 7 -tám 8 -chín 9 -mười 10 -mười một 11 -mười hai 12 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/numbers/__init__.py b/nemo_text_processing/inverse_text_normalization/vi/data/numbers/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/numbers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/numbers/digit.tsv b/nemo_text_processing/inverse_text_normalization/vi/data/numbers/digit.tsv deleted file mode 100644 index bdac5ded18ff..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/numbers/digit.tsv +++ /dev/null @@ -1,10 +0,0 @@ -một 1 -hai 2 -ba 3 -bốn 4 -năm 5 -sáu 6 -bảy 7 -bẩy 7 -tám 8 -chín 9 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/numbers/hundred.tsv b/nemo_text_processing/inverse_text_normalization/vi/data/numbers/hundred.tsv deleted file mode 100644 index b2f35ae9fbf3..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/numbers/hundred.tsv +++ /dev/null @@ -1 +0,0 @@ -trăm \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/numbers/teen.tsv b/nemo_text_processing/inverse_text_normalization/vi/data/numbers/teen.tsv deleted file mode 100644 index 722dc678d4eb..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/numbers/teen.tsv +++ /dev/null @@ -1,11 +0,0 @@ -mười 10 -mười một 11 -mười hai 12 -mười ba 13 -mười bốn 14 -mười lăm 15 -mười sáu 16 -mười bảy 17 -mười bẩy 17 -mười tám 18 -mười chín 19 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/numbers/thousands.tsv b/nemo_text_processing/inverse_text_normalization/vi/data/numbers/thousands.tsv deleted file mode 100644 index d2ca259e8be8..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/numbers/thousands.tsv +++ /dev/null @@ -1,6 +0,0 @@ -nghìn -triệu -tỉ -nghìn tỉ -triệu tỉ -tỉ tỉ \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/numbers/ties.tsv b/nemo_text_processing/inverse_text_normalization/vi/data/numbers/ties.tsv deleted file mode 100644 index cca1f8008036..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/numbers/ties.tsv +++ /dev/null @@ -1,9 +0,0 @@ -hai 2 -ba 3 -bốn 4 -năm 5 -sáu 6 -bảy 7 -bẩy 7 -tám 8 -chín 9 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/numbers/zero.tsv b/nemo_text_processing/inverse_text_normalization/vi/data/numbers/zero.tsv deleted file mode 100644 index 70d6dcf26ae2..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/numbers/zero.tsv +++ /dev/null @@ -1 +0,0 @@ -không 0 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/ordinals/__init__.py b/nemo_text_processing/inverse_text_normalization/vi/data/ordinals/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/ordinals/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/ordinals/digit.tsv b/nemo_text_processing/inverse_text_normalization/vi/data/ordinals/digit.tsv deleted file mode 100644 index a1403b7ceb4b..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/ordinals/digit.tsv +++ /dev/null @@ -1,12 +0,0 @@ -nhất 1 -nhì 2 -hai 2 -ba 3 -bốn 4 -tư 4 -năm 5 -sáu 6 -bảy 7 -bẩy 7 -tám 8 -chín 9 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/time/__init__.py b/nemo_text_processing/inverse_text_normalization/vi/data/time/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/time/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/time/hours.tsv b/nemo_text_processing/inverse_text_normalization/vi/data/time/hours.tsv deleted file mode 100644 index 48e35fc90130..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/time/hours.tsv +++ /dev/null @@ -1,29 +0,0 @@ -không 0 -một 1 -hai 2 -ba 3 -bốn 4 -năm 5 -sáu 6 -bảy 7 -bẩy 7 -tám 8 -chín 9 -mười 10 -mười một 11 -mười hai 12 -mười ba 13 -mười bốn 14 -mười lăm 15 -mười sáu 16 -mười bảy 17 -mười bẩy 17 -mười tám 18 -mười chín 19 -hai mươi 20 -hai mươi mốt 21 -hai mốt 21 -hai mươi hai 22 -hai hai 22 -hai mươi ba 23 -hai ba 23 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/time/hours_to.tsv b/nemo_text_processing/inverse_text_normalization/vi/data/time/hours_to.tsv deleted file mode 100644 index a56219579975..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/time/hours_to.tsv +++ /dev/null @@ -1,25 +0,0 @@ -1 0 -2 1 -3 2 -4 3 -5 4 -6 5 -7 6 -8 7 -9 8 -10 9 -11 10 -12 11 -13 12 -14 13 -15 14 -16 15 -17 16 -18 17 -19 18 -20 19 -21 20 -22 21 -23 22 -24 23 -0 23 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/time/hours_to_night.tsv b/nemo_text_processing/inverse_text_normalization/vi/data/time/hours_to_night.tsv deleted file mode 100644 index 1374f45c0f5b..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/time/hours_to_night.tsv +++ /dev/null @@ -1,12 +0,0 @@ -1 13 -2 14 -3 15 -4 16 -5 17 -6 18 -7 19 -8 20 -9 21 -10 22 -11 23 -12 0 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/time/minutes.tsv b/nemo_text_processing/inverse_text_normalization/vi/data/time/minutes.tsv deleted file mode 100644 index 0d7872b948f4..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/time/minutes.tsv +++ /dev/null @@ -1,118 +0,0 @@ -không 0 -một 1 -hai 2 -ba 3 -bốn 4 -năm 5 -sáu 6 -bảy 7 -bẩy 7 -tám 8 -chín 9 -mười 10 -mười một 11 -mười hai 12 -mười ba 13 -mười bốn 14 -mười lăm 15 -mười sáu 16 -mười bảy 17 -mười bẩy 17 -mười tám 18 -mười chín 19 -hai mươi 20 -hai mươi mốt 21 -hai mươi hai 22 -hai mươi ba 23 -hai mươi tư 24 -hai mươi bốn 24 -hai mươi lăm 25 -hai mươi sáu 26 -hai mươi bảy 27 -hai mươi bẩy 27 -hai mươi tám 28 -hai mươi chín 29 -ba mươi 30 -ba mươi mốt 21 -ba mươi hai 22 -ba mươi ba 33 -ba mươi tư 34 -ba mươi bốn 34 -ba mươi lăm 35 -ba mươi sáu 36 -ba mươi bảy 37 -ba mươi bẩy 37 -ba mươi tám 38 -ba mươi chín 39 -bốn mươi 40 -bốn mươi mốt 41 -bốn mươi hai 42 -bốn mươi ba 43 -bốn mươi tư 44 -bốn mươi bốn 44 -bốn mươi lăm 45 -bốn mươi sáu 46 -bốn mươi bảy 47 -bốn mươi bẩy 47 -bốn mươi tám 48 -bốn mươi chín 49 -năm mươi 50 -năm mươi mốt 51 -năm mươi hai 52 -năm mươi ba 53 -năm mươi tư 54 -năm mươi bốn 54 -năm mươi lăm 55 -năm mươi sáu 56 -năm mươi bảy 57 -năm mươi bẩy 57 -năm mươi tám 58 -năm mươi chín 59 -hai mốt 21 -hai hai 22 -hai ba 23 -hai tư 24 -hai bốn 24 -hai lăm 25 -hai năm 25 -hai sáu 26 -hai bảy 27 -hai bẩy 27 -hai tám 28 -hai chín 29 -ba mốt 21 -ba hai 22 -ba ba 33 -ba tư 34 -ba bốn 34 -ba lăm 35 -ba năm 35 -ba sáu 36 -ba bảy 37 -ba bẩy 37 -ba tám 38 -ba chín 39 -bốn mốt 41 -bốn hai 42 -bốn ba 43 -bốn tư 44 -bốn bốn 44 -bốn lăm 45 -bốn năm 45 -bốn sáu 46 -bốn bảy 47 -bốn bẩy 47 -bốn tám 48 -bốn chín 49 -năm mốt 51 -năm hai 52 -năm ba 53 -năm tư 54 -năm bốn 54 -năm lăm 55 -năm năm 55 -năm sáu 56 -năm bảy 57 -năm bẩy 57 -năm tám 58 -năm chín 59 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/time/minutes_to.tsv b/nemo_text_processing/inverse_text_normalization/vi/data/time/minutes_to.tsv deleted file mode 100644 index bcb3283651c4..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/time/minutes_to.tsv +++ /dev/null @@ -1,59 +0,0 @@ -1 59 -2 58 -3 57 -4 56 -5 55 -6 54 -7 53 -8 52 -9 51 -10 50 -11 49 -12 48 -13 47 -14 46 -15 45 -16 44 -17 43 -18 42 -19 41 -20 40 -21 39 -22 38 -23 37 -24 36 -25 35 -26 34 -27 33 -28 32 -29 31 -30 30 -31 29 -32 28 -33 27 -34 26 -35 25 -36 24 -37 23 -38 22 -39 21 -40 20 -41 19 -42 18 -43 17 -44 16 -45 15 -46 14 -47 13 -48 12 -49 11 -50 10 -51 9 -52 8 -53 7 -54 6 -55 5 -56 4 -57 3 -58 2 -59 1 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/time/time_suffix.tsv b/nemo_text_processing/inverse_text_normalization/vi/data/time/time_suffix.tsv deleted file mode 100644 index b39a9ef9a92c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/time/time_suffix.tsv +++ /dev/null @@ -1,8 +0,0 @@ -p m p.m. -pm p.m. -p.m. -p.m p.m. -am a.m. -a.m. -a.m a.m. -a m a.m. \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/time/time_zone.tsv b/nemo_text_processing/inverse_text_normalization/vi/data/time/time_zone.tsv deleted file mode 100644 index 3e0ade467a84..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/data/time/time_zone.tsv +++ /dev/null @@ -1,7 +0,0 @@ -cst c s t -cet c e t -pst p s t -est e s t -pt p t -et e t -gmt g m t diff --git a/nemo_text_processing/inverse_text_normalization/vi/data/whitelist.tsv b/nemo_text_processing/inverse_text_normalization/vi/data/whitelist.tsv deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/nemo_text_processing/inverse_text_normalization/vi/graph_utils.py b/nemo_text_processing/inverse_text_normalization/vi/graph_utils.py deleted file mode 100644 index adf1077fc08a..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/graph_utils.py +++ /dev/null @@ -1,152 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import string -from pathlib import Path -from typing import Dict - -import pynini -from pynini import Far -from pynini.export import export -from pynini.lib import byte, pynutil, utf8 - -NEMO_CHAR = utf8.VALID_UTF8_CHAR - -NEMO_DIGIT = byte.DIGIT -NEMO_LOWER = pynini.union(*string.ascii_lowercase).optimize() -NEMO_UPPER = pynini.union(*string.ascii_uppercase).optimize() -NEMO_ALPHA = pynini.union(NEMO_LOWER, NEMO_UPPER).optimize() -NEMO_ALNUM = pynini.union(NEMO_DIGIT, NEMO_ALPHA).optimize() -NEMO_HEX = pynini.union(*string.hexdigits).optimize() -NEMO_NON_BREAKING_SPACE = "\u00A0" -NEMO_SPACE = " " -NEMO_WHITE_SPACE = pynini.union(" ", "\t", "\n", "\r", "\u00A0").optimize() -NEMO_NOT_SPACE = pynini.difference(NEMO_CHAR, NEMO_WHITE_SPACE).optimize() -NEMO_NOT_QUOTE = pynini.difference(NEMO_CHAR, r'"').optimize() - -NEMO_PUNCT = pynini.union(*map(pynini.escape, string.punctuation)).optimize() -NEMO_GRAPH = pynini.union(NEMO_ALNUM, NEMO_PUNCT).optimize() - -NEMO_SIGMA = pynini.closure(NEMO_CHAR) - -delete_space = pynutil.delete(pynini.closure(NEMO_WHITE_SPACE)) -insert_space = pynutil.insert(" ") -delete_extra_space = pynini.cross(pynini.closure(NEMO_WHITE_SPACE, 1), " ") - -# French frequently compounds numbers with hyphen. -delete_hyphen = pynutil.delete(pynini.closure("-", 0, 1)) -insert_hyphen = pynutil.insert("-") - -TO_LOWER = pynini.union(*[pynini.cross(x, y) for x, y in zip(string.ascii_uppercase, string.ascii_lowercase)]) -TO_UPPER = pynini.invert(TO_LOWER) - - -def generator_main(file_name: str, graphs: Dict[str, pynini.FstLike]): - """ - Exports graph as OpenFst finite state archive (FAR) file with given file name and rule name. - - Args: - file_name: exported file name - graphs: Mapping of a rule name and Pynini WFST graph to be exported - """ - exporter = export.Exporter(file_name) - for rule, graph in graphs.items(): - exporter[rule] = graph.optimize() - exporter.close() - print(f"Created {file_name}") - - -def convert_space(fst) -> "pynini.FstLike": - """ - Converts space to nonbreaking space. - Used only in tagger grammars for transducing token values within quotes, e.g. name: "hello kitty" - This is making transducer significantly slower, so only use when there could be potential spaces within quotes, otherwise leave it. - - Args: - fst: input fst - - Returns output fst where breaking spaces are converted to non breaking spaces - """ - return fst @ pynini.cdrewrite(pynini.cross(NEMO_SPACE, NEMO_NON_BREAKING_SPACE), "", "", NEMO_SIGMA) - - -class GraphFst: - """ - Base class for all grammar fsts. - - Args: - name: name of grammar class - kind: either 'classify' or 'verbalize' - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, name: str, kind: str, deterministic: bool = True): - self.name = name - self.kind = kind - self._fst = None - self.deterministic = deterministic - - self.far_path = Path(os.path.dirname(__file__) + "/grammars/" + kind + "/" + name + ".far") - if self.far_exist(): - self._fst = Far(self.far_path, mode="r", arc_type="standard", far_type="default").get_fst() - - def far_exist(self) -> bool: - """ - Returns true if FAR can be loaded - """ - return self.far_path.exists() - - @property - def fst(self) -> "pynini.FstLike": - return self._fst - - @fst.setter - def fst(self, fst): - self._fst = fst - - def add_tokens(self, fst) -> "pynini.FstLike": - """ - Wraps class name around to given fst - - Args: - fst: input fst - - Returns: - Fst: fst - """ - return pynutil.insert(f"{self.name} {{ ") + fst + pynutil.insert(" }") - - def delete_tokens(self, fst) -> "pynini.FstLike": - """ - Deletes class name wrap around output of given fst - - Args: - fst: input fst - - Returns: - Fst: fst - """ - res = ( - pynutil.delete(f"{self.name}") - + delete_space - + pynutil.delete("{") - + delete_space - + fst - + delete_space - + pynutil.delete("}") - ) - return res @ pynini.cdrewrite(pynini.cross("\u00A0", " "), "", "", NEMO_SIGMA) diff --git a/nemo_text_processing/inverse_text_normalization/vi/taggers/__init__.py b/nemo_text_processing/inverse_text_normalization/vi/taggers/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/taggers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/vi/taggers/cardinal.py b/nemo_text_processing/inverse_text_normalization/vi/taggers/cardinal.py deleted file mode 100644 index d25c96071477..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/taggers/cardinal.py +++ /dev/null @@ -1,153 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import ( - NEMO_DIGIT, - NEMO_SPACE, - GraphFst, - delete_space, -) -from nemo_text_processing.inverse_text_normalization.vi.utils import get_abs_path -from pynini.lib import pynutil - - -class CardinalFst(GraphFst): - """ - Finite state transducer for classifying cardinals - e.g. trừ hai mươi ba -> cardinal { integer: "23" negative: "-" } } - e.g. hai nghìn lẻ chín -> cardinal { integer: "2009"} } - Numbers below ten are not converted. - """ - - def __init__(self): - super().__init__(name="cardinal", kind="classify") - graph_zero = pynini.string_file(get_abs_path("data/numbers/zero.tsv")) - graph_digit = pynini.string_file(get_abs_path("data/numbers/digit.tsv")) - graph_ties = pynini.string_file(get_abs_path("data/numbers/ties.tsv")) - graph_teen = pynini.string_file(get_abs_path("data/numbers/teen.tsv")) - - graph_one = pynini.cross("mốt", "1") - graph_four = pynini.cross("tư", "4") - graph_five = pynini.cross("lăm", "5") - graph_half = pynini.cross("rưỡi", "5") - graph_hundred = pynini.cross("trăm", "") - graph_ten = pynini.cross("mươi", "") - zero = pynini.cross(pynini.union("linh", "lẻ"), "0") - - optional_ten = pynini.closure(delete_space + graph_ten, 0, 1) - last_digit_exception = pynini.project(pynini.cross("năm", "5"), "input") - last_digit = pynini.union( - (pynini.project(graph_digit, "input") - last_digit_exception.arcsort()) @ graph_digit, - graph_one, - graph_four, - graph_five, - ) - - graph_hundred_ties_component = (graph_digit | graph_zero) + delete_space + graph_hundred - graph_hundred_ties_component += delete_space - graph_hundred_ties_component += pynini.union( - graph_teen, - (graph_half | graph_four | graph_one) + pynutil.insert("0"), - graph_ties + optional_ten + ((delete_space + last_digit) | pynutil.insert("0")), - zero + delete_space + (graph_digit | graph_four), - pynutil.insert("00"), - ) - graph_hundred_ties_component |= ( - pynutil.insert("0") - + delete_space - + pynini.union( - graph_teen, - graph_ties + optional_ten + delete_space + last_digit, - graph_ties + delete_space + graph_ten + pynutil.insert("0"), - zero + delete_space + (graph_digit | graph_four), - ) - ) - graph_hundred_component = graph_hundred_ties_component | (pynutil.insert("00") + delete_space + graph_digit) - - graph_hundred_component_at_least_one_none_zero_digit = graph_hundred_component @ ( - pynini.closure(NEMO_DIGIT) + (NEMO_DIGIT - "0") + pynini.closure(NEMO_DIGIT) - ) - self.graph_hundred_component_at_least_one_none_zero_digit = ( - graph_hundred_component_at_least_one_none_zero_digit - ) - graph_hundred_ties_zero = graph_hundred_ties_component | pynutil.insert("000") - - graph_thousands = pynini.union( - graph_hundred_component_at_least_one_none_zero_digit - + delete_space - + pynutil.delete(pynini.union("nghìn", "ngàn")), - pynutil.insert("000", weight=0.1), - ) - - graph_ten_thousand = pynini.union( - graph_hundred_component_at_least_one_none_zero_digit + delete_space + pynutil.delete("vạn"), - pynutil.insert("0000", weight=0.1), - ) - - graph_ten_thousand_suffix = pynini.union( - graph_digit + delete_space + pynutil.delete(pynini.union("nghìn", "ngàn")), - pynutil.insert("0", weight=0.1), - ) - - graph_million = pynini.union( - graph_hundred_component_at_least_one_none_zero_digit + delete_space + pynutil.delete("triệu"), - pynutil.insert("000", weight=0.1), - ) - graph_billion = pynini.union( - graph_hundred_component_at_least_one_none_zero_digit - + delete_space - + pynutil.delete(pynini.union("tỉ", "tỷ")), - pynutil.insert("000", weight=0.1), - ) - - graph = pynini.union( - graph_billion - + delete_space - + graph_million - + delete_space - + graph_thousands - + delete_space - + graph_hundred_ties_zero, - graph_ten_thousand + delete_space + graph_ten_thousand_suffix + delete_space + graph_hundred_ties_zero, - graph_hundred_component_at_least_one_none_zero_digit - + delete_space - + pynutil.delete(pynini.union("nghìn", "ngàn")) - + delete_space - + (((last_digit | graph_half) + pynutil.insert("00")) | graph_hundred_ties_zero), - graph_digit, - graph_zero, - ) - - graph = graph @ pynini.union( - pynutil.delete(pynini.closure("0")) + pynini.difference(NEMO_DIGIT, "0") + pynini.closure(NEMO_DIGIT), "0", - ) - - # don't convert cardinals from zero to nine inclusive - graph_exception = pynini.project(pynini.union(graph_digit, graph_zero), "input") - - self.graph_no_exception = graph - - self.graph = (pynini.project(graph, "input") - graph_exception.arcsort()) @ graph - - optional_minus_graph = pynini.closure( - pynutil.insert("negative: ") + pynini.cross(pynini.union("âm", "trừ"), '"-"') + NEMO_SPACE, 0, 1, - ) - - final_graph = optional_minus_graph + pynutil.insert('integer: "') + self.graph + pynutil.insert('"') - - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/vi/taggers/date.py b/nemo_text_processing/inverse_text_normalization/vi/taggers/date.py deleted file mode 100644 index e1986f69c144..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/taggers/date.py +++ /dev/null @@ -1,157 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import GraphFst, delete_extra_space, delete_space -from nemo_text_processing.inverse_text_normalization.vi.utils import get_abs_path -from pynini.lib import pynutil - -graph_teen = pynini.string_file(get_abs_path("data/numbers/teen.tsv")).optimize() -graph_digit = pynini.string_file(get_abs_path("data/numbers/digit.tsv")).optimize() -graph_zero = pynini.string_file(get_abs_path("data/numbers/zero.tsv")).optimize() -ties_graph = pynini.string_file(get_abs_path("data/numbers/ties.tsv")).optimize() - - -def _get_month_graph(): - """ - Transducer for month, e.g. march -> march - """ - month_graph = pynini.string_file(get_abs_path("data/months.tsv")).optimize() - return month_graph - - -def _get_ties_graph(): - """ - Transducer for 20-99 e.g - hai ba -> 23 - """ - graph_one = pynini.cross("mốt", "1") - graph_four = pynini.cross("tư", "4") - graph_five = pynini.cross("lăm", "5") - graph_ten = pynini.cross("mươi", "") - optional_ten = pynini.closure(delete_space + graph_ten, 0, 1) - - graph = pynini.union( - ties_graph + optional_ten + delete_space + (graph_digit | graph_one | graph_four | graph_five), - ties_graph + delete_space + graph_ten + pynutil.insert("0"), - ) - return graph - - -def _get_year_graph(): - """ - Transducer for year, e.g. hai không hai mươi -> 2020 - """ - - def _get_digits_graph(): - zero = pynini.cross((pynini.union("linh", "lẻ")), "0") - four = pynini.cross("tư", "4") - graph = pynini.union(zero + delete_space + (graph_digit | four), graph_zero + delete_space + graph_digit,) - graph.optimize() - return graph - - def _get_hundreds_graph(graph_ties, graph_digits): - graph = ( - graph_digit - + delete_space - + pynutil.delete("trăm") - + delete_space - + (graph_teen | graph_ties | graph_digits) - ) - return graph - - def _get_thousands_graph(graph_ties, graph_digits): - graph_hundred_component = ( - (graph_digit | graph_zero) + delete_space + pynutil.delete("trăm") - ) | pynutil.insert("0") - graph = ( - graph_digit - + delete_space - + pynutil.delete(pynini.union("nghìn", "ngàn")) - + delete_space - + graph_hundred_component - + delete_space - + (graph_teen | graph_ties | graph_digits) - ) - return graph - - graph_ties = _get_ties_graph() - graph_digits = _get_digits_graph() - graph_hundreds = _get_hundreds_graph(graph_ties, graph_digits) - graph_thousands = _get_thousands_graph(graph_ties, graph_digits) - year_graph = ( - # 20 19, 40 12, 2012, 2 0 0 5, 2 0 17, 938 - assuming no limit on the year - graph_digit - + delete_space - + (graph_digit | graph_zero) - + delete_space - + (graph_teen | graph_ties | graph_digits) - | graph_thousands - | graph_hundreds - | (graph_digit + pynutil.insert("0") + delete_space + (graph_ties | graph_digits | graph_teen)) - ) - year_graph.optimize() - return year_graph - - -class DateFst(GraphFst): - """ - Finite state transducer for classifying date, - e.g. mười lăm tháng một năm hai nghìn mười hai -> date { day: "15" month: "1" year: "2012" preserve_order: true } - e.g. ngày ba mốt tháng mười hai năm một chín chín chín -> date { day: "31" month: "12" year: "2012" preserve_order: true } - e.g. năm hai không hai mốt -> date { year: "2021" preserve_order: true } - - Args: - cardinal: CardinalFst - """ - - def __init__(self, cardinal: GraphFst): - super().__init__(name="date", kind="classify") - - cardinal_graph = cardinal.graph_no_exception - year_graph = _get_year_graph() - YEAR_WEIGHT = 0.001 - year_graph = pynutil.add_weight(year_graph, YEAR_WEIGHT) - month_graph = _get_month_graph() - - month_graph = pynutil.insert('month: "') + month_graph + pynutil.insert('"') - month_exception = pynini.project(pynini.cross("năm", "5"), "input") - month_graph_exception = (pynini.project(month_graph, "input") - month_exception.arcsort()) @ month_graph - - day_graph = pynutil.insert('day: "') + cardinal_graph + pynutil.insert('"') - # day_suffix = pynini.union("ngày", "mùng") - # optional_day = pynini.closure(day_suffix + delete_space, 0, 1) - - graph_month = pynutil.delete("tháng") + delete_space + month_graph_exception - graph_year = ( - delete_extra_space - + pynutil.delete("năm") - + delete_extra_space - + pynutil.insert('year: "') - + pynutil.add_weight(year_graph, -YEAR_WEIGHT) - + pynutil.insert('"') - ) - optional_graph_year = pynini.closure(graph_year, 0, 1) - graph_my = pynutil.delete("tháng") + delete_space + month_graph + graph_year - graph_dmy = ( - day_graph + delete_space + pynutil.delete("tháng") + delete_extra_space + month_graph + optional_graph_year - ) - graph_year = ( - pynutil.delete("năm") + delete_extra_space + pynutil.insert('year: "') + year_graph + pynutil.insert('"') - ) - - final_graph = (graph_dmy | graph_my | graph_month | graph_year) + pynutil.insert(" preserve_order: true") - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/vi/taggers/decimal.py b/nemo_text_processing/inverse_text_normalization/vi/taggers/decimal.py deleted file mode 100644 index 298319306e0f..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/taggers/decimal.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import ( - NEMO_DIGIT, - GraphFst, - delete_extra_space, - delete_space, -) -from nemo_text_processing.inverse_text_normalization.vi.utils import get_abs_path -from pynini.lib import pynutil - -graph_digit = pynini.string_file(get_abs_path("data/numbers/digit.tsv")) - - -def get_quantity(decimal: "pynini.FstLike", cardinal_up_to_hundred: "pynini.FstLike") -> "pynini.FstLike": - """ - Returns FST that transforms either a cardinal or decimal followed by a quantity into a numeral, - e.g. một triệu -> integer_part: "1" quantity: "triệu" - e.g. một tỷ rưỡi -> integer_part: "1" fractional_part: "5" quantity: "tỷ" - - Args: - decimal: decimal FST - cardinal_up_to_hundred: cardinal FST - """ - numbers = cardinal_up_to_hundred @ ( - pynutil.delete(pynini.closure("0")) + pynini.difference(NEMO_DIGIT, "0") + pynini.closure(NEMO_DIGIT) - ) - suffix = pynini.union("triệu", "tỉ", "tỷ", "vạn") - graph_four = pynini.cross("tư", "4") - graph_one = pynini.cross("mốt", "1") - graph_half = pynini.cross("rưỡi", "5") - last_digit_exception = pynini.project(pynini.cross("năm", "5"), "input") - last_digit = pynini.union( - (pynini.project(graph_digit, "input") - last_digit_exception.arcsort()) @ graph_digit, - graph_one, - graph_four, - graph_half, - ) - optional_fraction_graph = pynini.closure( - delete_extra_space - + pynutil.insert('fractional_part: "') - + (last_digit | graph_half | graph_one | graph_four) - + pynutil.insert('"'), - 0, - 1, - ) - - res = ( - pynutil.insert('integer_part: "') - + numbers - + pynutil.insert('"') - + delete_extra_space - + pynutil.insert('quantity: "') - + suffix - + pynutil.insert('"') - + optional_fraction_graph - ) - res |= ( - decimal - + delete_extra_space - + pynutil.insert('quantity: "') - + (suffix | "ngàn" | "nghìn") - + pynutil.insert('"') - ) - return res - - -class DecimalFst(GraphFst): - """ - Finite state transducer for classifying decimal - e.g. âm hai hai phẩy không năm tư năm tỉ -> decimal { negative: "true" integer_part: "22" fractional_part: "054" quantity: "tỉ" } - e.g. không chấm ba lăm -> decimal { integer_part: "0" fractional_part: "35" } - e.g. một triệu rưỡi -> decimal { integer_part: "1" quantity: "triệu" fractional_part: "5" } - Args: - cardinal: CardinalFst - """ - - def __init__(self, cardinal: GraphFst): - super().__init__(name="decimal", kind="classify") - - cardinal_graph = cardinal.graph_no_exception - - graph_decimal = graph_digit | pynini.string_file(get_abs_path("data/numbers/zero.tsv")) - graph_one = pynini.cross("mốt", "1") - graph_four = pynini.cross("tư", "4") - graph_five = pynini.cross("lăm", "5") - - graph_decimal = pynini.union( - graph_decimal, - graph_four, - pynini.closure(graph_decimal + delete_space, 1) + (graph_decimal | graph_four | graph_five | graph_one), - ) - self.graph = graph_decimal - - point = pynutil.delete("chấm") | pynutil.delete("phẩy") - - optional_graph_negative = pynini.closure( - pynutil.insert("negative: ") + pynini.cross(pynini.union("âm", "trừ"), '"true"') + delete_extra_space, - 0, - 1, - ) - - graph_fractional = pynutil.insert('fractional_part: "') + graph_decimal + pynutil.insert('"') - graph_integer = pynutil.insert('integer_part: "') + cardinal_graph + pynutil.insert('"') - final_graph_wo_sign = ( - pynini.closure(graph_integer + delete_extra_space, 0, 1) + point + delete_extra_space + graph_fractional - ) - final_graph = optional_graph_negative + final_graph_wo_sign - - self.final_graph_wo_negative = final_graph_wo_sign | get_quantity( - final_graph_wo_sign, cardinal.graph_hundred_component_at_least_one_none_zero_digit, - ) - final_graph |= optional_graph_negative + get_quantity( - final_graph_wo_sign, cardinal.graph_hundred_component_at_least_one_none_zero_digit, - ) - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/vi/taggers/electronic.py b/nemo_text_processing/inverse_text_normalization/vi/taggers/electronic.py deleted file mode 100644 index 3eb0c8886678..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/taggers/electronic.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import NEMO_ALPHA, GraphFst, insert_space -from nemo_text_processing.inverse_text_normalization.vi.utils import get_abs_path -from pynini.lib import pynutil - - -class ElectronicFst(GraphFst): - """ - Finite state transducer for classifying electronic: as URLs, email addresses, etc. - e.g. c d f một a còng a b c dot e d u -> tokens { electronic { username: "cdf1" domain: "abc.edu" } } - """ - - def __init__(self): - super().__init__(name="electronic", kind="classify") - - delete_extra_space = pynutil.delete(" ") - alpha_num = ( - NEMO_ALPHA - | pynini.string_file(get_abs_path("data/numbers/digit.tsv")) - | pynini.string_file(get_abs_path("data/numbers/zero.tsv")) - ) - - symbols = pynini.string_file(get_abs_path("data/electronic/symbols.tsv")).invert() - - accepted_username = alpha_num | symbols - process_dot = pynini.cross("chấm", ".") - username = ( - pynutil.insert('username: "') - + alpha_num - + pynini.closure(delete_extra_space + accepted_username) - + pynutil.insert('"') - ) - single_alphanum = pynini.closure(alpha_num + delete_extra_space) + alpha_num - server = single_alphanum | pynini.string_file(get_abs_path("data/electronic/server_name.tsv")) - domain = single_alphanum | pynini.string_file(get_abs_path("data/electronic/domain.tsv")) - multi_domain = ( - pynini.closure(process_dot + delete_extra_space + domain + delete_extra_space) - + process_dot - + delete_extra_space - + domain - ) - domain_graph = pynutil.insert('domain: "') + server + delete_extra_space + multi_domain + pynutil.insert('"') - graph = ( - username - + delete_extra_space - + pynutil.delete(pynini.union("a còng", "a móc", "a vòng")) - + insert_space - + delete_extra_space - + domain_graph - ) - - ############# url ### - protocol_end = pynini.cross(pynini.union("w w w", "www"), "www") - protocol_start = (pynini.cross("h t t p", "http") | pynini.cross("h t t p s", "https")) + pynini.cross( - " hai chấm sẹc sẹc ", "://" - ) - # .com, - ending = ( - delete_extra_space - + symbols - + delete_extra_space - + (domain | pynini.closure(accepted_username + delete_extra_space) + accepted_username) - ) - - protocol = ( - pynini.closure(protocol_start, 0, 1) - + protocol_end - + delete_extra_space - + process_dot - + pynini.closure(delete_extra_space + accepted_username, 1) - + pynini.closure(ending, 1, 2) - ) - protocol = pynutil.insert('protocol: "') + protocol + pynutil.insert('"') - graph |= protocol - ######## - - final_graph = self.add_tokens(graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/vi/taggers/fraction.py b/nemo_text_processing/inverse_text_normalization/vi/taggers/fraction.py deleted file mode 100644 index 7798be65409a..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/taggers/fraction.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import GraphFst, delete_extra_space, delete_space -from pynini.lib import pynutil - - -class FractionFst(GraphFst): - """ - Finite state transducer for classifying fraction - e.g. 2 phần 3 -> tokens { fraction { numerator: "2" denominator: "3" } } - e.g. 2 trên 3 -> tokens { fraction { numerator: "2" denominator: "3" } } - e.g. 2 chia 3 -> tokens { fraction { numerator: "2" denominator: "3" } } - - Args: - cardinal: OrdinalFst - """ - - def __init__(self, cardinal: GraphFst): - super().__init__(name="fraction", kind="classify") - # integer_part # numerator # denominator - - graph_cardinal = cardinal.graph_no_exception - graph_four = pynini.cross("tư", "4") - - numerator = pynutil.insert('numerator: "') + graph_cardinal + pynutil.insert('"') - fraction_component = pynutil.delete(pynini.union("phần", "trên", "chia")) - denominator = pynutil.insert('denominator: "') + (graph_cardinal | graph_four) + pynutil.insert('"') - - graph_fraction_component = numerator + delete_space + fraction_component + delete_extra_space + denominator - self.graph_fraction_component = graph_fraction_component - - graph = graph_fraction_component - graph = graph.optimize() - self.final_graph_wo_negative = graph - - optional_graph_negative = pynini.closure( - pynutil.insert("negative: ") + pynini.cross(pynini.union("âm", "trừ"), '"true"') + delete_extra_space, - 0, - 1, - ) - graph = optional_graph_negative + graph - final_graph = self.add_tokens(graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/vi/taggers/measure.py b/nemo_text_processing/inverse_text_normalization/vi/taggers/measure.py deleted file mode 100644 index 0b1f08b87ec7..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/taggers/measure.py +++ /dev/null @@ -1,103 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import ( - GraphFst, - convert_space, - delete_extra_space, - delete_space, -) -from nemo_text_processing.inverse_text_normalization.vi.utils import get_abs_path -from pynini.lib import pynutil - - -class MeasureFst(GraphFst): - """ - Finite state transducer for classifying measure - e.g. trừ mười hai ki lô gam -> measure { negative: "true" cardinal { integer: "12" } units: "kg" } - - Args: - cardinal: CardinalFst - decimal: DecimalFst - """ - - def __init__(self, cardinal: GraphFst, decimal: GraphFst): - super().__init__(name="measure", kind="classify") - - cardinal_graph = cardinal.graph_no_exception - - graph_digit = pynini.string_file(get_abs_path("data/numbers/digit.tsv")) - graph_four = pynini.cross("tư", "4") - graph_one = pynini.cross("mốt", "1") - graph_half = pynini.cross("rưỡi", "5") - - graph_unit = pynini.string_file(get_abs_path("data/measurements.tsv")) - graph_unit_singular = pynini.invert(graph_unit) # singular -> abbr - - optional_graph_negative = pynini.closure( - pynutil.insert("negative: ") + pynini.cross(pynini.union("âm", "trừ"), '"true"') + delete_extra_space, - 0, - 1, - ) - - unit_singular = convert_space(graph_unit_singular) - unit_misc = pynutil.insert("/") + pynutil.delete("trên") + delete_space + convert_space(graph_unit_singular) - - unit_singular = ( - pynutil.insert('units: "') - + (unit_singular | unit_misc | pynutil.add_weight(unit_singular + delete_space + unit_misc, 0.01)) - + pynutil.insert('"') - ) - - subgraph_decimal = ( - pynutil.insert("decimal { ") - + optional_graph_negative - + decimal.final_graph_wo_negative - + pynutil.insert(" }") - + delete_extra_space - + unit_singular - ) - - subgraph_cardinal = ( - pynutil.insert("cardinal { ") - + optional_graph_negative - + pynutil.insert('integer: "') - + cardinal_graph - + pynutil.insert('"') - + pynutil.insert(" }") - + delete_extra_space - + unit_singular - ) - fraction_graph = ( - delete_extra_space - + pynutil.insert('fractional_part: "') - + (graph_digit | graph_half | graph_one | graph_four) - + pynutil.insert('"') - ) - - subgraph_cardinal |= ( - pynutil.insert("cardinal { ") - + optional_graph_negative - + pynutil.insert('integer: "') - + cardinal_graph - + pynutil.insert('" }') - + delete_extra_space - + unit_singular - + fraction_graph - ) - final_graph = subgraph_decimal | subgraph_cardinal - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/vi/taggers/money.py b/nemo_text_processing/inverse_text_normalization/vi/taggers/money.py deleted file mode 100644 index b278773daf9e..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/taggers/money.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import ( - NEMO_DIGIT, - GraphFst, - convert_space, - delete_extra_space, -) -from nemo_text_processing.inverse_text_normalization.vi.utils import get_abs_path -from pynini.lib import pynutil - - -class MoneyFst(GraphFst): - """ - Finite state transducer for classifying money - e.g. mười hai đô la mỹ -> money { integer_part: "12" currency: "$" } - e.g. mười phẩy chín đồng -> money { integer_part: "10.9" currency: "đ" } - - Args: - cardinal: CardinalFst - decimal: DecimalFst - """ - - def __init__(self, cardinal: GraphFst, decimal: GraphFst): - super().__init__(name="money", kind="classify") - # quantity, integer_part, fractional_part, currency - - cardinal_graph = cardinal.graph_no_exception - graph_decimal_final = decimal.final_graph_wo_negative - graph_half = pynini.cross("rưỡi", "5") - - unit = pynini.string_file(get_abs_path("data/currency.tsv")) - unit_singular = pynini.invert(unit) - - graph_unit_singular = pynutil.insert('currency: "') + convert_space(unit_singular) + pynutil.insert('"') - - add_leading_zero_to_double_digit = (NEMO_DIGIT + NEMO_DIGIT) | (pynutil.insert("0") + NEMO_DIGIT) - - # twelve dollars fifty, only after integer - optional_cents_suffix = pynini.closure( - delete_extra_space - + pynutil.insert('fractional_part: "') - + (pynutil.add_weight(cardinal_graph @ add_leading_zero_to_double_digit, -0.7) | graph_half) - + pynutil.insert('"'), - 0, - 1, - ) - - graph_integer = ( - pynutil.insert('integer_part: "') - + cardinal_graph - + pynutil.insert('"') - + delete_extra_space - + graph_unit_singular - + optional_cents_suffix - ) - - graph_decimal = graph_decimal_final + delete_extra_space + graph_unit_singular + optional_cents_suffix - final_graph = graph_integer | graph_decimal - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/vi/taggers/ordinal.py b/nemo_text_processing/inverse_text_normalization/vi/taggers/ordinal.py deleted file mode 100644 index a7f79a1a8eee..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/taggers/ordinal.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import GraphFst, delete_space -from nemo_text_processing.inverse_text_normalization.vi.utils import get_abs_path -from pynini.lib import pynutil - - -class OrdinalFst(GraphFst): - """ - Finite state transducer for classifying ordinal - e.g. thứ nhất -> ordinal { integer: "1" } - """ - - def __init__(self): - super().__init__(name="ordinal", kind="classify") - - graph_digit = pynini.string_file(get_abs_path("data/ordinals/digit.tsv")) - graph_ordinal = pynini.cross("thứ", "") - graph = graph_digit - - self.graph = graph - final_graph = pynutil.insert('integer: "') + graph_ordinal + delete_space + self.graph + pynutil.insert('"') - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/vi/taggers/punctuation.py b/nemo_text_processing/inverse_text_normalization/vi/taggers/punctuation.py deleted file mode 100644 index cbfe13063546..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/taggers/punctuation.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import GraphFst -from pynini.lib import pynutil - - -class PunctuationFst(GraphFst): - """ - Finite state transducer for classifying punctuation - e.g. a, -> tokens { name: "a" } tokens { name: "," } - """ - - def __init__(self): - super().__init__(name="punctuation", kind="classify") - - s = "!#$%&'()*+,-./:;<=>?@^_`{|}~" - punct = pynini.union(*s) - - graph = pynutil.insert('name: "') + punct + pynutil.insert('"') - - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/vi/taggers/telephone.py b/nemo_text_processing/inverse_text_normalization/vi/taggers/telephone.py deleted file mode 100644 index 202547a403f9..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/taggers/telephone.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import GraphFst, delete_space -from nemo_text_processing.inverse_text_normalization.vi.utils import get_abs_path -from pynini.lib import pynutil - - -class TelephoneFst(GraphFst): - """ - Finite state transducer for classifying telephone numbers, e.g. - một hai ba một hai ba năm sáu bảy tám -> { number_part: "1231235678" } - """ - - def __init__(self): - super().__init__(name="telephone", kind="classify") - graph_zero = pynini.string_file(get_abs_path("data/numbers/zero.tsv")) - graph_digit = pynini.string_file(get_abs_path("data/numbers/digit.tsv")) - digit = graph_digit | graph_zero - last_digit = digit | pynini.cross("mốt", "1") | pynini.cross("tư", "4") | pynini.cross("lăm", "5") - - graph_number_part = pynini.closure(digit + delete_space, 2) + last_digit - number_part = pynutil.insert('number_part: "') + graph_number_part + pynutil.insert('"') - - graph = number_part - final_graph = self.add_tokens(graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/vi/taggers/time.py b/nemo_text_processing/inverse_text_normalization/vi/taggers/time.py deleted file mode 100644 index c13d6467e451..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/taggers/time.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import ( - GraphFst, - convert_space, - delete_extra_space, - delete_space, - insert_space, -) -from nemo_text_processing.inverse_text_normalization.vi.utils import get_abs_path -from pynini.lib import pynutil - - -class TimeFst(GraphFst): - """ - Finite state transducer for classifying time - e.g. hai rưỡi -> time { hours: "2" minutes: "30" } - e.g. chín giờ kém hai mươi -> time { hours: "8" minutes: "40" } - e.g. ba phút hai giây -> time { minutes: "3" seconds: "2" } - e.g. mười giờ chín phút bốn mươi lăm giây -> time { hours: "10" minutes: "9" seconds: "45" } - """ - - def __init__(self): - super().__init__(name="time", kind="classify") - # hours, minutes, seconds, suffix, zone, style, speak_period - - graph_hours_to = pynini.string_file(get_abs_path("data/time/hours_to.tsv")) - graph_minutes_to = pynini.string_file(get_abs_path("data/time/minutes_to.tsv")) - graph_hours = pynini.string_file(get_abs_path("data/time/hours.tsv")) - graph_minutes = pynini.string_file(get_abs_path("data/time/minutes.tsv")) - time_zone_graph = pynini.invert(pynini.string_file(get_abs_path("data/time/time_zone.tsv"))) - - graph_half = pynini.cross("rưỡi", "30") - oclock = pynini.cross("giờ", "") - minute = pynini.cross("phút", "") - optional_minute = pynini.closure(delete_space + minute, 0, 1) - second = pynini.cross("giây", "") - - final_graph_hour = pynutil.insert('hours: "') + graph_hours + pynutil.insert('"') + delete_space + oclock - graph_minute = graph_minutes + optional_minute - graph_second = graph_minutes + delete_space + second - final_time_zone_optional = pynini.closure( - delete_space - + insert_space - + pynutil.insert('zone: "') - + convert_space(time_zone_graph) - + pynutil.insert('"'), - 0, - 1, - ) - - graph_hm = ( - final_graph_hour - + delete_extra_space - + pynutil.insert('minutes: "') - + (graph_minute | graph_half) - + pynutil.insert('"') - ) - - graph_hms = ( - final_graph_hour - + delete_extra_space - + pynutil.insert('minutes: "') - + graph_minutes - + delete_space - + minute - + pynutil.insert('"') - + delete_extra_space - + pynutil.insert('seconds: "') - + graph_second - + pynutil.insert('"') - ) - - graph_ms = ( - pynutil.insert('minutes: "') - + graph_minutes - + delete_space - + minute - + pynutil.insert('"') - + delete_extra_space - + pynutil.insert('seconds: "') - + (graph_second | graph_half) - + pynutil.insert('"') - ) - - graph_hours_to_component = graph_hours @ graph_hours_to - graph_minutes_to_component = graph_minutes @ graph_minutes_to - - graph_time_to = ( - pynutil.insert('hours: "') - + graph_hours_to_component - + pynutil.insert('"') - + delete_space - + oclock - + delete_space - + pynutil.delete("kém") - + delete_extra_space - + pynutil.insert('minutes: "') - + graph_minutes_to_component - + pynutil.insert('"') - + optional_minute - ) - - final_graph = (final_graph_hour | graph_hm | graph_hms) + final_time_zone_optional - final_graph |= graph_ms - final_graph |= graph_time_to - - final_graph = self.add_tokens(final_graph) - - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/vi/taggers/tokenize_and_classify.py b/nemo_text_processing/inverse_text_normalization/vi/taggers/tokenize_and_classify.py deleted file mode 100644 index bd5ac1173dcb..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/taggers/tokenize_and_classify.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import ( - GraphFst, - delete_extra_space, - delete_space, - generator_main, -) -from nemo_text_processing.inverse_text_normalization.vi.taggers.cardinal import CardinalFst -from nemo_text_processing.inverse_text_normalization.vi.taggers.date import DateFst -from nemo_text_processing.inverse_text_normalization.vi.taggers.decimal import DecimalFst -from nemo_text_processing.inverse_text_normalization.vi.taggers.electronic import ElectronicFst -from nemo_text_processing.inverse_text_normalization.vi.taggers.fraction import FractionFst -from nemo_text_processing.inverse_text_normalization.vi.taggers.measure import MeasureFst -from nemo_text_processing.inverse_text_normalization.vi.taggers.money import MoneyFst -from nemo_text_processing.inverse_text_normalization.vi.taggers.ordinal import OrdinalFst -from nemo_text_processing.inverse_text_normalization.vi.taggers.punctuation import PunctuationFst -from nemo_text_processing.inverse_text_normalization.vi.taggers.telephone import TelephoneFst -from nemo_text_processing.inverse_text_normalization.vi.taggers.time import TimeFst -from nemo_text_processing.inverse_text_normalization.vi.taggers.whitelist import WhiteListFst -from nemo_text_processing.inverse_text_normalization.vi.taggers.word import WordFst -from pynini.lib import pynutil - -from nemo.utils import logging - - -class ClassifyFst(GraphFst): - """ - Final class that composes all other classification grammars. This class can process an entire sentence, that is lower cased. - For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - - Args: - cache_dir: path to a dir with .far grammar file. Set to None to avoid using cache. - overwrite_cache: set to True to overwrite .far files - """ - - def __init__(self, cache_dir: str = None, overwrite_cache: bool = False): - super().__init__(name="tokenize_and_classify", kind="classify") - - far_file = None - if cache_dir is not None and cache_dir != "None": - os.makedirs(cache_dir, exist_ok=True) - far_file = os.path.join(cache_dir, "_vi_itn.far") - if not overwrite_cache and far_file and os.path.exists(far_file): - self.fst = pynini.Far(far_file, mode="r")["tokenize_and_classify"] - logging.info(f"ClassifyFst.fst was restored from {far_file}.") - else: - logging.info(f"Creating ClassifyFst grammars.") - cardinal = CardinalFst() - cardinal_graph = cardinal.fst - - fraction = FractionFst(cardinal) - fraction_graph = fraction.fst - - ordinal = OrdinalFst() - ordinal_graph = ordinal.fst - - decimal = DecimalFst(cardinal) - decimal_graph = decimal.fst - - measure_graph = MeasureFst(cardinal=cardinal, decimal=decimal).fst - date_graph = DateFst(cardinal=cardinal).fst - word_graph = WordFst().fst - time_graph = TimeFst().fst - money_graph = MoneyFst(cardinal=cardinal, decimal=decimal).fst - whitelist_graph = WhiteListFst().fst - punct_graph = PunctuationFst().fst - electronic_graph = ElectronicFst().fst - telephone_graph = TelephoneFst().fst - - classify = ( - pynutil.add_weight(whitelist_graph, 1.01) - | pynutil.add_weight(time_graph, 1.05) - | pynutil.add_weight(date_graph, 1.09) - | pynutil.add_weight(decimal_graph, 1.08) - | pynutil.add_weight(measure_graph, 1.1) - | pynutil.add_weight(cardinal_graph, 1.1) - | pynutil.add_weight(ordinal_graph, 1.1) - | pynutil.add_weight(fraction_graph, 1.09) - | pynutil.add_weight(money_graph, 1.07) - | pynutil.add_weight(telephone_graph, 1.1) - | pynutil.add_weight(electronic_graph, 1.1) - | pynutil.add_weight(word_graph, 100) - ) - - punct = pynutil.insert("tokens { ") + pynutil.add_weight(punct_graph, weight=1.1) + pynutil.insert(" }") - token = pynutil.insert("tokens { ") + classify + pynutil.insert(" }") - token_plus_punct = ( - pynini.closure(punct + pynutil.insert(" ")) + token + pynini.closure(pynutil.insert(" ") + punct) - ) - - graph = token_plus_punct + pynini.closure(delete_extra_space + token_plus_punct) - graph = delete_space + graph + delete_space - - self.fst = graph.optimize() - - if far_file: - generator_main(far_file, {"tokenize_and_classify": self.fst}) - logging.info(f"ClassifyFst grammars are saved to {far_file}.") diff --git a/nemo_text_processing/inverse_text_normalization/vi/taggers/whitelist.py b/nemo_text_processing/inverse_text_normalization/vi/taggers/whitelist.py deleted file mode 100644 index 199ce204e6ba..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/taggers/whitelist.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import GraphFst, convert_space -from nemo_text_processing.inverse_text_normalization.vi.utils import get_abs_path -from pynini.lib import pynutil - - -class WhiteListFst(GraphFst): - """ - Finite state transducer for classifying whitelisted tokens - e.g. misses -> tokens { name: "mrs." } - This class has highest priority among all classifier grammars. Whitelisted tokens are defined and loaded from "data/whitelist.tsv". - """ - - def __init__(self): - super().__init__(name="whitelist", kind="classify") - - whitelist = pynini.string_file(get_abs_path("data/whitelist.tsv")).invert() - graph = pynutil.insert('name: "') + convert_space(whitelist) + pynutil.insert('"') - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/vi/taggers/word.py b/nemo_text_processing/inverse_text_normalization/vi/taggers/word.py deleted file mode 100644 index 3e1367d70818..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/taggers/word.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import NEMO_NOT_SPACE, GraphFst -from pynini.lib import pynutil - - -class WordFst(GraphFst): - """ - Finite state transducer for classifying plain tokens, that do not belong to any special class. This can be considered as the default class. - e.g. sleep -> tokens { name: "sleep" } - """ - - def __init__(self): - super().__init__(name="word", kind="classify") - word = pynutil.insert('name: "') + pynini.closure(NEMO_NOT_SPACE, 1) + pynutil.insert('"') - self.fst = word.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/vi/utils.py b/nemo_text_processing/inverse_text_normalization/vi/utils.py deleted file mode 100644 index 436f27413508..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/utils.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - - -def get_abs_path(rel_path): - """ - Get absolute path - - Args: - rel_path: relative path to this file - - Returns absolute path - """ - return os.path.dirname(os.path.abspath(__file__)) + "/" + rel_path diff --git a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/__init__.py b/nemo_text_processing/inverse_text_normalization/vi/verbalizers/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/cardinal.py b/nemo_text_processing/inverse_text_normalization/vi/verbalizers/cardinal.py deleted file mode 100644 index 584758a3abfe..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/cardinal.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class CardinalFst(GraphFst): - """ - Finite state transducer for verbalizing cardinal - e.g. cardinal { integer: "23" negative: "-" } -> -23 - """ - - def __init__(self): - super().__init__(name="cardinal", kind="verbalize") - optional_sign = pynini.closure( - pynutil.delete("negative:") - + delete_space - + pynutil.delete('"') - + NEMO_NOT_QUOTE - + pynutil.delete('"') - + delete_space, - 0, - 1, - ) - graph = ( - pynutil.delete("integer:") - + delete_space - + pynutil.delete('"') - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete('"') - ) - self.numbers = graph - graph = optional_sign + graph - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/date.py b/nemo_text_processing/inverse_text_normalization/vi/verbalizers/date.py deleted file mode 100644 index d746528db994..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/date.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class DateFst(GraphFst): - """ - Finite state transducer for verbalizing date, e.g. - date { month: "1" year: "2012"} -> tháng 1 năm 2012 - date { day: "5" month: "10" year: "2021" preserve_order: true } -> 5 tháng 10 năm 2021 - """ - - def __init__(self): - super().__init__(name="date", kind="verbalize") - day = ( - pynutil.delete("day:") - + delete_space - + pynutil.delete('"') - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete('"') - ) - month = ( - pynutil.delete("month:") - + delete_space - + pynutil.delete('"') - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete('"') - ) - year = ( - pynutil.delete("year:") - + delete_space - + pynutil.delete('"') - + pynini.closure(NEMO_NOT_QUOTE, 1) - + delete_space - + pynutil.delete('"') - ) - - # (day) month year - # day month - graph_dm = day + delete_space + pynutil.insert(" tháng ") + month - graph_dmy = graph_dm + delete_space + pynutil.insert(" năm ") + year - graph_m = pynutil.insert("tháng ") + month - graph_my = pynutil.insert("tháng ") + month + delete_space + pynutil.insert(" năm ") + year - graph_y = pynutil.insert("năm ") + year - - optional_preserve_order = pynini.closure( - pynutil.delete("preserve_order:") + delete_space + pynutil.delete("true") + delete_space - | pynutil.delete("field_order:") - + delete_space - + pynutil.delete('"') - + NEMO_NOT_QUOTE - + pynutil.delete('"') - + delete_space - ) - - final_graph = (graph_y | graph_m | graph_dm | graph_dmy | graph_my) + delete_space + optional_preserve_order - - delete_tokens = self.delete_tokens(final_graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/decimal.py b/nemo_text_processing/inverse_text_normalization/vi/verbalizers/decimal.py deleted file mode 100644 index c016c20e0518..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/decimal.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class DecimalFst(GraphFst): - """ - Finite state transducer for verbalizing decimal, e.g. - decimal { negative: "true" integer_part: "12" fractional_part: "5006" quantity: "tỷ" } -> -12.5006 tỷ - """ - - def __init__(self): - super().__init__(name="decimal", kind="verbalize") - optionl_sign = pynini.closure(pynini.cross('negative: "true"', "-") + delete_space, 0, 1) - integer = ( - pynutil.delete("integer_part:") - + delete_space - + pynutil.delete('"') - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete('"') - ) - optional_integer = pynini.closure(integer + delete_space, 0, 1) - fractional = ( - pynutil.insert(".") - + pynutil.delete("fractional_part:") - + delete_space - + pynutil.delete('"') - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete('"') - ) - optional_fractional = pynini.closure(fractional + delete_space, 0, 1) - quantity = ( - pynutil.delete("quantity:") - + delete_space - + pynutil.delete('"') - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete('"') - ) - optional_quantity = pynini.closure(pynutil.insert(" ") + quantity + delete_space, 0, 1) - graph = optional_integer + optional_fractional + optional_quantity - self.numbers = graph - graph = optionl_sign + graph - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/electronic.py b/nemo_text_processing/inverse_text_normalization/vi/verbalizers/electronic.py deleted file mode 100644 index 755172492f27..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/electronic.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class ElectronicFst(GraphFst): - """ - Finite state transducer for verbalizing electronic - e.g. tokens { electronic { username: "cdf1" domain: "abc.edu" } } -> cdf1@abc.edu - """ - - def __init__(self): - super().__init__(name="electronic", kind="verbalize") - user_name = ( - pynutil.delete("username:") - + delete_space - + pynutil.delete('"') - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete('"') - ) - domain = ( - pynutil.delete("domain:") - + delete_space - + pynutil.delete('"') - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete('"') - ) - - protocol = ( - pynutil.delete("protocol:") - + delete_space - + pynutil.delete('"') - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete('"') - ) - - graph = user_name + delete_space + pynutil.insert("@") + domain - graph |= protocol - - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/fraction.py b/nemo_text_processing/inverse_text_normalization/vi/verbalizers/fraction.py deleted file mode 100644 index e5e87767b0fe..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/fraction.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class FractionFst(GraphFst): - """ - Finite state transducer for verbalizing fraction, - e.g. fraction { numerator: "2" denominator: "3" } } -> 2/3 - e.g. fraction { numerator: "20" denominator: "3" negative: "true"} } -> 2/3 - """ - - def __init__(self): - super().__init__(name="fraction", kind="verbalize") - optional_sign = pynini.closure(pynini.cross('negative: "true"', "-") + delete_space, 0, 1) - numerator = pynutil.delete('numerator: "') + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete('"') - - denominator = ( - pynutil.insert("/") - + pynutil.delete('denominator: "') - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete('"') - ) - - graph = (numerator + delete_space + denominator).optimize() - self.numbers = graph - delete_tokens = self.delete_tokens(optional_sign + graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/measure.py b/nemo_text_processing/inverse_text_normalization/vi/verbalizers/measure.py deleted file mode 100644 index abc06f724839..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/measure.py +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import ( - NEMO_CHAR, - NEMO_NOT_QUOTE, - GraphFst, - delete_space, -) -from pynini.lib import pynutil - - -class MeasureFst(GraphFst): - """ - Finite state transducer for verbalizing measure, e.g. - measure { negative: "true" cardinal { integer: "12" } units: "kg" } -> -12 kg - - Args: - decimal: DecimalFst - cardinal: CardinalFst - """ - - def __init__(self, decimal: GraphFst, cardinal: GraphFst): - super().__init__(name="measure", kind="verbalize") - optional_sign = pynini.closure(pynini.cross('negative: "true"', "-"), 0, 1) - unit = ( - pynutil.delete("units:") - + delete_space - + pynutil.delete('"') - + pynini.closure(NEMO_CHAR - " ", 1) - + pynutil.delete('"') - + delete_space - ) - graph_decimal = ( - pynutil.delete("decimal {") - + delete_space - + optional_sign - + delete_space - + decimal.numbers - + delete_space - + pynutil.delete("}") - ) - graph_cardinal = ( - pynutil.delete("cardinal {") - + delete_space - + optional_sign - + delete_space - + cardinal.numbers - + delete_space - + pynutil.delete("}") - ) - fractional = ( - pynutil.insert(".") - + pynutil.delete("fractional_part:") - + delete_space - + pynutil.delete('"') - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete('"') - ) - optional_fractional = pynini.closure(fractional + delete_space, 0, 1) - graph = ( - (graph_cardinal | graph_decimal) - + delete_space - + optional_fractional - + pynutil.insert(" ") - + unit - + delete_space - ) - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/money.py b/nemo_text_processing/inverse_text_normalization/vi/verbalizers/money.py deleted file mode 100644 index dbea81ff44ed..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/money.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import NEMO_CHAR, GraphFst, delete_space -from pynini.lib import pynutil - - -class MoneyFst(GraphFst): - """ - Finite state transducer for verbalizing money, e.g. - money { integer_part: "12" fractional_part: "05" currency: "$" } -> 12.05$ - - Args: - decimal: DecimalFst - """ - - def __init__(self, decimal: GraphFst): - super().__init__(name="money", kind="verbalize") - unit = ( - pynutil.delete("currency:") - + delete_space - + pynutil.delete('"') - + pynini.closure(NEMO_CHAR - " ", 1) - + pynutil.delete('"') - ) - graph = decimal.numbers + delete_space + unit - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/ordinal.py b/nemo_text_processing/inverse_text_normalization/vi/verbalizers/ordinal.py deleted file mode 100644 index c45e1ee7b68c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/ordinal.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class OrdinalFst(GraphFst): - """ - Finite state transducer for verbalizing ordinal, e.g. - ordinal { integer: "2" } -> thứ 2 - """ - - def __init__(self): - super().__init__(name="ordinal", kind="verbalize") - graph = ( - pynutil.delete("integer:") - + delete_space - + pynutil.delete('"') - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete('"') - ) - - graph = pynutil.insert("thứ ") + graph - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/telephone.py b/nemo_text_processing/inverse_text_normalization/vi/verbalizers/telephone.py deleted file mode 100644 index d0e8de0b813a..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/telephone.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import NEMO_NOT_QUOTE, GraphFst -from pynini.lib import pynutil - - -class TelephoneFst(GraphFst): - """ - Finite state transducer for verbalizing telephone, e.g. - telephone { number_part: "1231235678" } - -> 1231235678 - """ - - def __init__(self): - super().__init__(name="telephone", kind="verbalize") - - number_part = pynutil.delete('number_part: "') + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete('"') - delete_tokens = self.delete_tokens(number_part) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/time.py b/nemo_text_processing/inverse_text_normalization/vi/verbalizers/time.py deleted file mode 100644 index 446160611f3c..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/time.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import ( - NEMO_CHAR, - NEMO_DIGIT, - GraphFst, - delete_space, - insert_space, -) -from pynini.lib import pynutil - - -class TimeFst(GraphFst): - """ - Finite state transducer for verbalizing time, e.g. - time { hours: "3" } -> 3h - time { hours: "12" minutes: "30" } -> 12:30 - time { hours: "1" minutes: "12" second: "22"} -> 1:12:22 - time { minutes: "36" second: "45"} -> 36p45s - time { hours: "2" zone: "gmt" } -> 2h gmt - """ - - def __init__(self): - super().__init__(name="time", kind="verbalize") - add_leading_zero_to_double_digit = (NEMO_DIGIT + NEMO_DIGIT) | (pynutil.insert("0") + NEMO_DIGIT) - hour = ( - pynutil.delete("hours:") - + delete_space - + pynutil.delete('"') - + pynini.closure(NEMO_DIGIT, 1) - + pynutil.delete('"') - ) - minute = ( - pynutil.delete("minutes:") - + delete_space - + pynutil.delete('"') - + pynini.closure(NEMO_DIGIT, 1) - + pynutil.delete('"') - ) - second = ( - pynutil.delete("seconds:") - + delete_space - + pynutil.delete('"') - + pynini.closure(NEMO_DIGIT, 1) - + pynutil.delete('"') - ) - zone = ( - delete_space - + insert_space - + pynutil.delete("zone:") - + delete_space - + pynutil.delete('"') - + pynini.closure(NEMO_CHAR - " ", 1) - + pynutil.delete('"') - ) - optional_zone = pynini.closure(zone, 0, 1) - optional_second = pynini.closure( - delete_space + pynutil.insert(":") + (second @ add_leading_zero_to_double_digit), 0, 1, - ) - - graph_h = hour + pynutil.insert("h") - graph_hms = ( - hour + delete_space + pynutil.insert(":") + (minute @ add_leading_zero_to_double_digit) + optional_second - ) - graph_ms = ( - minute - + delete_space - + pynutil.insert("p") - + (second @ add_leading_zero_to_double_digit) - + pynutil.insert("s") - ) - - graph = (graph_h | graph_ms | graph_hms) + optional_zone - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/verbalize.py b/nemo_text_processing/inverse_text_normalization/vi/verbalizers/verbalize.py deleted file mode 100644 index 28ea6883b0e3..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/verbalize.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import GraphFst -from nemo_text_processing.inverse_text_normalization.vi.verbalizers.cardinal import CardinalFst -from nemo_text_processing.inverse_text_normalization.vi.verbalizers.date import DateFst -from nemo_text_processing.inverse_text_normalization.vi.verbalizers.decimal import DecimalFst -from nemo_text_processing.inverse_text_normalization.vi.verbalizers.electronic import ElectronicFst -from nemo_text_processing.inverse_text_normalization.vi.verbalizers.fraction import FractionFst -from nemo_text_processing.inverse_text_normalization.vi.verbalizers.measure import MeasureFst -from nemo_text_processing.inverse_text_normalization.vi.verbalizers.money import MoneyFst -from nemo_text_processing.inverse_text_normalization.vi.verbalizers.ordinal import OrdinalFst -from nemo_text_processing.inverse_text_normalization.vi.verbalizers.telephone import TelephoneFst -from nemo_text_processing.inverse_text_normalization.vi.verbalizers.time import TimeFst -from nemo_text_processing.inverse_text_normalization.vi.verbalizers.whitelist import WhiteListFst - - -class VerbalizeFst(GraphFst): - """ - Composes other verbalizer grammars. - For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - """ - - def __init__(self): - super().__init__(name="verbalize", kind="verbalize") - cardinal = CardinalFst() - cardinal_graph = cardinal.fst - ordinal_graph = OrdinalFst().fst - decimal = DecimalFst() - decimal_graph = decimal.fst - fraction = FractionFst() - fraction_graph = fraction.fst - measure_graph = MeasureFst(decimal=decimal, cardinal=cardinal).fst - money_graph = MoneyFst(decimal=decimal).fst - time_graph = TimeFst().fst - date_graph = DateFst().fst - whitelist_graph = WhiteListFst().fst - telephone_graph = TelephoneFst().fst - electronic_graph = ElectronicFst().fst - graph = ( - time_graph - | date_graph - | money_graph - | measure_graph - | ordinal_graph - | fraction_graph - | decimal_graph - | cardinal_graph - | whitelist_graph - | telephone_graph - | electronic_graph - ) - self.fst = graph diff --git a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/verbalize_final.py b/nemo_text_processing/inverse_text_normalization/vi/verbalizers/verbalize_final.py deleted file mode 100644 index 40899e1919eb..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/verbalize_final.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import GraphFst, delete_extra_space, delete_space -from nemo_text_processing.inverse_text_normalization.vi.verbalizers.verbalize import VerbalizeFst -from nemo_text_processing.inverse_text_normalization.vi.verbalizers.word import WordFst -from pynini.lib import pynutil - - -class VerbalizeFinalFst(GraphFst): - """ - Finite state transducer that verbalizes an entire sentence, e.g. - tokens { name: "its" } tokens { time { hours: "12" minutes: "30" } } tokens { name: "now" } -> its 12:30 now - """ - - def __init__(self): - super().__init__(name="verbalize_final", kind="verbalize") - verbalize = VerbalizeFst().fst - word = WordFst().fst - types = verbalize | word - graph = ( - pynutil.delete("tokens") - + delete_space - + pynutil.delete("{") - + delete_space - + types - + delete_space - + pynutil.delete("}") - ) - graph = delete_space + pynini.closure(graph + delete_extra_space) + graph + delete_space - self.fst = graph diff --git a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/whitelist.py b/nemo_text_processing/inverse_text_normalization/vi/verbalizers/whitelist.py deleted file mode 100644 index 558c75451667..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/whitelist.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import ( - NEMO_CHAR, - NEMO_SIGMA, - GraphFst, - delete_space, -) -from pynini.lib import pynutil - - -class WhiteListFst(GraphFst): - """ - Finite state transducer for verbalizing whitelist - e.g. tokens { name: "mrs." } -> mrs. - """ - - def __init__(self): - super().__init__(name="whitelist", kind="verbalize") - graph = ( - pynutil.delete("name:") - + delete_space - + pynutil.delete('"') - + pynini.closure(NEMO_CHAR - " ", 1) - + pynutil.delete('"') - ) - graph = graph @ pynini.cdrewrite(pynini.cross(u"\u00A0", " "), "", "", NEMO_SIGMA) - self.fst = graph.optimize() diff --git a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/word.py b/nemo_text_processing/inverse_text_normalization/vi/verbalizers/word.py deleted file mode 100644 index 3d7d6f8dbba8..000000000000 --- a/nemo_text_processing/inverse_text_normalization/vi/verbalizers/word.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.inverse_text_normalization.vi.graph_utils import ( - NEMO_CHAR, - NEMO_SIGMA, - GraphFst, - delete_space, -) -from pynini.lib import pynutil - - -class WordFst(GraphFst): - """ - Finite state transducer for verbalizing plain tokens - e.g. tokens { name: "sleep" } -> sleep - """ - - def __init__(self): - super().__init__(name="word", kind="verbalize") - chars = pynini.closure(NEMO_CHAR - " ", 1) - char = pynutil.delete("name:") + delete_space + pynutil.delete('"') + chars + pynutil.delete('"') - graph = char @ pynini.cdrewrite(pynini.cross(u"\u00A0", " "), "", "", NEMO_SIGMA) - - self.fst = graph.optimize() diff --git a/nemo_text_processing/text_normalization/README.md b/nemo_text_processing/text_normalization/README.md deleted file mode 100644 index d14e4d1fa06e..000000000000 --- a/nemo_text_processing/text_normalization/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Text Normalization - -Text Normalization is part of NeMo's `nemo_text_processing` - a Python package that is installed with the `nemo_toolkit`. -It converts text from written form into its verbalized form, e.g. "123" -> "one hundred twenty three". - -See [NeMo documentation](https://docs.nvidia.com/deeplearning/nemo/user-guide/docs/en/stable/nlp/text_normalization/wfst/wfst_text_normalization.html) for details. - -Tutorial with overview of the package capabilities: [Text_(Inverse)_Normalization.ipynb](https://colab.research.google.com/github/NVIDIA/NeMo/blob/stable/tutorials/text_processing/Text_(Inverse)_Normalization.ipynb) - -Tutorial on how to customize the underlying gramamrs: [WFST_Tutorial.ipynb](https://colab.research.google.com/github/NVIDIA/NeMo/blob/stable/tutorials/text_processing/WFST_Tutorial.ipynb) diff --git a/nemo_text_processing/text_normalization/__init__.py b/nemo_text_processing/text_normalization/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/text_normalization/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/data_loader_utils.py b/nemo_text_processing/text_normalization/data_loader_utils.py deleted file mode 100644 index 4fde522f22d8..000000000000 --- a/nemo_text_processing/text_normalization/data_loader_utils.py +++ /dev/null @@ -1,351 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import json -import re -import string -import sys -from collections import defaultdict, namedtuple -from typing import Dict, List, Optional, Set, Tuple -from unicodedata import category - -from nemo.utils import logging - -EOS_TYPE = "EOS" -PUNCT_TYPE = "PUNCT" -PLAIN_TYPE = "PLAIN" -Instance = namedtuple('Instance', 'token_type un_normalized normalized') -known_types = [ - "PLAIN", - "DATE", - "CARDINAL", - "LETTERS", - "VERBATIM", - "MEASURE", - "DECIMAL", - "ORDINAL", - "DIGIT", - "MONEY", - "TELEPHONE", - "ELECTRONIC", - "FRACTION", - "TIME", - "ADDRESS", -] - - -def _load_kaggle_text_norm_file(file_path: str) -> List[Instance]: - """ - https://www.kaggle.com/richardwilliamsproat/text-normalization-for-english-russian-and-polish - Loads text file in the Kaggle Google text normalization file format: \t\t<`self` if trivial class or normalized text> - E.g. - PLAIN Brillantaisia - PLAIN is - PLAIN a - PLAIN genus - PLAIN of - PLAIN plant - PLAIN in - PLAIN family - PLAIN Acanthaceae - PUNCT . sil - - - Args: - file_path: file path to text file - - Returns: flat list of instances - """ - res = [] - with open(file_path, 'r') as fp: - for line in fp: - parts = line.strip().split("\t") - if parts[0] == "": - res.append(Instance(token_type=EOS_TYPE, un_normalized="", normalized="")) - else: - l_type, l_token, l_normalized = parts - l_token = l_token.lower() - l_normalized = l_normalized.lower() - - if l_type == PLAIN_TYPE: - res.append(Instance(token_type=l_type, un_normalized=l_token, normalized=l_token)) - elif l_type != PUNCT_TYPE: - res.append(Instance(token_type=l_type, un_normalized=l_token, normalized=l_normalized)) - return res - - -def load_files(file_paths: List[str], load_func=_load_kaggle_text_norm_file) -> List[Instance]: - """ - Load given list of text files using the `load_func` function. - - Args: - file_paths: list of file paths - load_func: loading function - - Returns: flat list of instances - """ - res = [] - for file_path in file_paths: - res.extend(load_func(file_path=file_path)) - return res - - -def clean_generic(text: str) -> str: - """ - Cleans text without affecting semiotic classes. - - Args: - text: string - - Returns: cleaned string - """ - text = text.strip() - text = text.lower() - return text - - -def evaluate(preds: List[str], labels: List[str], input: Optional[List[str]] = None, verbose: bool = True) -> float: - """ - Evaluates accuracy given predictions and labels. - - Args: - preds: predictions - labels: labels - input: optional, only needed for verbosity - verbose: if true prints [input], golden labels and predictions - - Returns accuracy - """ - acc = 0 - nums = len(preds) - for i in range(nums): - pred_norm = clean_generic(preds[i]) - label_norm = clean_generic(labels[i]) - if pred_norm == label_norm: - acc = acc + 1 - else: - if input: - print(f"inpu: {json.dumps(input[i])}") - print(f"gold: {json.dumps(label_norm)}") - print(f"pred: {json.dumps(pred_norm)}") - return acc / nums - - -def training_data_to_tokens( - data: List[Instance], category: Optional[str] = None -) -> Dict[str, Tuple[List[str], List[str]]]: - """ - Filters the instance list by category if provided and converts it into a map from token type to list of un_normalized and normalized strings - - Args: - data: list of instances - category: optional semiotic class category name - - Returns Dict: token type -> (list of un_normalized strings, list of normalized strings) - """ - result = defaultdict(lambda: ([], [])) - for instance in data: - if instance.token_type != EOS_TYPE: - if category is None or instance.token_type == category: - result[instance.token_type][0].append(instance.un_normalized) - result[instance.token_type][1].append(instance.normalized) - return result - - -def training_data_to_sentences(data: List[Instance]) -> Tuple[List[str], List[str], List[Set[str]]]: - """ - Takes instance list, creates list of sentences split by EOS_Token - Args: - data: list of instances - Returns (list of unnormalized sentences, list of normalized sentences, list of sets of categories in a sentence) - """ - # split data at EOS boundaries - sentences = [] - sentence = [] - categories = [] - sentence_categories = set() - - for instance in data: - if instance.token_type == EOS_TYPE: - sentences.append(sentence) - sentence = [] - categories.append(sentence_categories) - sentence_categories = set() - else: - sentence.append(instance) - sentence_categories.update([instance.token_type]) - un_normalized = [" ".join([instance.un_normalized for instance in sentence]) for sentence in sentences] - normalized = [" ".join([instance.normalized for instance in sentence]) for sentence in sentences] - return un_normalized, normalized, categories - - -def post_process_punctuation(text: str) -> str: - """ - Normalized quotes and spaces - - Args: - text: text - - Returns: text with normalized spaces and quotes - """ - text = ( - text.replace('( ', '(') - .replace(' )', ')') - .replace('{ ', '{') - .replace(' }', '}') - .replace('[ ', '[') - .replace(' ]', ']') - .replace(' ', ' ') - .replace('”', '"') - .replace("’", "'") - .replace("»", '"') - .replace("«", '"') - .replace("\\", "") - .replace("„", '"') - .replace("´", "'") - .replace("’", "'") - .replace('“', '"') - .replace("‘", "'") - .replace('`', "'") - .replace('- -', "--") - ) - - for punct in "!,.:;?": - text = text.replace(f' {punct}', punct) - return text.strip() - - -def pre_process(text: str) -> str: - """ - Optional text preprocessing before normalization (part of TTS TN pipeline) - - Args: - text: string that may include semiotic classes - - Returns: text with spaces around punctuation marks - """ - space_both = '[]' - for punct in space_both: - text = text.replace(punct, ' ' + punct + ' ') - - # remove extra space - text = re.sub(r' +', ' ', text) - return text - - -def load_file(file_path: str) -> List[str]: - """ - Loads given text file with separate lines into list of string. - - Args: - file_path: file path - - Returns: flat list of string - """ - res = [] - with open(file_path, 'r') as fp: - for line in fp: - res.append(line) - return res - - -def write_file(file_path: str, data: List[str]): - """ - Writes out list of string to file. - - Args: - file_path: file path - data: list of string - - """ - with open(file_path, 'w') as fp: - for line in data: - fp.write(line + '\n') - - -def post_process_punct(input: str, normalized_text: str, add_unicode_punct: bool = False): - """ - Post-processing of the normalized output to match input in terms of spaces around punctuation marks. - After NN normalization, Moses detokenization puts a space after - punctuation marks, and attaches an opening quote "'" to the word to the right. - E.g., input to the TN NN model is "12 test' example", - after normalization and detokenization -> "twelve test 'example" (the quote is considered to be an opening quote, - but it doesn't match the input and can cause issues during TTS voice generation.) - The current function will match the punctuation and spaces of the normalized text with the input sequence. - "12 test' example" -> "twelve test 'example" -> "twelve test' example" (the quote was shifted to match the input). - - Args: - input: input text (original input to the NN, before normalization or tokenization) - normalized_text: output text (output of the TN NN model) - add_unicode_punct: set to True to handle unicode punctuation marks as well as default string.punctuation (increases post processing time) - """ - # in the post-processing WFST graph "``" are repalced with '"" quotes (otherwise single quotes "`" won't be handled correctly) - # this function fixes spaces around them based on input sequence, so here we're making the same double quote replacement - # to make sure these new double quotes work with this function - if "``" in input and "``" not in normalized_text: - input = input.replace("``", '"') - input = [x for x in input] - normalized_text = [x for x in normalized_text] - punct_marks = [x for x in string.punctuation if x in input] - - if add_unicode_punct: - punct_unicode = [ - chr(i) - for i in range(sys.maxunicode) - if category(chr(i)).startswith("P") and chr(i) not in punct_default and chr(i) in input - ] - punct_marks = punct_marks.extend(punct_unicode) - - for punct in punct_marks: - try: - equal = True - if input.count(punct) != normalized_text.count(punct): - equal = False - idx_in, idx_out = 0, 0 - while punct in input[idx_in:]: - idx_out = normalized_text.index(punct, idx_out) - idx_in = input.index(punct, idx_in) - - def _is_valid(idx_out, idx_in, normalized_text, input): - """Check if previous or next word match (for cases when punctuation marks are part of - semiotic token, i.e. some punctuation can be missing in the normalized text)""" - return (idx_out > 0 and idx_in > 0 and normalized_text[idx_out - 1] == input[idx_in - 1]) or ( - idx_out < len(normalized_text) - 1 - and idx_in < len(input) - 1 - and normalized_text[idx_out + 1] == input[idx_in + 1] - ) - - if not equal and not _is_valid(idx_out, idx_in, normalized_text, input): - idx_in += 1 - continue - if idx_in > 0 and idx_out > 0: - if normalized_text[idx_out - 1] == " " and input[idx_in - 1] != " ": - normalized_text[idx_out - 1] = "" - - elif normalized_text[idx_out - 1] != " " and input[idx_in - 1] == " ": - normalized_text[idx_out - 1] += " " - - if idx_in < len(input) - 1 and idx_out < len(normalized_text) - 1: - if normalized_text[idx_out + 1] == " " and input[idx_in + 1] != " ": - normalized_text[idx_out + 1] = "" - elif normalized_text[idx_out + 1] != " " and input[idx_in + 1] == " ": - normalized_text[idx_out] = normalized_text[idx_out] + " " - idx_out += 1 - idx_in += 1 - except: - logging.debug(f"Skipping post-processing of {''.join(normalized_text)} for '{punct}'") - - normalized_text = "".join(normalized_text) - return re.sub(r' +', ' ', normalized_text) diff --git a/nemo_text_processing/text_normalization/de/__init__.py b/nemo_text_processing/text_normalization/de/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/text_normalization/de/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/de/data/__init__.py b/nemo_text_processing/text_normalization/de/data/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/text_normalization/de/data/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/de/data/electronic/__init__.py b/nemo_text_processing/text_normalization/de/data/electronic/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/text_normalization/de/data/electronic/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/de/data/electronic/domain.tsv b/nemo_text_processing/text_normalization/de/data/electronic/domain.tsv deleted file mode 100644 index 5738cd66ce8f..000000000000 --- a/nemo_text_processing/text_normalization/de/data/electronic/domain.tsv +++ /dev/null @@ -1,9 +0,0 @@ -.com punkt com -.uk punkt uk -.fr punkt fr -.net dot net -.br punkt br -.in punkt in -.ru punkt ru -.de punkt de -.it punkt it \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/de/data/electronic/server_name.tsv b/nemo_text_processing/text_normalization/de/data/electronic/server_name.tsv deleted file mode 100644 index c05c20585050..000000000000 --- a/nemo_text_processing/text_normalization/de/data/electronic/server_name.tsv +++ /dev/null @@ -1,12 +0,0 @@ -gmail g mail -nvidia -outlook -hotmail -yahoo -live -yandex -orange -wanadoo -web -comcast -aol \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/de/data/electronic/symbols.tsv b/nemo_text_processing/text_normalization/de/data/electronic/symbols.tsv deleted file mode 100644 index 57a0317f8682..000000000000 --- a/nemo_text_processing/text_normalization/de/data/electronic/symbols.tsv +++ /dev/null @@ -1,20 +0,0 @@ -. punkt -: doppelpunkt -- bindestrich -_ unterstrich -! ausrufezeichen -# raute -$ dollar zeichen -% prozent zeichen -& und -' apostroph -* asterisk -+ plus -/ slash -= gleichheitszeichen -? fragezeichen -^ zirkumflex -{ linke klammer auf -} rechte klammer zu -~ tilde -, komma \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/de/data/fractions.tsv b/nemo_text_processing/text_normalization/de/data/fractions.tsv deleted file mode 100644 index 50d8847a1fc3..000000000000 --- a/nemo_text_processing/text_normalization/de/data/fractions.tsv +++ /dev/null @@ -1,30 +0,0 @@ -halb zwei -drittel drei -viertel vier -fünftel fünf -sechstel sechs -siebtel sieben -achtel acht -neuntel neun -zehntel zehn -elftel elf -zwölftel zwölf -dreizehntel dreizehn -vierzehntel vierzehn -fünfzehntel fünfzehn -sechzehntel sechzehn -siebzehntel siebzehn -achtzehntel achtzehn -neunzehntel neunzehn -zwanzigstel zwanzig -dreißigstel dreißig -vierzigstel vierzig -fünfzigstel fünfzig -sechzigstel sechzig -siebzigstel siebzig -achtzigstel achtzig -neunzigstel neunzig -hundertstel hundert -tausendstel tausend -millionstel million -milliardstel milliarde \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/de/data/measure/__init__.py b/nemo_text_processing/text_normalization/de/data/measure/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/text_normalization/de/data/measure/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/de/data/measure/measurements.tsv b/nemo_text_processing/text_normalization/de/data/measure/measurements.tsv deleted file mode 100644 index 8292eca7fa2c..000000000000 --- a/nemo_text_processing/text_normalization/de/data/measure/measurements.tsv +++ /dev/null @@ -1,82 +0,0 @@ -% prozent -f fahrenheit -c celsius -°C grad celsius -°F grad fahrenheit -K kelvin -km kilometer -m meter -cm zentimeter -mm millimeter -μm mikrometer -nm nanometer -dm dezimeter -pm pikometer -hm hektometer -ha hektar -mi meile -m² quadrat meter -0.0001 -km² quadrat kilometer -0.0001 -mm² quadrat millimeter -0.0001 -cm² quadrat zentimeter -0.0001 -m³ kubik meter -0.0001 -km³ kubik kilometer -0.0001 -mm³ kubik millimeter -0.0001 -cm³ kubik zentimeter -0.0001 -m2 quadrat meter -km2 quadrat kilometer -mm2 quadrat millimeter -cm2 quadrat zentimeter -m3 kubik meter -km3 kubik kilometer -mm3 kubik millimeter -cm3 kubik zentimeter -ft fuß -g gramm -µg mikrogramm -mg milligramm -kg kilogramm -lb pfund -oz unze -cwt zentner -gr korn -dr drachne -μg mikrogramm -pg petagramm -h stunde -s sekunde -min minute -ds decisekunde -ms millisekunde -μs mikrosekunde -hz hertz -kw kilowatt -kwh kilowattstunde -ghz gigahertz -khz kilohertz -mhz megahertz -v volt -mc megacoulomb -mA milliampere -A ampere -tw terawatt -mv millivolt -mw megawatt -gw gigawatt -ω ohm -db dezibel -gb gigabyte -kb kilobit -pb petabit -mb megabyte -kb kilobyte -tb terabyte -kv kilovolt -mv megavolt -kn kilonewton -ml milliliter -l liter -ma megaampere -bar bar -kcal kilokalorie -cal kalorie \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/de/data/measure/suppletive.tsv b/nemo_text_processing/text_normalization/de/data/measure/suppletive.tsv deleted file mode 100644 index 449d545f67b5..000000000000 --- a/nemo_text_processing/text_normalization/de/data/measure/suppletive.tsv +++ /dev/null @@ -1,10 +0,0 @@ -meile meilen -unze unzen -drachne drachnen -stunde stunden -sekunde sekunden -minute minuten -minute minuten -bit bits -byte bytes -kalorie kalorien \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/de/data/money/__init__.py b/nemo_text_processing/text_normalization/de/data/money/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/text_normalization/de/data/money/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/de/data/money/currency.tsv b/nemo_text_processing/text_normalization/de/data/money/currency.tsv deleted file mode 100644 index fa6c618ce5a8..000000000000 --- a/nemo_text_processing/text_normalization/de/data/money/currency.tsv +++ /dev/null @@ -1,25 +0,0 @@ -€ euro -$ dollar -$ us dollar -£ pfund -₩ won -nzd neuseeland dollar -rs rupie -chf schweizer franken -dkk dänische krone -fim finnische mark -aed dirham -¥ yen -czk tschechische krone -mro ouguiya -pkr pakistanische rupie -crc colon -hkd hong kong dollar -npr nepalesische rupee -awg aruba florin -nok norwegische krone -tzs tansania schilling -sek schwedisch krone -cyp zypern pfund -dm d-mark -dm deutsche mark \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/de/data/money/currency_minor_plural.tsv b/nemo_text_processing/text_normalization/de/data/money/currency_minor_plural.tsv deleted file mode 100644 index 9818ee743793..000000000000 --- a/nemo_text_processing/text_normalization/de/data/money/currency_minor_plural.tsv +++ /dev/null @@ -1,3 +0,0 @@ -$ cent -€ cent -£ pence \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/de/data/money/currency_minor_singular.tsv b/nemo_text_processing/text_normalization/de/data/money/currency_minor_singular.tsv deleted file mode 100644 index fe629d87b693..000000000000 --- a/nemo_text_processing/text_normalization/de/data/money/currency_minor_singular.tsv +++ /dev/null @@ -1,3 +0,0 @@ -$ cent -€ cent -£ penny \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/de/data/months/__init__.py b/nemo_text_processing/text_normalization/de/data/months/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/text_normalization/de/data/months/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/de/data/months/abbr_to_name.tsv b/nemo_text_processing/text_normalization/de/data/months/abbr_to_name.tsv deleted file mode 100644 index 81a323845b14..000000000000 --- a/nemo_text_processing/text_normalization/de/data/months/abbr_to_name.tsv +++ /dev/null @@ -1,13 +0,0 @@ -jan januar -feb februar -mär märz -apr april -mai mai -jun juni -jul juli -aug august -sep september -sept september -okt oktober -nov november -dez dezember \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/de/data/months/numbers.tsv b/nemo_text_processing/text_normalization/de/data/months/numbers.tsv deleted file mode 100644 index 012afcc2a34d..000000000000 --- a/nemo_text_processing/text_normalization/de/data/months/numbers.tsv +++ /dev/null @@ -1,24 +0,0 @@ -1 januar -2 februar -3 märz -4 april -5 mai -6 juni -7 juli -8 august -9 september -10 oktober -11 november -12 dezember -01 januar -02 februar -03 märz -04 april -05 mai -06 juni -07 juli -08 august -09 september -10 oktober -11 november -12 dezember \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/de/data/numbers/__init__.py b/nemo_text_processing/text_normalization/de/data/numbers/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/text_normalization/de/data/numbers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/de/data/numbers/digit.tsv b/nemo_text_processing/text_normalization/de/data/numbers/digit.tsv deleted file mode 100644 index 7634e5ac545e..000000000000 --- a/nemo_text_processing/text_normalization/de/data/numbers/digit.tsv +++ /dev/null @@ -1,8 +0,0 @@ -zwei 2 -drei 3 -vier 4 -fünf 5 -sechs 6 -sieben 7 -acht 8 -neun 9 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/de/data/numbers/ones.tsv b/nemo_text_processing/text_normalization/de/data/numbers/ones.tsv deleted file mode 100644 index 7d12cd59b1d8..000000000000 --- a/nemo_text_processing/text_normalization/de/data/numbers/ones.tsv +++ /dev/null @@ -1,3 +0,0 @@ -eine 1 -ein 1 -eins 1 -0.0001 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/de/data/numbers/quantities.tsv b/nemo_text_processing/text_normalization/de/data/numbers/quantities.tsv deleted file mode 100644 index 712fe0f38514..000000000000 --- a/nemo_text_processing/text_normalization/de/data/numbers/quantities.tsv +++ /dev/null @@ -1,12 +0,0 @@ -million -millionen -milliarde -milliarden -billion -billionen -billiarde -billiarden -trillion -trillionen -trilliarde -trilliarde \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/de/data/numbers/teen.tsv b/nemo_text_processing/text_normalization/de/data/numbers/teen.tsv deleted file mode 100644 index 854712ea6156..000000000000 --- a/nemo_text_processing/text_normalization/de/data/numbers/teen.tsv +++ /dev/null @@ -1,10 +0,0 @@ -zehn 10 -elf 11 -zwölf 12 -dreizehn 13 -vierzehn 14 -fünfzehn 15 -sechzehn 16 -siebzehn 17 -achtzehn 18 -neunzehn 19 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/de/data/numbers/ties.tsv b/nemo_text_processing/text_normalization/de/data/numbers/ties.tsv deleted file mode 100644 index 28a3f8032455..000000000000 --- a/nemo_text_processing/text_normalization/de/data/numbers/ties.tsv +++ /dev/null @@ -1,8 +0,0 @@ -zwanzig 2 -dreißig 3 -vierzig 4 -fünfzig 5 -sechzig 6 -siebzig 7 -achtzig 8 -neunzig 9 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/de/data/numbers/zero.tsv b/nemo_text_processing/text_normalization/de/data/numbers/zero.tsv deleted file mode 100644 index e27b1c6405a4..000000000000 --- a/nemo_text_processing/text_normalization/de/data/numbers/zero.tsv +++ /dev/null @@ -1 +0,0 @@ -null 0 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/de/data/ordinals/__init__.py b/nemo_text_processing/text_normalization/de/data/ordinals/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/text_normalization/de/data/ordinals/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/de/data/ordinals/digit.tsv b/nemo_text_processing/text_normalization/de/data/ordinals/digit.tsv deleted file mode 100644 index 31c91f635d80..000000000000 --- a/nemo_text_processing/text_normalization/de/data/ordinals/digit.tsv +++ /dev/null @@ -1,9 +0,0 @@ -ers eins -zwei zwei -drit drei -vier vier -fünf fünf -sechs sechs -sieb sieben -ach acht -neun neun \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/de/data/ordinals/thousands.tsv b/nemo_text_processing/text_normalization/de/data/ordinals/thousands.tsv deleted file mode 100644 index a97b94c744da..000000000000 --- a/nemo_text_processing/text_normalization/de/data/ordinals/thousands.tsv +++ /dev/null @@ -1,4 +0,0 @@ -hunderts hundert -tausends tausend -millions million -milliards milliarde \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/de/data/ordinals/ties.tsv b/nemo_text_processing/text_normalization/de/data/ordinals/ties.tsv deleted file mode 100644 index 4ab2bd508046..000000000000 --- a/nemo_text_processing/text_normalization/de/data/ordinals/ties.tsv +++ /dev/null @@ -1,8 +0,0 @@ -zwanzigs zwanzig -dreißigs dreißig -vierzigs vierzig -fünfzigs fünfzig -sechzigs sechzig -siebzigs siebzig -achtzigs achtzig -neunzigs neunzig \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/de/data/time/__init__.py b/nemo_text_processing/text_normalization/de/data/time/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/text_normalization/de/data/time/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/de/data/time/hour_to.tsv b/nemo_text_processing/text_normalization/de/data/time/hour_to.tsv deleted file mode 100644 index e681afc6c25c..000000000000 --- a/nemo_text_processing/text_normalization/de/data/time/hour_to.tsv +++ /dev/null @@ -1,12 +0,0 @@ -1 12 -2 1 -3 2 -4 3 -5 4 -6 5 -7 6 -8 7 -9 8 -10 9 -11 10 -12 11 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/de/data/time/hour_to_night.tsv b/nemo_text_processing/text_normalization/de/data/time/hour_to_night.tsv deleted file mode 100644 index b7765ac2a3c6..000000000000 --- a/nemo_text_processing/text_normalization/de/data/time/hour_to_night.tsv +++ /dev/null @@ -1,13 +0,0 @@ -12 0 -1 13 -2 14 -3 15 -4 16 -5 17 -6 18 -7 19 -8 20 -9 21 -10 22 -11 23 -12 24 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/de/data/time/minute_to.tsv b/nemo_text_processing/text_normalization/de/data/time/minute_to.tsv deleted file mode 100644 index edab4d5b03d6..000000000000 --- a/nemo_text_processing/text_normalization/de/data/time/minute_to.tsv +++ /dev/null @@ -1,59 +0,0 @@ -1 59 -2 58 -3 57 -4 56 -5 55 -6 54 -7 53 -8 52 -9 51 -10 50 -11 49 -12 48 -13 47 -14 46 -15 45 -16 44 -17 43 -18 42 -19 41 -20 40 -21 39 -22 38 -23 37 -24 36 -25 35 -26 34 -27 33 -28 32 -29 31 -30 30 -31 29 -32 28 -33 27 -34 26 -35 25 -36 24 -37 23 -38 22 -39 21 -40 20 -41 19 -42 18 -43 17 -44 16 -45 15 -46 14 -47 13 -48 12 -49 11 -50 10 -51 9 -52 8 -53 7 -54 6 -55 5 -56 4 -57 3 -58 2 -59 1 diff --git a/nemo_text_processing/text_normalization/de/data/time/time_zone.tsv b/nemo_text_processing/text_normalization/de/data/time/time_zone.tsv deleted file mode 100644 index 3e0ade467a84..000000000000 --- a/nemo_text_processing/text_normalization/de/data/time/time_zone.tsv +++ /dev/null @@ -1,7 +0,0 @@ -cst c s t -cet c e t -pst p s t -est e s t -pt p t -et e t -gmt g m t diff --git a/nemo_text_processing/text_normalization/de/data/whitelist.tsv b/nemo_text_processing/text_normalization/de/data/whitelist.tsv deleted file mode 100644 index b956ade12298..000000000000 --- a/nemo_text_processing/text_normalization/de/data/whitelist.tsv +++ /dev/null @@ -1,7 +0,0 @@ -z.B. zum beispiel -d.h. dass heißt -Dr. doktor -Mr. mister -Mrs. misses -Ms. miss -Nr. nummer diff --git a/nemo_text_processing/text_normalization/de/taggers/__init__.py b/nemo_text_processing/text_normalization/de/taggers/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/text_normalization/de/taggers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/de/taggers/cardinal.py b/nemo_text_processing/text_normalization/de/taggers/cardinal.py deleted file mode 100644 index 8f1a13b0c07c..000000000000 --- a/nemo_text_processing/text_normalization/de/taggers/cardinal.py +++ /dev/null @@ -1,199 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from collections import defaultdict - -import pynini -from nemo_text_processing.text_normalization.de.utils import get_abs_path, load_labels -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_DIGIT, - NEMO_SIGMA, - GraphFst, - delete_space, - insert_space, -) -from pynini.lib import pynutil - -AND = "und" - - -def get_ties_digit(digit_path: str, tie_path: str) -> 'pynini.FstLike': - """ - getting all inverse normalizations for numbers between 21 - 100 - - Args: - digit_path: file to digit tsv - tie_path: file to tie tsv, e.g. 20, 30, etc. - Returns: - res: fst that converts numbers to their verbalization - """ - - digits = defaultdict(list) - ties = defaultdict(list) - for k, v in load_labels(digit_path): - digits[v].append(k) - digits["1"] = ["ein"] - - for k, v in load_labels(tie_path): - ties[v].append(k) - - d = [] - for i in range(21, 100): - s = str(i) - if s[1] == "0": - continue - - for di in digits[s[1]]: - for ti in ties[s[0]]: - word = di + f" {AND} " + ti - d.append((word, s)) - - res = pynini.string_map(d) - return res - - -class CardinalFst(GraphFst): - """ - Finite state transducer for classifying cardinals, e.g. - "101" -> cardinal { integer: "ein hundert und zehn" } - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = False): - super().__init__(name="cardinal", kind="classify", deterministic=deterministic) - - graph_zero = pynini.string_file(get_abs_path("data/numbers/zero.tsv")).invert() - graph_digit_no_one = pynini.string_file(get_abs_path("data/numbers/digit.tsv")).invert() - graph_one = pynini.string_file(get_abs_path("data/numbers/ones.tsv")).invert() - graph_digit = graph_digit_no_one | graph_one - self.digit = (graph_digit | graph_zero).optimize() - graph_teen = pynini.string_file(get_abs_path("data/numbers/teen.tsv")).invert() - - graph_ties = pynini.string_file(get_abs_path("data/numbers/ties.tsv")).invert() - # separator = "." - - def tens_no_zero(): - return ( - pynutil.delete("0") + graph_digit - | get_ties_digit( - get_abs_path("data/numbers/digit.tsv"), get_abs_path("data/numbers/ties.tsv") - ).invert() - | graph_teen - | (graph_ties + pynutil.delete("0")) - ) - - def hundred_non_zero(): - return (graph_digit_no_one + insert_space | pynini.cross("1", "ein ")) + pynutil.insert("hundert") + ( - pynini.closure(insert_space + pynutil.insert(AND, weight=0.0001), 0, 1) + insert_space + tens_no_zero() - | pynutil.delete("00") - ) | pynutil.delete("0") + tens_no_zero() - - def thousand(): - return (hundred_non_zero() + insert_space + pynutil.insert("tausend") | pynutil.delete("000")) + ( - insert_space + hundred_non_zero() | pynutil.delete("000") - ) - - optional_plural_quantity_en = pynini.closure(pynutil.insert("en", weight=-0.0001), 0, 1) - optional_plural_quantity_n = pynini.closure(pynutil.insert("n", weight=-0.0001), 0, 1) - graph_million = pynini.union( - hundred_non_zero() + insert_space + pynutil.insert("million") + optional_plural_quantity_en, - pynutil.delete("000"), - ) - - graph_billion = pynini.union( - hundred_non_zero() + insert_space + pynutil.insert("milliarde") + optional_plural_quantity_n, - pynutil.delete("000"), - ) - - graph_trillion = pynini.union( - hundred_non_zero() + insert_space + pynutil.insert("billion") + optional_plural_quantity_en, - pynutil.delete("000"), - ) - - graph_quadrillion = pynini.union( - hundred_non_zero() + insert_space + pynutil.insert("billiarde") + optional_plural_quantity_n, - pynutil.delete("000"), - ) - - graph_quintillion = pynini.union( - hundred_non_zero() + insert_space + pynutil.insert("trillion") + optional_plural_quantity_en, - pynutil.delete("000"), - ) - - graph_sextillion = pynini.union( - hundred_non_zero() + insert_space + pynutil.insert("trilliarde") + optional_plural_quantity_n, - pynutil.delete("000"), - ) - graph = pynini.union( - graph_sextillion - + insert_space - + graph_quintillion - + insert_space - + graph_quadrillion - + insert_space - + graph_trillion - + insert_space - + graph_billion - + insert_space - + graph_million - + insert_space - + thousand() - ) - - fix_syntax = [ - ("eins tausend", "ein tausend"), - ("eins millionen", "eine million"), - ("eins milliarden", "eine milliarde"), - ("eins billionen", "eine billion"), - ("eins billiarden", "eine billiarde"), - ] - fix_syntax = pynini.union(*[pynini.cross(*x) for x in fix_syntax]) - self.graph = ( - ((NEMO_DIGIT - "0" + pynini.closure(NEMO_DIGIT, 0)) - "0" - "1") - @ pynini.cdrewrite(pynini.closure(pynutil.insert("0")), "[BOS]", "", NEMO_SIGMA) - @ NEMO_DIGIT ** 24 - @ graph - @ pynini.cdrewrite(delete_space, "[BOS]", "", NEMO_SIGMA) - @ pynini.cdrewrite(delete_space, "", "[EOS]", NEMO_SIGMA) - @ pynini.cdrewrite(pynini.cross(" ", " "), "", "", NEMO_SIGMA) - @ pynini.cdrewrite(fix_syntax, "[BOS]", "", NEMO_SIGMA) - ) - self.graph |= graph_zero | pynini.cross("1", "eins") - - # self.graph = pynini.cdrewrite(pynutil.delete(separator), "", "", NEMO_SIGMA) @ self.graph - self.graph = self.graph.optimize() - - self.graph_hundred_component_at_least_one_none_zero_digit = ( - ((NEMO_DIGIT - "0" + pynini.closure(NEMO_DIGIT, 0)) - "0" - "1") - @ pynini.cdrewrite(pynini.closure(pynutil.insert("0")), "[BOS]", "", NEMO_SIGMA) - @ NEMO_DIGIT ** 3 - @ hundred_non_zero() - ) | pynini.cross("1", "eins") - - self.graph_hundred_component_at_least_one_none_zero_digit = ( - self.graph_hundred_component_at_least_one_none_zero_digit.optimize() - ) - - self.two_digit_non_zero = ( - pynini.closure(NEMO_DIGIT, 1, 2) @ self.graph_hundred_component_at_least_one_none_zero_digit - ) - - optional_minus_graph = pynini.closure(pynutil.insert("negative: ") + pynini.cross("-", "\"true\" "), 0, 1) - - final_graph = optional_minus_graph + pynutil.insert("integer: \"") + self.graph + pynutil.insert("\"") - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/de/taggers/date.py b/nemo_text_processing/text_normalization/de/taggers/date.py deleted file mode 100644 index ab56ad5ce27f..000000000000 --- a/nemo_text_processing/text_normalization/de/taggers/date.py +++ /dev/null @@ -1,141 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.de.utils import get_abs_path, load_labels -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_CHAR, - NEMO_DIGIT, - TO_LOWER, - GraphFst, - insert_space, -) -from pynini.lib import pynutil - -graph_teen = pynini.invert(pynini.string_file(get_abs_path("data/numbers/teen.tsv"))).optimize() -graph_digit = pynini.invert(pynini.string_file(get_abs_path("data/numbers/digit.tsv"))).optimize() -ties_graph = pynini.invert(pynini.string_file(get_abs_path("data/numbers/ties.tsv"))).optimize() -delete_leading_zero = (pynutil.delete("0") | (NEMO_DIGIT - "0")) + NEMO_DIGIT - - -def get_year_graph(cardinal: GraphFst) -> 'pynini.FstLike': - """ - Returns year verbalizations as fst - - < 2000 neunzehn (hundert) (vier und zwanzig), >= 2000 regular cardinal - **00 ** hundert - - Args: - delete_leading_zero: removed leading zero - cardinal: cardinal GraphFst - """ - - year_gt_2000 = (pynini.union("21", "20") + NEMO_DIGIT ** 2) @ cardinal.graph - - graph_two_digit = delete_leading_zero @ cardinal.two_digit_non_zero - hundred = pynutil.insert("hundert") - graph_double_double = ( - (pynini.accep("1") + NEMO_DIGIT) @ graph_two_digit - + insert_space - + pynini.closure(hundred + insert_space, 0, 1) - + graph_two_digit - ) - # for 20** - graph_double_double |= pynini.accep("20") @ graph_two_digit + insert_space + graph_two_digit - graph = ( - graph_double_double - | (pynini.accep("1") + NEMO_DIGIT) @ graph_two_digit + insert_space + pynutil.delete("00") + hundred - | year_gt_2000 - ) - return graph - - -class DateFst(GraphFst): - """ - Finite state transducer for classifying date, e.g. - "01.04.2010" -> date { day: "erster" month: "april" year: "zwei tausend zehn" preserve_order: true } - "1994" -> date { year: "neunzehn vier und neuzig" } - "1900" -> date { year: "neunzehn hundert" } - - Args: - cardinal: cardinal GraphFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, cardinal: GraphFst, deterministic: bool): - super().__init__(name="date", kind="classify", deterministic=deterministic) - - month_abbr_graph = load_labels(get_abs_path("data/months/abbr_to_name.tsv")) - number_to_month = pynini.string_file(get_abs_path("data/months/numbers.tsv")).optimize() - month_graph = pynini.union(*[x[1] for x in month_abbr_graph]).optimize() - month_abbr_graph = pynini.string_map(month_abbr_graph) - month_abbr_graph = ( - pynutil.add_weight(month_abbr_graph, weight=0.0001) - | ((TO_LOWER + pynini.closure(NEMO_CHAR)) @ month_abbr_graph) - ) + pynini.closure(pynutil.delete(".", weight=-0.0001), 0, 1) - - self.month_abbr = month_abbr_graph - month_graph |= (TO_LOWER + pynini.closure(NEMO_CHAR)) @ month_graph - # jan.-> januar, Jan-> januar, januar-> januar - month_graph |= month_abbr_graph - - numbers = cardinal.graph_hundred_component_at_least_one_none_zero_digit - optional_leading_zero = delete_leading_zero | NEMO_DIGIT - # 01, 31, 1 - digit_day = optional_leading_zero @ pynini.union(*[str(x) for x in range(1, 32)]) @ numbers - day = (pynutil.insert("day: \"") + digit_day + pynutil.insert("\"")).optimize() - - digit_month = optional_leading_zero @ pynini.union(*[str(x) for x in range(1, 13)]) - number_to_month = digit_month @ number_to_month - digit_month @= numbers - - month_name = (pynutil.insert("month: \"") + month_graph + pynutil.insert("\"")).optimize() - month_number = ( - pynutil.insert("month: \"") - + (pynutil.add_weight(digit_month, weight=0.0001) | number_to_month) - + pynutil.insert("\"") - ).optimize() - - # prefer cardinal over year - year = pynutil.add_weight(get_year_graph(cardinal=cardinal), weight=0.001) - self.year = year - - year_only = pynutil.insert("year: \"") + year + pynutil.insert("\"") - - graph_dmy = ( - day - + pynutil.delete(".") - + pynini.closure(pynutil.delete(" "), 0, 1) - + insert_space - + month_name - + pynini.closure(pynini.accep(" ") + year_only, 0, 1) - ) - - separators = ["."] - for sep in separators: - year_optional = pynini.closure(pynini.cross(sep, " ") + year_only, 0, 1) - new_graph = day + pynini.cross(sep, " ") + month_number + year_optional - graph_dmy |= new_graph - - dash = "-" - day_optional = pynini.closure(pynini.cross(dash, " ") + day, 0, 1) - graph_ymd = year_only + pynini.cross(dash, " ") + month_number + day_optional - - final_graph = graph_dmy + pynutil.insert(" preserve_order: true") - final_graph |= year_only - final_graph |= graph_ymd - - self.final_graph = final_graph.optimize() - self.fst = self.add_tokens(self.final_graph).optimize() diff --git a/nemo_text_processing/text_normalization/de/taggers/decimal.py b/nemo_text_processing/text_normalization/de/taggers/decimal.py deleted file mode 100644 index d5cd58ed0970..000000000000 --- a/nemo_text_processing/text_normalization/de/taggers/decimal.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.de.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst, insert_space -from pynini.lib import pynutil - -quantities = pynini.string_file(get_abs_path("data/numbers/quantities.tsv")) - - -def get_quantity(decimal: 'pynini.FstLike', cardinal_up_to_hundred: 'pynini.FstLike') -> 'pynini.FstLike': - """ - Returns FST that transforms either a cardinal or decimal followed by a quantity into a numeral, - e.g. 1 million -> integer_part: "eine" quantity: "million" - e.g. 1.4 million -> integer_part: "eins" fractional_part: "vier" quantity: "million" - - Args: - decimal: decimal FST - cardinal_up_to_hundred: cardinal FST - """ - numbers = cardinal_up_to_hundred - - res = ( - pynutil.insert("integer_part: \"") - + numbers - + pynutil.insert("\"") - + pynini.accep(" ") - + pynutil.insert("quantity: \"") - + quantities - + pynutil.insert("\"") - ) - res |= decimal + pynini.accep(" ") + pynutil.insert("quantity: \"") + quantities + pynutil.insert("\"") - return res - - -class DecimalFst(GraphFst): - """ - Finite state transducer for classifying decimal, e.g. - -11,4006 billion -> decimal { negative: "true" integer_part: "elf" fractional_part: "vier null null sechs" quantity: "billion" preserve_order: true } - 1 billion -> decimal { integer_part: "eins" quantity: "billion" preserve_order: true } - Args: - cardinal: CardinalFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, cardinal: GraphFst, deterministic: bool = True): - super().__init__(name="decimal", kind="classify", deterministic=deterministic) - - graph_digit = pynini.string_file(get_abs_path("data/numbers/digit.tsv")).invert() - graph_digit |= pynini.string_file(get_abs_path("data/numbers/zero.tsv")).invert() - graph_digit |= pynini.cross("1", "eins") - self.graph = graph_digit + pynini.closure(insert_space + graph_digit).optimize() - - point = pynutil.delete(",") - optional_graph_negative = pynini.closure(pynutil.insert("negative: ") + pynini.cross("-", "\"true\" "), 0, 1) - - self.graph_fractional = pynutil.insert("fractional_part: \"") + self.graph + pynutil.insert("\"") - self.graph_integer = pynutil.insert("integer_part: \"") + cardinal.graph + pynutil.insert("\"") - final_graph_wo_sign = self.graph_integer + point + insert_space + self.graph_fractional - - self.final_graph_wo_negative = final_graph_wo_sign | get_quantity( - final_graph_wo_sign, cardinal.graph_hundred_component_at_least_one_none_zero_digit - ) - final_graph = optional_graph_negative + self.final_graph_wo_negative - final_graph += pynutil.insert(" preserve_order: true") - - final_graph = self.add_tokens(final_graph) - - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/de/taggers/electronic.py b/nemo_text_processing/text_normalization/de/taggers/electronic.py deleted file mode 100644 index 53a859700425..000000000000 --- a/nemo_text_processing/text_normalization/de/taggers/electronic.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.text_normalization.de.utils import get_abs_path, load_labels -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_ALPHA, NEMO_DIGIT, GraphFst, insert_space -from pynini.lib import pynutil - - -class ElectronicFst(GraphFst): - """ - Finite state transducer for classifying electronic: email addresses - e.g. "abc@hotmail.com" -> electronic { username: "abc" domain: "hotmail.com" preserve_order: true } - e.g. "www.abc.com/123" -> electronic { protocol: "www." domain: "abc.com/123" preserve_order: true } - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="electronic", kind="classify", deterministic=deterministic) - - dot = pynini.accep(".") - accepted_common_domains = [x[0] for x in load_labels(get_abs_path("data/electronic/domain.tsv"))] - accepted_common_domains = pynini.union(*accepted_common_domains) - accepted_symbols = [x[0] for x in load_labels(get_abs_path("data/electronic/symbols.tsv"))] - accepted_symbols = pynini.union(*accepted_symbols) - dot - accepted_characters = pynini.closure(NEMO_ALPHA | NEMO_DIGIT | accepted_symbols) - - # email - username = pynutil.insert("username: \"") + accepted_characters + pynutil.insert("\"") + pynini.cross('@', ' ') - domain_graph = accepted_characters + dot + accepted_characters - domain_graph = pynutil.insert("domain: \"") + domain_graph + pynutil.insert("\"") - domain_common_graph = ( - pynutil.insert("domain: \"") - + accepted_characters - + accepted_common_domains - + pynini.closure((accepted_symbols | dot) + pynini.closure(accepted_characters, 1), 0, 1) - + pynutil.insert("\"") - ) - graph = (username + domain_graph) | domain_common_graph - - # url - protocol_start = pynini.accep("https://") | pynini.accep("http://") - protocol_end = pynini.accep("www.") - protocol = protocol_start | protocol_end | (protocol_start + protocol_end) - protocol = pynutil.insert("protocol: \"") + protocol + pynutil.insert("\"") - graph |= protocol + insert_space + (domain_graph | domain_common_graph) - self.graph = graph - - final_graph = self.add_tokens(self.graph + pynutil.insert(" preserve_order: true")) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/de/taggers/fraction.py b/nemo_text_processing/text_normalization/de/taggers/fraction.py deleted file mode 100644 index 71d51c600b2f..000000000000 --- a/nemo_text_processing/text_normalization/de/taggers/fraction.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst -from pynini.lib import pynutil - - -class FractionFst(GraphFst): - """ - Finite state transducer for classifying fraction - "23 4/6" -> - fraction { integer: "drei und zwanzig" numerator: "vier" denominator: "sechs" preserve_order: true } - - Args: - cardinal: cardinal GraphFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, cardinal, deterministic: bool = True): - super().__init__(name="fraction", kind="classify", deterministic=deterministic) - cardinal_graph = cardinal.graph - - self.optional_graph_negative = pynini.closure( - pynutil.insert("negative: ") + pynini.cross("-", "\"true\" "), 0, 1 - ) - self.integer = pynutil.insert("integer_part: \"") + cardinal_graph + pynutil.insert("\"") - self.numerator = ( - pynutil.insert("numerator: \"") + cardinal_graph + pynini.cross(pynini.union("/", " / "), "\" ") - ) - self.denominator = pynutil.insert("denominator: \"") + cardinal_graph + pynutil.insert("\"") - - self.graph = ( - self.optional_graph_negative - + pynini.closure(self.integer + pynini.accep(" "), 0, 1) - + self.numerator - + self.denominator - ) - - graph = self.graph + pynutil.insert(" preserve_order: true") - final_graph = self.add_tokens(graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/de/taggers/measure.py b/nemo_text_processing/text_normalization/de/taggers/measure.py deleted file mode 100644 index c9dbe412056f..000000000000 --- a/nemo_text_processing/text_normalization/de/taggers/measure.py +++ /dev/null @@ -1,187 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.de.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_ALPHA, - NEMO_DIGIT, - NEMO_NON_BREAKING_SPACE, - NEMO_SIGMA, - GraphFst, - convert_space, - insert_space, -) -from pynini.examples import plurals -from pynini.lib import pynutil - -unit_singular = pynini.string_file(get_abs_path("data/measure/measurements.tsv")) -suppletive = pynini.string_file(get_abs_path("data/measure/suppletive.tsv")) - - -def singular_to_plural(): - # plural endung n/en maskuline Nomen mit den Endungen e, ent, and, ant, ist, or - _n = NEMO_SIGMA + pynini.union("e") + pynutil.insert("n") - _en = ( - NEMO_SIGMA - + pynini.union("ent", "and", "ant", "ist", "or", "ion", "ik", "heit", "keit", "schaft", "tät", "ung") - + pynutil.insert("en") - ) - _nen = NEMO_SIGMA + pynini.union("in") + (pynutil.insert("e") | pynutil.insert("nen")) - _fremd = NEMO_SIGMA + pynini.union("ma", "um", "us") + pynutil.insert("en") - # maskuline Nomen mit den Endungen eur, ich, ier, ig, ling, ör - _e = NEMO_SIGMA + pynini.union("eur", "ich", "ier", "ig", "ling", "ör") + pynutil.insert("e") - _s = NEMO_SIGMA + pynini.union("a", "i", "o", "u", "y") + pynutil.insert("s") - - graph_plural = plurals._priority_union( - suppletive, pynini.union(_n, _en, _nen, _fremd, _e, _s), NEMO_SIGMA - ).optimize() - - return graph_plural - - -class MeasureFst(GraphFst): - """ - Finite state transducer for classifying measure, e.g. - "2,4 oz" -> measure { cardinal { integer_part: "zwei" fractional_part: "vier" units: "unzen" preserve_order: true } } - "1 oz" -> measure { cardinal { integer: "zwei" units: "unze" preserve_order: true } } - "1 million oz" -> measure { cardinal { integer: "eins" quantity: "million" units: "unze" preserve_order: true } } - This class also converts words containing numbers and letters - e.g. "a-8" —> "a acht" - e.g. "1,2-a" —> "ein komma zwei a" - - Args: - cardinal: CardinalFst - decimal: DecimalFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, cardinal: GraphFst, decimal: GraphFst, fraction: GraphFst, deterministic: bool = True): - super().__init__(name="measure", kind="classify", deterministic=deterministic) - cardinal_graph = cardinal.graph - - graph_unit_singular = convert_space(unit_singular) - graph_unit_plural = graph_unit_singular @ pynini.cdrewrite(convert_space(suppletive), "", "[EOS]", NEMO_SIGMA) - optional_graph_negative = pynini.closure("-", 0, 1) - - graph_unit_denominator = ( - pynini.cross("/", "pro") + pynutil.insert(NEMO_NON_BREAKING_SPACE) + graph_unit_singular - ) - - optional_unit_denominator = pynini.closure( - pynutil.insert(NEMO_NON_BREAKING_SPACE) + graph_unit_denominator, 0, 1, - ) - - unit_plural = ( - pynutil.insert("units: \"") - + (graph_unit_plural + (optional_unit_denominator) | graph_unit_denominator) - + pynutil.insert("\"") - ) - - unit_singular_graph = ( - pynutil.insert("units: \"") - + ((graph_unit_singular + optional_unit_denominator) | graph_unit_denominator) - + pynutil.insert("\"") - ) - - subgraph_decimal = decimal.fst + insert_space + pynini.closure(pynutil.delete(" "), 0, 1) + unit_plural - - subgraph_cardinal = ( - (optional_graph_negative + (pynini.closure(NEMO_DIGIT) - "1")) @ cardinal.fst - + insert_space - + pynini.closure(pynutil.delete(" "), 0, 1) - + unit_plural - ) - - subgraph_cardinal |= ( - (optional_graph_negative + pynini.accep("1")) - @ cardinal.fst - @ pynini.cdrewrite(pynini.cross("eins", "ein"), "", "", NEMO_SIGMA) - + insert_space - + pynini.closure(pynutil.delete(" "), 0, 1) - + unit_singular_graph - ) - - subgraph_fraction = fraction.fst + insert_space + pynini.closure(pynutil.delete(" "), 0, 1) + unit_plural - - cardinal_dash_alpha = ( - pynutil.insert("cardinal { integer: \"") - + cardinal_graph - + pynutil.delete('-') - + pynutil.insert("\" } units: \"") - + pynini.closure(NEMO_ALPHA, 1) - + pynutil.insert("\"") - ) - - alpha_dash_cardinal = ( - pynutil.insert("units: \"") - + pynini.closure(NEMO_ALPHA, 1) - + pynutil.delete('-') - + pynutil.insert("\"") - + pynutil.insert(" cardinal { integer: \"") - + cardinal_graph - + pynutil.insert("\" }") - ) - - decimal_dash_alpha = ( - pynutil.insert("decimal { ") - + decimal.final_graph_wo_negative - + pynutil.delete('-') - + pynutil.insert(" } units: \"") - + pynini.closure(NEMO_ALPHA, 1) - + pynutil.insert("\"") - ) - - decimal_times = ( - pynutil.insert("decimal { ") - + decimal.final_graph_wo_negative - + pynutil.insert(" } units: \"") - + pynini.union('x', 'X') - + pynutil.insert("\"") - ) - - cardinal_times = ( - pynutil.insert("cardinal { integer: \"") - + cardinal_graph - + pynutil.insert("\" } units: \"") - + pynini.union('x', 'X') - + pynutil.insert("\"") - ) - - alpha_dash_decimal = ( - pynutil.insert("units: \"") - + pynini.closure(NEMO_ALPHA, 1) - + pynutil.delete('-') - + pynutil.insert("\"") - + pynutil.insert(" decimal { ") - + decimal.final_graph_wo_negative - + pynutil.insert(" }") - ) - - final_graph = ( - subgraph_decimal - | subgraph_cardinal - | cardinal_dash_alpha - | alpha_dash_cardinal - | decimal_dash_alpha - | decimal_times - | alpha_dash_decimal - | subgraph_fraction - | cardinal_times - ) - final_graph += pynutil.insert(" preserve_order: true") - final_graph = self.add_tokens(final_graph) - - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/de/taggers/money.py b/nemo_text_processing/text_normalization/de/taggers/money.py deleted file mode 100644 index bc27d41596a4..000000000000 --- a/nemo_text_processing/text_normalization/de/taggers/money.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.de.utils import get_abs_path, load_labels -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_ALPHA, - NEMO_DIGIT, - NEMO_SIGMA, - GraphFst, - convert_space, - insert_space, -) -from pynini.lib import pynutil - -min_singular = pynini.string_file(get_abs_path("data/money/currency_minor_singular.tsv")) -min_plural = pynini.string_file(get_abs_path("data/money/currency_minor_plural.tsv")) -maj_singular = pynini.string_file((get_abs_path("data/money/currency.tsv"))) - - -class MoneyFst(GraphFst): - """ - Finite state transducer for classifying money, e.g. - "€1" -> money { currency_maj: "euro" integer_part: "ein"} - "€1,000" -> money { currency_maj: "euro" integer_part: "ein" } - "€1,001" -> money { currency_maj: "euro" integer_part: "eins" fractional_part: "null null eins"} - "£1,4" -> money { integer_part: "ein" currency_maj: "pfund" fractional_part: "vierzig" preserve_order: true} - -> money { integer_part: "ein" currency_maj: "pfund" fractional_part: "vierzig" currency_min: "pence" preserve_order: true} - "£0,01" -> money { fractional_part: "ein" currency_min: "penny" preserve_order: true} - "£0,01 million" -> money { currency_maj: "pfund" integer_part: "null" fractional_part: "null eins" quantity: "million"} - - Args: - cardinal: CardinalFst - decimal: DecimalFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, cardinal: GraphFst, decimal: GraphFst, deterministic: bool = True): - super().__init__(name="money", kind="classify", deterministic=deterministic) - cardinal_graph = cardinal.graph - graph_decimal_final = decimal.fst - - maj_singular_labels = load_labels(get_abs_path("data/money/currency.tsv")) - maj_singular_graph = convert_space(maj_singular) - maj_plural_graph = maj_singular_graph - - graph_maj_singular = pynutil.insert("currency_maj: \"") + maj_singular_graph + pynutil.insert("\"") - graph_maj_plural = pynutil.insert("currency_maj: \"") + maj_plural_graph + pynutil.insert("\"") - - optional_delete_fractional_zeros = pynini.closure( - pynutil.delete(",") + pynini.closure(pynutil.delete("0"), 1), 0, 1 - ) - graph_integer_one = pynutil.insert("integer_part: \"") + pynini.cross("1", "ein") + pynutil.insert("\"") - - # only for decimals where third decimal after comma is non-zero or with quantity - decimal_delete_last_zeros = ( - pynini.closure(NEMO_DIGIT, 1) - + pynini.accep(",") - + pynini.closure(NEMO_DIGIT, 2) - + (NEMO_DIGIT - "0") - + pynini.closure(pynutil.delete("0")) - ) - decimal_with_quantity = NEMO_SIGMA + NEMO_ALPHA - graph_decimal = ( - graph_maj_plural + insert_space + (decimal_delete_last_zeros | decimal_with_quantity) @ graph_decimal_final - ) - - graph_integer = ( - pynutil.insert("integer_part: \"") + ((NEMO_SIGMA - "1") @ cardinal_graph) + pynutil.insert("\"") - ) - - graph_integer_only = graph_maj_singular + insert_space + graph_integer_one - graph_integer_only |= graph_maj_plural + insert_space + graph_integer - - graph = (graph_integer_only + optional_delete_fractional_zeros) | graph_decimal - - # remove trailing zeros of non zero number in the first 2 digits and fill up to 2 digits - # e.g. 2000 -> 20, 0200->02, 01 -> 01, 10 -> 10 - # not accepted: 002, 00, 0, - two_digits_fractional_part = ( - pynini.closure(NEMO_DIGIT) + (NEMO_DIGIT - "0") + pynini.closure(pynutil.delete("0")) - ) @ ( - (pynutil.delete("0") + (NEMO_DIGIT - "0")) - | ((NEMO_DIGIT - "0") + pynutil.insert("0")) - | ((NEMO_DIGIT - "0") + NEMO_DIGIT) - ) - - graph_min_singular = pynutil.insert(" currency_min: \"") + min_singular + pynutil.insert("\"") - graph_min_plural = pynutil.insert(" currency_min: \"") + min_plural + pynutil.insert("\"") - - # format ** euro ** cent - decimal_graph_with_minor = None - for curr_symbol, _ in maj_singular_labels: - preserve_order = pynutil.insert(" preserve_order: true") - integer_plus_maj = graph_integer + insert_space + pynutil.insert(curr_symbol) @ graph_maj_plural - integer_plus_maj |= graph_integer_one + insert_space + pynutil.insert(curr_symbol) @ graph_maj_singular - # non zero integer part - integer_plus_maj = (pynini.closure(NEMO_DIGIT) - "0") @ integer_plus_maj - - graph_fractional_one = two_digits_fractional_part @ pynini.cross("1", "ein") - graph_fractional_one = pynutil.insert("fractional_part: \"") + graph_fractional_one + pynutil.insert("\"") - graph_fractional = ( - two_digits_fractional_part @ (pynini.closure(NEMO_DIGIT, 1, 2) - "1") @ cardinal.two_digit_non_zero - ) - graph_fractional = pynutil.insert("fractional_part: \"") + graph_fractional + pynutil.insert("\"") - - fractional_plus_min = graph_fractional + insert_space + pynutil.insert(curr_symbol) @ graph_min_plural - fractional_plus_min |= ( - graph_fractional_one + insert_space + pynutil.insert(curr_symbol) @ graph_min_singular - ) - - decimal_graph_with_minor_curr = integer_plus_maj + pynini.cross(",", " ") + fractional_plus_min - decimal_graph_with_minor_curr |= pynutil.add_weight( - integer_plus_maj - + pynini.cross(",", " ") - + pynutil.insert("fractional_part: \"") - + two_digits_fractional_part @ cardinal.two_digit_non_zero - + pynutil.insert("\""), - weight=0.0001, - ) - - decimal_graph_with_minor_curr |= pynutil.delete("0,") + fractional_plus_min - decimal_graph_with_minor_curr = ( - pynutil.delete(curr_symbol) + decimal_graph_with_minor_curr + preserve_order - ) - - decimal_graph_with_minor = ( - decimal_graph_with_minor_curr - if decimal_graph_with_minor is None - else pynini.union(decimal_graph_with_minor, decimal_graph_with_minor_curr) - ) - - final_graph = graph | decimal_graph_with_minor - - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/de/taggers/ordinal.py b/nemo_text_processing/text_normalization/de/taggers/ordinal.py deleted file mode 100644 index 5644665518b0..000000000000 --- a/nemo_text_processing/text_normalization/de/taggers/ordinal.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Adapted from https://github.com/google/TextNormalizationCoveringGrammars -# Russian minimally supervised number grammar. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_DIGIT, GraphFst -from pynini.lib import pynutil - - -class OrdinalFst(GraphFst): - """ - Finite state transducer for classifying cardinals, e.g. - "2." -> ordinal { integer: "zwei" } } - "2tes" -> ordinal { integer: "zwei" } } - - Args: - cardinal: cardinal GraphFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, cardinal: GraphFst, deterministic=False): - super().__init__(name="ordinal", kind="classify", deterministic=deterministic) - - cardinal_graph = cardinal.graph - endings = ["ter", "tes", "tem", "te", "ten"] - self.graph = ( - ( - pynini.closure(NEMO_DIGIT | pynini.accep(".")) - + pynutil.delete(pynutil.add_weight(pynini.union(*endings), weight=0.0001) | pynini.accep(".")) - ) - @ cardinal_graph - ).optimize() - final_graph = pynutil.insert("integer: \"") + self.graph + pynutil.insert("\"") - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/de/taggers/telephone.py b/nemo_text_processing/text_normalization/de/taggers/telephone.py deleted file mode 100644 index f4306b9be773..000000000000 --- a/nemo_text_processing/text_normalization/de/taggers/telephone.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.de.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_DIGIT, GraphFst, insert_space -from pynini.lib import pynutil - - -class TelephoneFst(GraphFst): - """ - Finite state transducer for classifying telephone, which includes country code, number part and extension - - E.g - "+49 1234-1233" -> telephone { country_code: "plus neun und vierzig" number_part: "eins zwei drei vier eins zwei drei drei" preserve_order: true } - "(012) 1234-1233" -> telephone { country_code: "null eins zwei" number_part: "eins zwei drei vier eins zwei drei drei" preserve_order: true } - (0**) - - Args: - cardinal: cardinal GraphFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, cardinal: GraphFst, deterministic: bool = True): - super().__init__(name="telephone", kind="classify", deterministic=deterministic) - - graph_zero = pynini.invert(pynini.string_file(get_abs_path("data/numbers/zero.tsv"))).optimize() - graph_digit_no_zero = pynini.invert( - pynini.string_file(get_abs_path("data/numbers/digit.tsv")) - ).optimize() | pynini.cross("1", "eins") - graph_digit = graph_digit_no_zero | graph_zero - - numbers_with_single_digits = pynini.closure(graph_digit + insert_space) + graph_digit - - two_digit_and_zero = (NEMO_DIGIT ** 2 @ cardinal.two_digit_non_zero) | graph_zero - # def add_space_after_two_digit(): - # return pynini.closure(two_digit_and_zero + insert_space) + ( - # two_digit_and_zero - # ) - - country_code = pynini.closure(pynini.cross("+", "plus "), 0, 1) + two_digit_and_zero - country_code |= ( - pynutil.delete("(") + graph_zero + insert_space + numbers_with_single_digits + pynutil.delete(")") - ) - country_code |= graph_zero + insert_space + numbers_with_single_digits - - country_code = pynutil.insert("country_code: \"") + country_code + pynutil.insert("\"") - - del_separator = pynini.cross(pynini.union("-", " "), " ") - # numbers_with_two_digits = pynini.closure(graph_digit + insert_space) + add_space_after_two_digit() + pynini.closure(insert_space + graph_digit) - # numbers = numbers_with_two_digits + pynini.closure(del_separator + numbers_with_two_digits, 0, 1) - numbers = numbers_with_single_digits + pynini.closure(del_separator + numbers_with_single_digits, 0, 1) - number_length = pynini.closure((NEMO_DIGIT | pynini.union("-", " ", ")", "(")), 7) - number_part = pynini.compose(number_length, numbers) - number = pynutil.insert("number_part: \"") + number_part + pynutil.insert("\"") - - graph = country_code + pynini.accep(" ") + number - self.graph = graph - final_graph = self.add_tokens(self.graph + pynutil.insert(" preserve_order: true")) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/de/taggers/time.py b/nemo_text_processing/text_normalization/de/taggers/time.py deleted file mode 100644 index b9e244635eff..000000000000 --- a/nemo_text_processing/text_normalization/de/taggers/time.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.text_normalization.de.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_DIGIT, GraphFst, convert_space, insert_space -from pynini.lib import pynutil - - -class TimeFst(GraphFst): - """ - Finite state transducer for classifying time, e.g. - "02:15 Uhr est" -> time { hours: "2" minutes: "15" zone: "e s t"} - "2 Uhr" -> time { hours: "2" } - "09:00 Uhr" -> time { hours: "2" } - "02:15:10 Uhr" -> time { hours: "2" minutes: "15" seconds: "10"} - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="time", kind="classify", deterministic=deterministic) - - final_suffix = pynutil.delete(" ") + pynutil.delete("Uhr") | pynutil.delete("uhr") - time_zone_graph = pynini.string_file(get_abs_path("data/time/time_zone.tsv")) - - labels_hour = [str(x) for x in range(0, 25)] - labels_minute_single = [str(x) for x in range(1, 10)] - labels_minute_double = [str(x) for x in range(10, 60)] - - delete_leading_zero_to_double_digit = (pynutil.delete("0") | (NEMO_DIGIT - "0")) + NEMO_DIGIT - - graph_hour = pynini.union(*labels_hour) - - graph_minute_single = pynini.union(*labels_minute_single) - graph_minute_double = pynini.union(*labels_minute_double) - - final_graph_hour_only = pynutil.insert("hours: \"") + graph_hour + pynutil.insert("\"") - final_graph_hour = ( - pynutil.insert("hours: \"") + delete_leading_zero_to_double_digit @ graph_hour + pynutil.insert("\"") - ) - final_graph_minute = ( - pynutil.insert("minutes: \"") - + (pynutil.delete("0") + graph_minute_single | graph_minute_double) - + pynutil.insert("\"") - ) - final_graph_second = ( - pynutil.insert("seconds: \"") - + (pynutil.delete("0") + graph_minute_single | graph_minute_double) - + pynutil.insert("\"") - ) - final_time_zone_optional = pynini.closure( - pynini.accep(" ") + pynutil.insert("zone: \"") + convert_space(time_zone_graph) + pynutil.insert("\""), - 0, - 1, - ) - - # 02:30 Uhr - graph_hm = ( - final_graph_hour - + pynutil.delete(":") - + (pynutil.delete("00") | (insert_space + final_graph_minute)) - + final_suffix - + final_time_zone_optional - ) - - # 10:30:05 Uhr, - graph_hms = ( - final_graph_hour - + pynutil.delete(":") - + (pynini.cross("00", " minutes: \"0\"") | (insert_space + final_graph_minute)) - + pynutil.delete(":") - + (pynini.cross("00", " seconds: \"0\"") | (insert_space + final_graph_second)) - + final_suffix - + final_time_zone_optional - + pynutil.insert(" preserve_order: true") - ) - - # 2 Uhr est - graph_h = final_graph_hour_only + final_suffix + final_time_zone_optional - final_graph = (graph_hm | graph_h | graph_hms).optimize() - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/de/taggers/tokenize_and_classify.py b/nemo_text_processing/text_normalization/de/taggers/tokenize_and_classify.py deleted file mode 100644 index a446fcfdb714..000000000000 --- a/nemo_text_processing/text_normalization/de/taggers/tokenize_and_classify.py +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -import pynini -from nemo_text_processing.text_normalization.de.taggers.cardinal import CardinalFst -from nemo_text_processing.text_normalization.de.taggers.date import DateFst -from nemo_text_processing.text_normalization.de.taggers.decimal import DecimalFst -from nemo_text_processing.text_normalization.de.taggers.electronic import ElectronicFst -from nemo_text_processing.text_normalization.de.taggers.fraction import FractionFst -from nemo_text_processing.text_normalization.de.taggers.measure import MeasureFst -from nemo_text_processing.text_normalization.de.taggers.money import MoneyFst -from nemo_text_processing.text_normalization.de.taggers.ordinal import OrdinalFst -from nemo_text_processing.text_normalization.de.taggers.telephone import TelephoneFst -from nemo_text_processing.text_normalization.de.taggers.time import TimeFst -from nemo_text_processing.text_normalization.de.taggers.whitelist import WhiteListFst -from nemo_text_processing.text_normalization.de.taggers.word import WordFst -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_CHAR, - NEMO_DIGIT, - GraphFst, - delete_extra_space, - delete_space, - generator_main, -) -from nemo_text_processing.text_normalization.en.taggers.punctuation import PunctuationFst -from pynini.lib import pynutil - -from nemo.utils import logging - - -class ClassifyFst(GraphFst): - """ - Final class that composes all other classification grammars. This class can process an entire sentence, that is - lower cased. For deployment, this grammar will be compiled and exported to OpenFst Finite State Archive (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - - Args: - input_case: accepting either "lower_cased" or "cased" input. - deterministic: if True will provide a single transduction option, - for False multiple options (used for audio-based normalization) - cache_dir: path to a dir with .far grammar file. Set to None to avoid using cache. - overwrite_cache: set to True to overwrite .far files - whitelist: path to a file with whitelist replacements - """ - - def __init__( - self, - input_case: str, - deterministic: bool = False, - cache_dir: str = None, - overwrite_cache: bool = False, - whitelist: str = None, - ): - super().__init__(name="tokenize_and_classify", kind="classify", deterministic=deterministic) - far_file = None - if cache_dir is not None and cache_dir != "None": - os.makedirs(cache_dir, exist_ok=True) - whitelist_file = os.path.basename(whitelist) if whitelist else "" - far_file = os.path.join( - cache_dir, f"_{input_case}_de_tn_{deterministic}_deterministic{whitelist_file}.far" - ) - if not overwrite_cache and far_file and os.path.exists(far_file): - self.fst = pynini.Far(far_file, mode="r")["tokenize_and_classify"] - no_digits = pynini.closure(pynini.difference(NEMO_CHAR, NEMO_DIGIT)) - self.fst_no_digits = pynini.compose(self.fst, no_digits).optimize() - logging.info(f"ClassifyFst.fst was restored from {far_file}.") - else: - logging.info(f"Creating ClassifyFst grammars. This might take some time...") - - self.cardinal = CardinalFst(deterministic=deterministic) - cardinal_graph = self.cardinal.fst - - self.ordinal = OrdinalFst(cardinal=self.cardinal, deterministic=deterministic) - ordinal_graph = self.ordinal.fst - - self.decimal = DecimalFst(cardinal=self.cardinal, deterministic=deterministic) - decimal_graph = self.decimal.fst - - self.fraction = FractionFst(cardinal=self.cardinal, deterministic=deterministic) - fraction_graph = self.fraction.fst - self.measure = MeasureFst( - cardinal=self.cardinal, decimal=self.decimal, fraction=self.fraction, deterministic=deterministic - ) - measure_graph = self.measure.fst - self.date = DateFst(cardinal=self.cardinal, deterministic=deterministic) - date_graph = self.date.fst - word_graph = WordFst(deterministic=deterministic).fst - self.time = TimeFst(deterministic=deterministic) - time_graph = self.time.fst - self.telephone = TelephoneFst(cardinal=self.cardinal, deterministic=deterministic) - telephone_graph = self.telephone.fst - self.electronic = ElectronicFst(deterministic=deterministic) - electronic_graph = self.electronic.fst - self.money = MoneyFst(cardinal=self.cardinal, decimal=self.decimal, deterministic=deterministic) - money_graph = self.money.fst - self.whitelist = WhiteListFst(input_case=input_case, deterministic=deterministic, input_file=whitelist) - whitelist_graph = self.whitelist.fst - punct_graph = PunctuationFst(deterministic=deterministic).fst - - classify = ( - pynutil.add_weight(whitelist_graph, 1.01) - | pynutil.add_weight(time_graph, 1.1) - | pynutil.add_weight(measure_graph, 1.1) - | pynutil.add_weight(cardinal_graph, 1.1) - | pynutil.add_weight(fraction_graph, 1.1) - | pynutil.add_weight(date_graph, 1.1) - | pynutil.add_weight(ordinal_graph, 1.1) - | pynutil.add_weight(decimal_graph, 1.1) - | pynutil.add_weight(money_graph, 1.1) - | pynutil.add_weight(telephone_graph, 1.1) - | pynutil.add_weight(electronic_graph, 1.1) - ) - - classify |= pynutil.add_weight(word_graph, 100) - - punct = pynutil.insert("tokens { ") + pynutil.add_weight(punct_graph, weight=1.1) + pynutil.insert(" }") - token = pynutil.insert("tokens { ") + classify + pynutil.insert(" }") - token_plus_punct = ( - pynini.closure(punct + pynutil.insert(" ")) + token + pynini.closure(pynutil.insert(" ") + punct) - ) - - graph = token_plus_punct + pynini.closure(pynutil.add_weight(delete_extra_space, 1.1) + token_plus_punct) - graph = delete_space + graph + delete_space - - self.fst = graph.optimize() - no_digits = pynini.closure(pynini.difference(NEMO_CHAR, NEMO_DIGIT)) - self.fst_no_digits = pynini.compose(self.fst, no_digits).optimize() - - if far_file: - generator_main(far_file, {"tokenize_and_classify": self.fst}) - logging.info(f"ClassifyFst grammars are saved to {far_file}.") diff --git a/nemo_text_processing/text_normalization/de/taggers/whitelist.py b/nemo_text_processing/text_normalization/de/taggers/whitelist.py deleted file mode 100644 index 4e8829bf17c9..000000000000 --- a/nemo_text_processing/text_normalization/de/taggers/whitelist.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.de.utils import get_abs_path, load_labels -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst, convert_space -from pynini.lib import pynutil - - -class WhiteListFst(GraphFst): - """ - Finite state transducer for classifying whitelist, e.g. - "Mr." -> tokens { name: "mister" } - This class has highest priority among all classifier grammars. Whitelisted tokens are defined and loaded from "data/whitelist.tsv". - - Args: - input_case: accepting either "lower_cased" or "cased" input. - deterministic: if True will provide a single transduction option, - for False multiple options (used for audio-based normalization) - input_file: path to a file with whitelist replacements - """ - - def __init__(self, input_case: str, deterministic: bool = True, input_file: str = None): - super().__init__(name="whitelist", kind="classify", deterministic=deterministic) - - def _get_whitelist_graph(input_case, file): - whitelist = load_labels(file) - if input_case == "lower_cased": - whitelist = [[x[0].lower()] + x[1:] for x in whitelist] - graph = pynini.string_map(whitelist) - return graph - - graph = _get_whitelist_graph(input_case, get_abs_path("data/whitelist.tsv")) - if not deterministic and input_case != "lower_cased": - graph |= pynutil.add_weight( - _get_whitelist_graph("lower_cased", get_abs_path("data/whitelist.tsv")), weight=0.0001 - ) - - if input_file: - whitelist_provided = _get_whitelist_graph(input_case, input_file) - if not deterministic: - graph |= whitelist_provided - else: - graph = whitelist_provided - - if not deterministic: - units_graph = _get_whitelist_graph(input_case, file=get_abs_path("data/measure/measurements.tsv")) - graph |= units_graph - - self.graph = graph - self.final_graph = convert_space(self.graph).optimize() - self.fst = (pynutil.insert("name: \"") + self.final_graph + pynutil.insert("\"")).optimize() diff --git a/nemo_text_processing/text_normalization/de/taggers/word.py b/nemo_text_processing/text_normalization/de/taggers/word.py deleted file mode 100644 index 16743d28b574..000000000000 --- a/nemo_text_processing/text_normalization/de/taggers/word.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_SPACE, GraphFst -from pynini.lib import pynutil - - -class WordFst(GraphFst): - """ - Finite state transducer for classifying word. - e.g. sleep -> tokens { name: "sleep" } - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="word", kind="classify") - word = pynutil.insert("name: \"") + pynini.closure(NEMO_NOT_SPACE, 1) + pynutil.insert("\"") - self.fst = word.optimize() diff --git a/nemo_text_processing/text_normalization/de/utils.py b/nemo_text_processing/text_normalization/de/utils.py deleted file mode 100644 index 9186c73dbe6e..000000000000 --- a/nemo_text_processing/text_normalization/de/utils.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import csv -import os - -from nemo.utils import logging - - -def get_abs_path(rel_path): - """ - Get absolute path - - Args: - rel_path: relative path to this file - - Returns absolute path - """ - abs_path = os.path.dirname(os.path.abspath(__file__)) + os.sep + rel_path - - if not os.path.exists(abs_path): - logging.warning(f'{abs_path} does not exist') - return abs_path - - -def load_labels(abs_path): - """ - loads relative path file as dictionary - - Args: - abs_path: absolute path - - Returns dictionary of mappings - """ - with open(abs_path, encoding="utf-8") as label_tsv: - labels = list(csv.reader(label_tsv, delimiter="\t")) - return labels diff --git a/nemo_text_processing/text_normalization/de/verbalizers/__init__.py b/nemo_text_processing/text_normalization/de/verbalizers/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/text_normalization/de/verbalizers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/de/verbalizers/cardinal.py b/nemo_text_processing/text_normalization/de/verbalizers/cardinal.py deleted file mode 100644 index 6adf17dd78ea..000000000000 --- a/nemo_text_processing/text_normalization/de/verbalizers/cardinal.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst -from pynini.lib import pynutil - - -class CardinalFst(GraphFst): - """ - Finite state transducer for verbalizing cardinals - e.g. cardinal { integer: "zwei" } -> "zwei" - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="cardinal", kind="verbalize", deterministic=deterministic) - optional_sign = pynini.closure(pynini.cross("negative: \"true\" ", "minus "), 0, 1) - self.optional_sign = optional_sign - integer = pynini.closure(NEMO_NOT_QUOTE, 1) - - self.integer = pynutil.delete(" \"") + integer + pynutil.delete("\"") - - integer = pynutil.delete("integer:") + self.integer - self.numbers = integer - graph = optional_sign + self.numbers - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/de/verbalizers/date.py b/nemo_text_processing/text_normalization/de/verbalizers/date.py deleted file mode 100644 index 2a1a8ed72546..000000000000 --- a/nemo_text_processing/text_normalization/de/verbalizers/date.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.de.utils import get_abs_path, load_labels -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_NOT_QUOTE, - NEMO_SIGMA, - GraphFst, - delete_preserve_order, -) -from pynini.lib import pynutil - - -class DateFst(GraphFst): - """ - Finite state transducer for verbalizing date, e.g. - date { day: "vier" month: "april" year: "zwei tausend zwei" } -> "vierter april zwei tausend zwei" - date { day: "vier" month: "mai" year: "zwei tausend zwei" } -> "vierter mai zwei tausend zwei" - - Args: - ordinal: ordinal verbalizer GraphFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, ordinal: GraphFst, deterministic: bool = True): - super().__init__(name="date", kind="verbalize", deterministic=deterministic) - - day_cardinal = pynutil.delete("day: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - day = day_cardinal @ pynini.cdrewrite(ordinal.ordinal_stem, "", "[EOS]", NEMO_SIGMA) + pynutil.insert("ter") - - months_names = pynini.union(*[x[1] for x in load_labels(get_abs_path("data/months/abbr_to_name.tsv"))]) - month = pynutil.delete("month: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - final_month = month @ months_names - final_month |= month @ pynini.difference(NEMO_SIGMA, months_names) @ pynini.cdrewrite( - ordinal.ordinal_stem, "", "[EOS]", NEMO_SIGMA - ) + pynutil.insert("ter") - - year = pynutil.delete("year: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - - # day month year - graph_dmy = day + pynini.accep(" ") + final_month + pynini.closure(pynini.accep(" ") + year, 0, 1) - graph_dmy |= final_month + pynini.accep(" ") + year - - self.graph = graph_dmy | year - final_graph = self.graph + delete_preserve_order - - delete_tokens = self.delete_tokens(final_graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/de/verbalizers/decimal.py b/nemo_text_processing/text_normalization/de/verbalizers/decimal.py deleted file mode 100644 index 4e5ac7f9411e..000000000000 --- a/nemo_text_processing/text_normalization/de/verbalizers/decimal.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.de.taggers.decimal import quantities -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_NOT_QUOTE, - GraphFst, - delete_preserve_order, - insert_space, -) -from pynini.lib import pynutil - - -class DecimalFst(GraphFst): - """ - Finite state transducer for classifying decimal, e.g. - decimal { negative: "true" integer_part: "elf" fractional_part: "vier null sechs" quantity: "billionen" } -> minus elf komma vier null sechs billionen - decimal { integer_part: "eins" quantity: "billion" } -> eins billion - - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="decimal", kind="classify", deterministic=deterministic) - - delete_space = pynutil.delete(" ") - self.optional_sign = pynini.closure(pynini.cross("negative: \"true\"", "minus ") + delete_space, 0, 1) - self.integer = pynutil.delete("integer_part: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - self.fractional_default = ( - pynutil.delete("fractional_part: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - ) - - self.fractional = pynutil.insert(" komma ") + self.fractional_default - - self.quantity = ( - delete_space + insert_space + pynutil.delete("quantity: \"") + quantities + pynutil.delete("\"") - ) - self.optional_quantity = pynini.closure(self.quantity, 0, 1) - - graph = self.optional_sign + ( - self.integer + self.quantity | self.integer + delete_space + self.fractional + self.optional_quantity - ) - - self.numbers = graph - graph += delete_preserve_order - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/de/verbalizers/electronic.py b/nemo_text_processing/text_normalization/de/verbalizers/electronic.py deleted file mode 100644 index 184ce01b1c5f..000000000000 --- a/nemo_text_processing/text_normalization/de/verbalizers/electronic.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.de.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_NOT_QUOTE, - NEMO_SIGMA, - GraphFst, - delete_preserve_order, - insert_space, -) -from pynini.lib import pynutil - - -class ElectronicFst(GraphFst): - """ - Finite state transducer for verbalizing electronic - e.g. electronic { username: "abc" domain: "hotmail.com" } -> "a b c at hotmail punkt com" - -> "a b c at h o t m a i l punkt c o m" - -> "a b c at hotmail punkt c o m" - -> "a b c at h o t m a i l punkt com" - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="electronic", kind="verbalize", deterministic=deterministic) - graph_digit_no_zero = pynini.invert( - pynini.string_file(get_abs_path("data/numbers/digit.tsv")) - ).optimize() | pynini.cross("1", "eins") - graph_zero = pynini.invert(pynini.string_file(get_abs_path("data/numbers/zero.tsv"))).optimize() - graph_digit = graph_digit_no_zero | graph_zero - graph_symbols = pynini.string_file(get_abs_path("data/electronic/symbols.tsv")).optimize() - server_common = pynini.string_file(get_abs_path("data/electronic/server_name.tsv")) - domain_common = pynini.string_file(get_abs_path("data/electronic/domain.tsv")) - - def add_space_after_char(): - return pynini.closure(NEMO_NOT_QUOTE - pynini.accep(" ") + insert_space) + ( - NEMO_NOT_QUOTE - pynini.accep(" ") - ) - - verbalize_characters = pynini.cdrewrite(graph_symbols | graph_digit, "", "", NEMO_SIGMA) - - user_name = pynutil.delete("username: \"") + add_space_after_char() + pynutil.delete("\"") - user_name @= verbalize_characters - - convert_defaults = pynutil.add_weight(NEMO_NOT_QUOTE, weight=0.0001) | domain_common | server_common - domain = convert_defaults + pynini.closure(insert_space + convert_defaults) - domain @= verbalize_characters - - domain = pynutil.delete("domain: \"") + domain + pynutil.delete("\"") - protocol = ( - pynutil.delete("protocol: \"") - + add_space_after_char() @ pynini.cdrewrite(graph_symbols, "", "", NEMO_SIGMA) - + pynutil.delete("\"") - ) - self.graph = (pynini.closure(protocol + pynini.accep(" "), 0, 1) + domain) | ( - user_name + pynini.accep(" ") + pynutil.insert("at ") + domain - ) - delete_tokens = self.delete_tokens(self.graph + delete_preserve_order) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/de/verbalizers/fraction.py b/nemo_text_processing/text_normalization/de/verbalizers/fraction.py deleted file mode 100644 index 2221a2912887..000000000000 --- a/nemo_text_processing/text_normalization/de/verbalizers/fraction.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_NOT_QUOTE, - NEMO_SIGMA, - GraphFst, - delete_preserve_order, - insert_space, -) -from pynini.lib import pynutil - - -class FractionFst(GraphFst): - """ - Finite state transducer for verbalizing fraction - e.g. fraction { integer: "drei" numerator: "eins" denominator: "zwei" }-> drei ein halb - e.g. fraction { numerator: "vier" denominator: "zwei" } -> vier halbe - e.g. fraction { numerator: "drei" denominator: "vier" } -> drei viertel - - Args: - ordinal: ordinal GraphFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, ordinal: GraphFst, deterministic: bool = True): - super().__init__(name="fraction", kind="verbalize", deterministic=deterministic) - - optional_sign = pynini.closure(pynini.cross("negative: \"true\"", "minus ") + pynutil.delete(" "), 0, 1) - change_one = pynini.cdrewrite( - pynutil.add_weight(pynini.cross("eins", "ein"), weight=-0.0001), "[BOS]", "[EOS]", NEMO_SIGMA - ) - change_numerator_two = pynini.cdrewrite(pynini.cross("zweitel", "halbe"), "[BOS]", "[EOS]", NEMO_SIGMA) - integer = pynutil.delete("integer_part: \"") + change_one + pynutil.delete("\" ") - numerator = pynutil.delete("numerator: \"") + change_one + pynutil.delete("\" ") - denominator = ( - pynutil.delete("denominator: \"") - + pynini.closure(NEMO_NOT_QUOTE) - @ ( - pynini.cdrewrite(pynini.closure(ordinal.ordinal_stem, 0, 1), "", "[EOS]", NEMO_SIGMA) - + pynutil.insert("tel") - ) - @ change_numerator_two - + pynutil.delete("\"") - ) - - integer += insert_space + pynini.closure(pynutil.insert("und ", weight=0.001), 0, 1) - - denominator_one_half = pynini.cdrewrite(pynini.cross("ein halbe", "ein halb"), "[BOS]", "[EOS]", NEMO_SIGMA) - - fraction_default = (numerator + insert_space + denominator) @ denominator_one_half - - self.graph = optional_sign + pynini.closure(integer, 0, 1) + fraction_default - - graph = self.graph + delete_preserve_order - - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/de/verbalizers/measure.py b/nemo_text_processing/text_normalization/de/verbalizers/measure.py deleted file mode 100644 index b9bcd190e272..000000000000 --- a/nemo_text_processing/text_normalization/de/verbalizers/measure.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_NOT_QUOTE, - GraphFst, - delete_extra_space, - delete_preserve_order, -) -from pynini.lib import pynutil - - -class MeasureFst(GraphFst): - """ - Finite state transducer for verbalizing measure, e.g. - measure { cardinal { integer: "zwei" units: "unzen" } } -> "zwei unzen" - measure { cardinal { integer_part: "zwei" quantity: "millionen" units: "unzen" } } -> "zwei millionen unzen" - - Args: - decimal: decimal GraphFst - cardinal: cardinal GraphFst - fraction: fraction GraphFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, decimal: GraphFst, cardinal: GraphFst, fraction: GraphFst, deterministic: bool): - super().__init__(name="measure", kind="verbalize", deterministic=deterministic) - unit = pynutil.delete("units: \"") + pynini.closure(NEMO_NOT_QUOTE) + pynutil.delete("\"") - - graph_decimal = decimal.fst - graph_cardinal = cardinal.fst - graph_fraction = fraction.fst - - graph = (graph_cardinal | graph_decimal | graph_fraction) + pynini.accep(" ") + unit - - graph |= unit + delete_extra_space + (graph_cardinal | graph_decimal) - graph += delete_preserve_order - - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/de/verbalizers/money.py b/nemo_text_processing/text_normalization/de/verbalizers/money.py deleted file mode 100644 index 94fde2482e14..000000000000 --- a/nemo_text_processing/text_normalization/de/verbalizers/money.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_preserve_order -from pynini.lib import pynutil - - -class MoneyFst(GraphFst): - """ - Finite state transducer for verbalizing money, e.g. - money { currency_maj: "euro" integer_part: "ein"} -> "ein euro" - money { currency_maj: "euro" integer_part: "eins" fractional_part: "null null eins"} -> "eins komma null null eins euro" - money { integer_part: "ein" currency_maj: "pfund" fractional_part: "vierzig" preserve_order: true} -> "ein pfund vierzig" - money { integer_part: "ein" currency_maj: "pfund" fractional_part: "vierzig" currency_min: "pence" preserve_order: true} -> "ein pfund vierzig pence" - money { fractional_part: "ein" currency_min: "penny" preserve_order: true} -> "ein penny" - money { currency_maj: "pfund" integer_part: "null" fractional_part: "null eins" quantity: "million"} -> "null komma null eins million pfund" - - Args: - decimal: GraphFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, decimal: GraphFst, deterministic: bool = True): - super().__init__(name="money", kind="verbalize", deterministic=deterministic) - - keep_space = pynini.accep(" ") - - maj = pynutil.delete("currency_maj: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - min = pynutil.delete("currency_min: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - - fractional_part = ( - pynutil.delete("fractional_part: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - ) - - integer_part = pynutil.delete("integer_part: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - optional_add_and = pynini.closure(pynutil.insert("und "), 0, 1) - - # *** currency_maj - graph_integer = integer_part + keep_space + maj - - # *** currency_maj + (***) | ((und) *** current_min) - graph_integer_with_minor = ( - integer_part - + keep_space - + maj - + keep_space - + (fractional_part | (optional_add_and + fractional_part + keep_space + min)) - + delete_preserve_order - ) - - # *** komma *** currency_maj - graph_decimal = decimal.fst + keep_space + maj - - # *** current_min - graph_minor = fractional_part + keep_space + min + delete_preserve_order - - graph = graph_integer | graph_integer_with_minor | graph_decimal | graph_minor - - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/de/verbalizers/ordinal.py b/nemo_text_processing/text_normalization/de/verbalizers/ordinal.py deleted file mode 100644 index 917337c7026d..000000000000 --- a/nemo_text_processing/text_normalization/de/verbalizers/ordinal.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.de.utils import get_abs_path -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, NEMO_SIGMA, GraphFst -from pynini.lib import pynutil - - -class OrdinalFst(GraphFst): - """ - Finite state transducer for verbalizing roman numerals - e.g. ordinal { integer: "vier" } } -> "vierter" - -> "viertes" ... - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="ordinal", kind="verbalize", deterministic=deterministic) - graph_digit = pynini.string_file(get_abs_path("data/ordinals/digit.tsv")).invert() - graph_ties = pynini.string_file(get_abs_path("data/ordinals/ties.tsv")).invert() - graph_thousands = pynini.string_file(get_abs_path("data/ordinals/thousands.tsv")).invert() - - graph = pynutil.delete("integer: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - - suffixes = pynini.union("ten", "tem", "ter", "tes", "te") - convert_rest = pynutil.insert(suffixes, weight=0.01) - self.ordinal_stem = graph_digit | graph_ties | graph_thousands - - suffix = pynini.cdrewrite( - pynini.closure(self.ordinal_stem, 0, 1) + convert_rest, "", "[EOS]", NEMO_SIGMA, - ).optimize() - self.graph = pynini.compose(graph, suffix) - self.suffix = suffix - delete_tokens = self.delete_tokens(self.graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/de/verbalizers/telephone.py b/nemo_text_processing/text_normalization/de/verbalizers/telephone.py deleted file mode 100644 index 25fbeae8c605..000000000000 --- a/nemo_text_processing/text_normalization/de/verbalizers/telephone.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_preserve_order -from pynini.lib import pynutil - - -class TelephoneFst(GraphFst): - """ - Finite state transducer for verbalizing telephone, e.g. - telephone { country_code: "plus neun und vierzig" number_part: "null eins eins eins null null null" } - -> "plus neun und vierzig null eins eins eins null null null" - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="telephone", kind="verbalize", deterministic=deterministic) - - country_code = pynutil.delete("country_code: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - - number_part = pynutil.delete("number_part: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - - self.graph = country_code + pynini.accep(" ") + number_part - - graph = self.graph + delete_preserve_order - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/de/verbalizers/time.py b/nemo_text_processing/text_normalization/de/verbalizers/time.py deleted file mode 100644 index f6ba49332bfa..000000000000 --- a/nemo_text_processing/text_normalization/de/verbalizers/time.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.de.utils import get_abs_path, load_labels -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_DIGIT, - NEMO_SIGMA, - GraphFst, - convert_space, - delete_preserve_order, -) -from pynini.lib import pynutil - - -class TimeFst(GraphFst): - """ - Finite state transducer for verbalizing electronic, e.g. - time { hours: "2" minutes: "15"} -> "zwei uhr fünfzehn" - time { minutes: "15" hours: "2" } -> "viertel nach zwei" - time { minutes: "15" hours: "2" } -> "fünfzehn nach zwei" - time { hours: "14" minutes: "15"} -> "vierzehn uhr fünfzehn" - time { minutes: "15" hours: "14" } -> "viertel nach zwei" - time { minutes: "15" hours: "14" } -> "fünfzehn nach drei" - time { minutes: "45" hours: "14" } -> "viertel vor drei" - - Args: - cardinal_tagger: cardinal_tagger tagger GraphFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, cardinal_tagger: GraphFst, deterministic: bool = True): - super().__init__(name="time", kind="verbalize", deterministic=deterministic) - - # add weight so when using inverse text normalization this conversion is depriotized - night_to_early = pynutil.add_weight( - pynini.invert(pynini.string_file(get_abs_path("data/time/hour_to_night.tsv"))).optimize(), weight=0.0001 - ) - hour_to = pynini.invert(pynini.string_file(get_abs_path("data/time/hour_to.tsv"))).optimize() - minute_to = pynini.invert(pynini.string_file(get_abs_path("data/time/minute_to.tsv"))).optimize() - time_zone_graph = pynini.invert( - convert_space(pynini.union(*[x[1] for x in load_labels(get_abs_path("data/time/time_zone.tsv"))])) - ) - - graph_zero = pynini.invert(pynini.string_file(get_abs_path("data/numbers/zero.tsv"))).optimize() - number_verbalization = graph_zero | cardinal_tagger.two_digit_non_zero - hour = pynutil.delete("hours: \"") + pynini.closure(NEMO_DIGIT, 1) + pynutil.delete("\"") - hour_verbalized = hour @ number_verbalization @ pynini.cdrewrite( - pynini.cross("eins", "ein"), "[BOS]", "[EOS]", NEMO_SIGMA - ) + pynutil.insert(" uhr") - minute = pynutil.delete("minutes: \"") + pynini.closure(NEMO_DIGIT, 1) + pynutil.delete("\"") - zone = pynutil.delete("zone: \"") + time_zone_graph + pynutil.delete("\"") - optional_zone = pynini.closure(pynini.accep(" ") + zone, 0, 1) - second = pynutil.delete("seconds: \"") + pynini.closure(NEMO_DIGIT, 1) + pynutil.delete("\"") - graph_hms = ( - hour_verbalized - + pynini.accep(" ") - + minute @ number_verbalization - + pynutil.insert(" minuten") - + pynini.accep(" ") - + second @ number_verbalization - + pynutil.insert(" sekunden") - + optional_zone - ) - graph_hms @= pynini.cdrewrite( - pynini.cross("eins minuten", "eine minute") | pynini.cross("eins sekunden", "eine sekunde"), - pynini.union(" ", "[BOS]"), - "", - NEMO_SIGMA, - ) - - min_30 = [str(x) for x in range(1, 31)] - min_30 = pynini.union(*min_30) - min_29 = [str(x) for x in range(1, 30)] - min_29 = pynini.union(*min_29) - - graph_h = hour_verbalized - graph_hm = hour_verbalized + pynini.accep(" ") + minute @ number_verbalization - - graph_m_past_h = ( - minute @ min_30 @ (number_verbalization | pynini.cross("15", "viertel")) - + pynini.accep(" ") - + pynutil.insert("nach ") - # + hour @ number_verbalization - + hour @ pynini.cdrewrite(night_to_early, "[BOS]", "[EOS]", NEMO_SIGMA) @ number_verbalization - ) - graph_m30_h = ( - minute @ pynini.cross("30", "halb") - + pynini.accep(" ") - + hour @ pynini.cdrewrite(night_to_early, "[BOS]", "[EOS]", NEMO_SIGMA) @ hour_to @ number_verbalization - ) - graph_m_to_h = ( - minute @ minute_to @ min_29 @ (number_verbalization | pynini.cross("15", "viertel")) - + pynini.accep(" ") - + pynutil.insert("vor ") - + hour @ pynini.cdrewrite(night_to_early, "[BOS]", "[EOS]", NEMO_SIGMA) @ hour_to @ number_verbalization - ) - - self.graph = ( - graph_hms - | graph_h - | graph_hm - | pynutil.add_weight(graph_m_past_h, weight=0.0001) - | pynutil.add_weight(graph_m30_h, weight=0.0001) - | pynutil.add_weight(graph_m_to_h, weight=0.0001) - ) + optional_zone - delete_tokens = self.delete_tokens(self.graph + delete_preserve_order) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/de/verbalizers/verbalize.py b/nemo_text_processing/text_normalization/de/verbalizers/verbalize.py deleted file mode 100644 index e0aaed12522e..000000000000 --- a/nemo_text_processing/text_normalization/de/verbalizers/verbalize.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from nemo_text_processing.text_normalization.de.taggers.cardinal import CardinalFst as CardinalTagger -from nemo_text_processing.text_normalization.de.verbalizers.cardinal import CardinalFst -from nemo_text_processing.text_normalization.de.verbalizers.date import DateFst -from nemo_text_processing.text_normalization.de.verbalizers.decimal import DecimalFst -from nemo_text_processing.text_normalization.de.verbalizers.electronic import ElectronicFst -from nemo_text_processing.text_normalization.de.verbalizers.fraction import FractionFst -from nemo_text_processing.text_normalization.de.verbalizers.measure import MeasureFst -from nemo_text_processing.text_normalization.de.verbalizers.money import MoneyFst -from nemo_text_processing.text_normalization.de.verbalizers.ordinal import OrdinalFst -from nemo_text_processing.text_normalization.de.verbalizers.telephone import TelephoneFst -from nemo_text_processing.text_normalization.de.verbalizers.time import TimeFst -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst -from nemo_text_processing.text_normalization.en.verbalizers.whitelist import WhiteListFst - - -class VerbalizeFst(GraphFst): - """ - Composes other verbalizer grammars. - For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - - Args: - deterministic: if True will provide a single transduction option, - for False multiple options (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="verbalize", kind="verbalize", deterministic=deterministic) - cardinal_tagger = CardinalTagger(deterministic=deterministic) - cardinal = CardinalFst(deterministic=deterministic) - cardinal_graph = cardinal.fst - ordinal = OrdinalFst(deterministic=deterministic) - ordinal_graph = ordinal.fst - decimal = DecimalFst(deterministic=deterministic) - decimal_graph = decimal.fst - fraction = FractionFst(ordinal=ordinal, deterministic=deterministic) - fraction_graph = fraction.fst - date = DateFst(ordinal=ordinal) - date_graph = date.fst - measure = MeasureFst(cardinal=cardinal, decimal=decimal, fraction=fraction, deterministic=deterministic) - measure_graph = measure.fst - electronic = ElectronicFst(deterministic=deterministic) - electronic_graph = electronic.fst - whitelist_graph = WhiteListFst(deterministic=deterministic).fst - money_graph = MoneyFst(decimal=decimal).fst - telephone_graph = TelephoneFst(deterministic=deterministic).fst - time_graph = TimeFst(cardinal_tagger=cardinal_tagger, deterministic=deterministic).fst - - graph = ( - cardinal_graph - | measure_graph - | decimal_graph - | ordinal_graph - | date_graph - | electronic_graph - | money_graph - | fraction_graph - | whitelist_graph - | telephone_graph - | time_graph - ) - self.fst = graph diff --git a/nemo_text_processing/text_normalization/de/verbalizers/verbalize_final.py b/nemo_text_processing/text_normalization/de/verbalizers/verbalize_final.py deleted file mode 100644 index fe1ceaad9089..000000000000 --- a/nemo_text_processing/text_normalization/de/verbalizers/verbalize_final.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -import pynini -from nemo_text_processing.text_normalization.de.verbalizers.verbalize import VerbalizeFst -from nemo_text_processing.text_normalization.en.graph_utils import ( - GraphFst, - delete_extra_space, - delete_space, - generator_main, -) -from nemo_text_processing.text_normalization.en.verbalizers.word import WordFst -from pynini.lib import pynutil - -from nemo.utils import logging - - -class VerbalizeFinalFst(GraphFst): - """ - Finite state transducer that verbalizes an entire sentence - - Args: - deterministic: if True will provide a single transduction option, - for False multiple options (used for audio-based normalization) - cache_dir: path to a dir with .far grammar file. Set to None to avoid using cache. - overwrite_cache: set to True to overwrite .far files - """ - - def __init__(self, deterministic: bool = True, cache_dir: str = None, overwrite_cache: bool = False): - super().__init__(name="verbalize_final", kind="verbalize", deterministic=deterministic) - - far_file = None - if cache_dir is not None and cache_dir != "None": - os.makedirs(cache_dir, exist_ok=True) - far_file = os.path.join(cache_dir, f"de_tn_{deterministic}_deterministic_verbalizer.far") - if not overwrite_cache and far_file and os.path.exists(far_file): - self.fst = pynini.Far(far_file, mode="r")["verbalize"] - logging.info(f'VerbalizeFinalFst graph was restored from {far_file}.') - else: - verbalize = VerbalizeFst(deterministic=deterministic).fst - word = WordFst(deterministic=deterministic).fst - - types = verbalize | word - graph = ( - pynutil.delete("tokens") - + delete_space - + pynutil.delete("{") - + delete_space - + types - + delete_space - + pynutil.delete("}") - ) - graph = delete_space + pynini.closure(graph + delete_extra_space) + graph + delete_space - - self.fst = graph.optimize() - if far_file: - generator_main(far_file, {"verbalize": self.fst}) - logging.info(f"VerbalizeFinalFst grammars are saved to {far_file}.") diff --git a/nemo_text_processing/text_normalization/en/__init__.py b/nemo_text_processing/text_normalization/en/__init__.py deleted file mode 100644 index a9d7d97c9257..000000000000 --- a/nemo_text_processing/text_normalization/en/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from nemo_text_processing.text_normalization.en.taggers.tokenize_and_classify import ClassifyFst -from nemo_text_processing.text_normalization.en.verbalizers.verbalize import VerbalizeFst -from nemo_text_processing.text_normalization.en.verbalizers.verbalize_final import VerbalizeFinalFst diff --git a/nemo_text_processing/text_normalization/en/clean_eval_data.py b/nemo_text_processing/text_normalization/en/clean_eval_data.py deleted file mode 100644 index 8c33c4fa62ca..000000000000 --- a/nemo_text_processing/text_normalization/en/clean_eval_data.py +++ /dev/null @@ -1,342 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from argparse import ArgumentParser -from typing import List - -import regex as re -from nemo_text_processing.text_normalization.data_loader_utils import ( - EOS_TYPE, - Instance, - load_files, - training_data_to_sentences, -) - - -""" -This file is for evaluation purposes. -filter_loaded_data() cleans data (list of instances) for text normalization. Filters and cleaners can be specified for each semiotic class individually. -For example, normalized text should only include characters and whitespace characters but no punctuation. - Cardinal unnormalized instances should contain at least one integer and all other characters are removed. -""" - - -class Filter: - """ - Filter class - - Args: - class_type: semiotic class used in dataset - process_func: function to transform text - filter_func: function to filter text - - """ - - def __init__(self, class_type: str, process_func: object, filter_func: object): - self.class_type = class_type - self.process_func = process_func - self.filter_func = filter_func - - def filter(self, instance: Instance) -> bool: - """ - filter function - - Args: - filters given instance with filter function - - Returns: True if given instance fulfills criteria or does not belong to class type - """ - if instance.token_type != self.class_type: - return True - return self.filter_func(instance) - - def process(self, instance: Instance) -> Instance: - """ - process function - - Args: - processes given instance with process function - - Returns: processed instance if instance belongs to expected class type or original instance - """ - if instance.token_type != self.class_type: - return instance - return self.process_func(instance) - - -def filter_cardinal_1(instance: Instance) -> bool: - ok = re.search(r"[0-9]", instance.un_normalized) - return ok - - -def process_cardinal_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - un_normalized = re.sub(r"[^0-9]", "", un_normalized) - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_ordinal_1(instance: Instance) -> bool: - ok = re.search(r"(st|nd|rd|th)\s*$", instance.un_normalized) - return ok - - -def process_ordinal_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - un_normalized = re.sub(r"[,\s]", "", un_normalized) - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_decimal_1(instance: Instance) -> bool: - ok = re.search(r"[0-9]", instance.un_normalized) - return ok - - -def process_decimal_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - un_normalized = re.sub(r",", "", un_normalized) - normalized = instance.normalized - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_measure_1(instance: Instance) -> bool: - ok = True - return ok - - -def process_measure_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - un_normalized = re.sub(r",", "", un_normalized) - un_normalized = re.sub(r"m2", "m²", un_normalized) - un_normalized = re.sub(r"(\d)([^\d.\s])", r"\1 \2", un_normalized) - normalized = re.sub(r"[^a-z\s]", "", normalized) - normalized = re.sub(r"per ([a-z\s]*)s$", r"per \1", normalized) - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_money_1(instance: Instance) -> bool: - ok = re.search(r"[0-9]", instance.un_normalized) - return ok - - -def process_money_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - un_normalized = re.sub(r",", "", un_normalized) - un_normalized = re.sub(r"a\$", r"$", un_normalized) - un_normalized = re.sub(r"us\$", r"$", un_normalized) - un_normalized = re.sub(r"(\d)m\s*$", r"\1 million", un_normalized) - un_normalized = re.sub(r"(\d)bn?\s*$", r"\1 billion", un_normalized) - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_time_1(instance: Instance) -> bool: - ok = re.search(r"[0-9]", instance.un_normalized) - return ok - - -def process_time_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - un_normalized = re.sub(r": ", ":", un_normalized) - un_normalized = re.sub(r"(\d)\s?a\s?m\s?", r"\1 a.m.", un_normalized) - un_normalized = re.sub(r"(\d)\s?p\s?m\s?", r"\1 p.m.", un_normalized) - normalized = instance.normalized - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_plain_1(instance: Instance) -> bool: - ok = True - return ok - - -def process_plain_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_punct_1(instance: Instance) -> bool: - ok = True - return ok - - -def process_punct_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_date_1(instance: Instance) -> bool: - ok = True - return ok - - -def process_date_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - un_normalized = re.sub(r",", "", un_normalized) - normalized = instance.normalized - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_letters_1(instance: Instance) -> bool: - ok = True - return ok - - -def process_letters_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_verbatim_1(instance: Instance) -> bool: - ok = True - return ok - - -def process_verbatim_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_digit_1(instance: Instance) -> bool: - ok = re.search(r"[0-9]", instance.un_normalized) - return ok - - -def process_digit_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_telephone_1(instance: Instance) -> bool: - ok = re.search(r"[0-9]", instance.un_normalized) - return ok - - -def process_telephone_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_electronic_1(instance: Instance) -> bool: - ok = re.search(r"[0-9]", instance.un_normalized) - return ok - - -def process_electronic_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_fraction_1(instance: Instance) -> bool: - ok = re.search(r"[0-9]", instance.un_normalized) - return ok - - -def process_fraction_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -def filter_address_1(instance: Instance) -> bool: - ok = True - return ok - - -def process_address_1(instance: Instance) -> Instance: - un_normalized = instance.un_normalized - normalized = instance.normalized - normalized = re.sub(r"[^a-z ]", "", normalized) - return Instance(token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized) - - -filters = [] -filters.append(Filter(class_type="CARDINAL", process_func=process_cardinal_1, filter_func=filter_cardinal_1)) -filters.append(Filter(class_type="ORDINAL", process_func=process_ordinal_1, filter_func=filter_ordinal_1)) -filters.append(Filter(class_type="DECIMAL", process_func=process_decimal_1, filter_func=filter_decimal_1)) -filters.append(Filter(class_type="MEASURE", process_func=process_measure_1, filter_func=filter_measure_1)) -filters.append(Filter(class_type="MONEY", process_func=process_money_1, filter_func=filter_money_1)) -filters.append(Filter(class_type="TIME", process_func=process_time_1, filter_func=filter_time_1)) - -filters.append(Filter(class_type="DATE", process_func=process_date_1, filter_func=filter_date_1)) -filters.append(Filter(class_type="PLAIN", process_func=process_plain_1, filter_func=filter_plain_1)) -filters.append(Filter(class_type="PUNCT", process_func=process_punct_1, filter_func=filter_punct_1)) -filters.append(Filter(class_type="LETTERS", process_func=process_letters_1, filter_func=filter_letters_1)) -filters.append(Filter(class_type="VERBATIM", process_func=process_verbatim_1, filter_func=filter_verbatim_1)) -filters.append(Filter(class_type="DIGIT", process_func=process_digit_1, filter_func=filter_digit_1)) -filters.append(Filter(class_type="TELEPHONE", process_func=process_telephone_1, filter_func=filter_telephone_1)) -filters.append(Filter(class_type="ELECTRONIC", process_func=process_electronic_1, filter_func=filter_electronic_1)) -filters.append(Filter(class_type="FRACTION", process_func=process_fraction_1, filter_func=filter_fraction_1)) -filters.append(Filter(class_type="ADDRESS", process_func=process_address_1, filter_func=filter_address_1)) -filters.append(Filter(class_type=EOS_TYPE, process_func=lambda x: x, filter_func=lambda x: True)) - - -def filter_loaded_data(data: List[Instance], verbose: bool = False) -> List[Instance]: - """ - Filters list of instances - - Args: - data: list of instances - - Returns: filtered and transformed list of instances - """ - updates_instances = [] - for instance in data: - updated_instance = False - for fil in filters: - if fil.class_type == instance.token_type and fil.filter(instance): - instance = fil.process(instance) - updated_instance = True - if updated_instance: - if verbose: - print(instance) - updates_instances.append(instance) - return updates_instances - - -def parse_args(): - parser = ArgumentParser() - parser.add_argument("--input", help="input file path", type=str, default='./en_with_types/output-00001-of-00100') - parser.add_argument("--verbose", help="print filtered instances", action='store_true') - return parser.parse_args() - - -if __name__ == "__main__": - args = parse_args() - file_path = args.input - - print("Loading training data: " + file_path) - instance_list = load_files([file_path]) # List of instances - filtered_instance_list = filter_loaded_data(instance_list, args.verbose) - training_data_to_sentences(filtered_instance_list) diff --git a/nemo_text_processing/text_normalization/en/data/__init__.py b/nemo_text_processing/text_normalization/en/data/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/text_normalization/en/data/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/en/data/address/__init__.py b/nemo_text_processing/text_normalization/en/data/address/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/nemo_text_processing/text_normalization/en/data/address/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/en/data/address/address_word.tsv b/nemo_text_processing/text_normalization/en/data/address/address_word.tsv deleted file mode 100644 index 2e9e71615acd..000000000000 --- a/nemo_text_processing/text_normalization/en/data/address/address_word.tsv +++ /dev/null @@ -1,14 +0,0 @@ -st Street -street Street -expy Expressway -fwy Freeway -hwy Highway -dr Drive -ct Court -ave Avenue -av Avenue -cir Circle -blvd Boulevard -alley Alley -way Way -jct Junction \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/en/data/address/state.tsv b/nemo_text_processing/text_normalization/en/data/address/state.tsv deleted file mode 100644 index 921704f76129..000000000000 --- a/nemo_text_processing/text_normalization/en/data/address/state.tsv +++ /dev/null @@ -1,52 +0,0 @@ -Alabama AL -Alaska AK -Arizona AZ -Arkansas AR -California CA -Colorado CO -Connecticut CT -Delaware DE -Florida FL -Georgia GA -Hawaii HI -Idaho ID -Illinois IL -Indiana IN -Indiana IND -Iowa IA -Kansas KS -Kentucky KY -Louisiana LA -Maine ME -Maryland MD -Massachusetts MA -Michigan MI -Minnesota MN -Mississippi MS -Missouri MO -Montana MT -Nebraska NE -Nevada NV -New Hampshire NH -New Jersey NJ -New Mexico NM -New York NY -North Carolina NC -North Dakota ND -Ohio OH -Oklahoma OK -Oregon OR -Pennsylvania PA -Rhode Island RI -South Carolina SC -South Dakota SD -Tennessee TN -Tennessee TENN -Texas TX -Utah UT -Vermont VT -Virginia VA -Washington WA -West Virginia WV -Wisconsin WI -Wyoming WY \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/en/data/date/__init__.py b/nemo_text_processing/text_normalization/en/data/date/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/text_normalization/en/data/date/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/en/data/date/day.tsv b/nemo_text_processing/text_normalization/en/data/date/day.tsv deleted file mode 100644 index ef8dd7af2852..000000000000 --- a/nemo_text_processing/text_normalization/en/data/date/day.tsv +++ /dev/null @@ -1,31 +0,0 @@ -one -two -three -four -five -six -seven -eight -nine -ten -eleven -twelve -thirteen -fourteen -fifteen -sixteen -seventeen -eighteen -nineteen -twenty -twenty one -twenty two -twenty three -twenty four -twenty five -twenty six -twenty seven -twenty eight -twenty nine -thirty -thirty one \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/en/data/date/month_abbr.tsv b/nemo_text_processing/text_normalization/en/data/date/month_abbr.tsv deleted file mode 100644 index e70e41045b3b..000000000000 --- a/nemo_text_processing/text_normalization/en/data/date/month_abbr.tsv +++ /dev/null @@ -1,12 +0,0 @@ -jan january -feb february -mar march -apr april -jun june -jul july -aug august -sep september -sept september -oct october -nov november -dec december diff --git a/nemo_text_processing/text_normalization/en/data/date/month_name.tsv b/nemo_text_processing/text_normalization/en/data/date/month_name.tsv deleted file mode 100644 index 1c05ac02a39e..000000000000 --- a/nemo_text_processing/text_normalization/en/data/date/month_name.tsv +++ /dev/null @@ -1,12 +0,0 @@ -january -february -march -april -may -june -july -august -september -october -november -december diff --git a/nemo_text_processing/text_normalization/en/data/date/month_number.tsv b/nemo_text_processing/text_normalization/en/data/date/month_number.tsv deleted file mode 100644 index f0cacd5d1ed3..000000000000 --- a/nemo_text_processing/text_normalization/en/data/date/month_number.tsv +++ /dev/null @@ -1,24 +0,0 @@ -1 january -2 february -3 march -4 april -5 may -6 june -7 july -8 august -9 september -10 october -11 november -12 december -01 january -02 february -03 march -04 april -05 may -06 june -07 july -08 august -09 september -10 october -11 november -12 december \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/en/data/date/year_suffix.tsv b/nemo_text_processing/text_normalization/en/data/date/year_suffix.tsv deleted file mode 100644 index 8c799ddbcfba..000000000000 --- a/nemo_text_processing/text_normalization/en/data/date/year_suffix.tsv +++ /dev/null @@ -1,16 +0,0 @@ -A. D AD -A.D AD -a. d AD -a.d AD -a. d. AD -a.d. AD -B. C BC -B.C BC -b. c BC -b.c BC -A. D. AD -A.D. AD -B. C. BC -B.C. BC -b. c. BC -b.c. BC diff --git a/nemo_text_processing/text_normalization/en/data/electronic/__init__.py b/nemo_text_processing/text_normalization/en/data/electronic/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/en/data/electronic/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/en/data/electronic/domain.tsv b/nemo_text_processing/text_normalization/en/data/electronic/domain.tsv deleted file mode 100644 index 0e7042c5f77f..000000000000 --- a/nemo_text_processing/text_normalization/en/data/electronic/domain.tsv +++ /dev/null @@ -1,12 +0,0 @@ -.com dot com -.org dot org -.gov dot gov -.uk dot UK -.fr dot FR -.net dot net -.br dot BR -.in dot IN -.ru dot RU -.de dot DE -.it dot IT -.jpg dot jpeg \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/en/data/electronic/symbol.tsv b/nemo_text_processing/text_normalization/en/data/electronic/symbol.tsv deleted file mode 100644 index f633c77cb793..000000000000 --- a/nemo_text_processing/text_normalization/en/data/electronic/symbol.tsv +++ /dev/null @@ -1,21 +0,0 @@ -. dot -- dash -_ underscore -! exclamation mark -# number sign -$ dollar sign -% percent sign -& ampersand -' quote -* asterisk -+ plus -/ slash -= equal sign -? question mark -^ circumflex -` right single quote -{ left brace -| vertical bar -} right brace -~ tilde -, comma \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/en/data/measure/__init__.py b/nemo_text_processing/text_normalization/en/data/measure/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/text_normalization/en/data/measure/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/en/data/measure/math_operation.tsv b/nemo_text_processing/text_normalization/en/data/measure/math_operation.tsv deleted file mode 100644 index 21e72fd0c405..000000000000 --- a/nemo_text_processing/text_normalization/en/data/measure/math_operation.tsv +++ /dev/null @@ -1,8 +0,0 @@ -+ plus -- minus -/ divided -÷ divided -: divided -× times -* times -· times \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/en/data/measure/unit.tsv b/nemo_text_processing/text_normalization/en/data/measure/unit.tsv deleted file mode 100644 index c033ab842385..000000000000 --- a/nemo_text_processing/text_normalization/en/data/measure/unit.tsv +++ /dev/null @@ -1,127 +0,0 @@ -amu atomic mass unit -bar bar -° degree -º degree -°c degree Celsius -°C degree Celsius -ºc degree Celsius -ºC degree Celsius -℃ degree Celsius -cm2 square centimeter -cm² square centimeter -cm3 cubic centimeter -cm³ cubic centimeter -cm centimeter -cwt hundredweight -db decibel -dm3 cubic decimeter -dm³ cubic decimeter -dm decimeter -ds decisecond -°f degree Fahrenheit -°F degree Fahrenheit -℉ degree Fahrenheit -ft foot -ghz gigahertz -gw gigawatt -gwh gigawatt hour -hz hertz -" inch -kbps kilobit per second -kcal kilo calory -kgf kilogram force -kg kilogram -khz kilohertz -km2 square kilometer -km² square kilometer -km3 cubic kilometer -km³ cubic kilometer -km kilometer -kpa kilopascal -kwh kilowatt hour -kw kilowatt -kW kilowatt -lb pound -lbs pound -m2 square meter -m² square meter -m3 cubic meter -m³ cubic meter -mbps megabit per second -mg milligram -mhz megahertz -mi2 square mile -mi² square mile -mi3 cubic mile -mi³ cubic mile -cu mi cubic mile -mi mile -min minute -ml milliliter -mm2 square millimeter -mm² square millimeter -mol mole -mpa megapascal -mph mile per hour -ng nanogram -nm nanometer -ns nanosecond -oz ounce -pa pascal -% percent -rad radian -rpm revolution per minute -sq ft square foot -sq mi square mile -sv sievert -tb terabyte -tj terajoule -tl teraliter -v volt -yd yard -μg microgram -μm micrometer -μs microsecond -ω ohm -atm ATM -au AU -bq BQ -cc CC -cd CD -da DA -eb EB -ev EV -f F -gb GB -g G -gl GL -gpa GPA -gy GY -ha HA -h H -hl HL -hp GP -hs HS -kb KB -kl KL -kn KN -kt KT -kv KV -lm LM -ma MA -mA MA -mb MB -mc MC -mf MF -m M -mm MM -ms MS -mv MV -mw MW -pb PB -pg PG -ps PS -s S -tb TB -tb YB -zb ZB \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/en/data/measure/unit_alternatives.tsv b/nemo_text_processing/text_normalization/en/data/measure/unit_alternatives.tsv deleted file mode 100644 index 77e4057ae70b..000000000000 --- a/nemo_text_processing/text_normalization/en/data/measure/unit_alternatives.tsv +++ /dev/null @@ -1,43 +0,0 @@ -atm atmosphere -bq becquerel -cd candela -da dalton -eb exabyte -f degree Fahrenheit -gb gigabyte -g gram -gl gigaliter -ha hectare -h hour -hl hectoliter -hp horsepower -hp horsepower -kb kilobit -kb kilobyte -ma megaampere -mA megaampere -ma milliampere -mA milliampere -mb megabyte -mc megacoulomb -mf megafarad -m meter -m minute -mm millimeter -mm millimeter -mm millimeter -ms megasecond -ms mega siemens -ms millisecond -mv millivolt -mV millivolt -mw megawatt -mW megawatt -pb petabyte -pg petagram -ps petasecond -s second -tb terabyte -tb terabyte -yb yottabyte -zb zettabyte diff --git a/nemo_text_processing/text_normalization/en/data/money/__init__.py b/nemo_text_processing/text_normalization/en/data/money/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/text_normalization/en/data/money/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/en/data/money/currency_major.tsv b/nemo_text_processing/text_normalization/en/data/money/currency_major.tsv deleted file mode 100644 index 5201efd8a3a2..000000000000 --- a/nemo_text_processing/text_normalization/en/data/money/currency_major.tsv +++ /dev/null @@ -1,39 +0,0 @@ -$ dollar -$ us dollar -US$ us dollar -฿ Thai Baht -£ pound -€ euro -₩ won -nzd new zealand dollar -rs rupee -chf swiss franc -dkk danish kroner -fim finnish markka -aed arab emirates dirham -¥ yen -czk czech koruna -mro mauritanian ouguiya -pkr pakistani rupee -crc costa rican colon -hk$ hong kong dollar -npr nepalese rupee -awg aruban florin -nok norwegian kroner -tzs tanzanian shilling -sek swedish kronor -cyp cypriot pound -r real -sar saudi riyal -cve cape verde escudo -rsd serbian dinar -dm german mark -shp saint helena pounds -php philippine peso -cad canadian dollar -ssp south sudanese pound -scr seychelles rupee -mvr maldivian rufiyaa -DH dirham -Dh dirham -Dhs. dirham diff --git a/nemo_text_processing/text_normalization/en/data/money/currency_minor_plural.tsv b/nemo_text_processing/text_normalization/en/data/money/currency_minor_plural.tsv deleted file mode 100644 index a2f67b3a3d00..000000000000 --- a/nemo_text_processing/text_normalization/en/data/money/currency_minor_plural.tsv +++ /dev/null @@ -1,4 +0,0 @@ -$ cents -US$ cents -€ cents -£ pence \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/en/data/money/currency_minor_singular.tsv b/nemo_text_processing/text_normalization/en/data/money/currency_minor_singular.tsv deleted file mode 100644 index fe629d87b693..000000000000 --- a/nemo_text_processing/text_normalization/en/data/money/currency_minor_singular.tsv +++ /dev/null @@ -1,3 +0,0 @@ -$ cent -€ cent -£ penny \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/en/data/money/per_unit.tsv b/nemo_text_processing/text_normalization/en/data/money/per_unit.tsv deleted file mode 100644 index 654806689127..000000000000 --- a/nemo_text_processing/text_normalization/en/data/money/per_unit.tsv +++ /dev/null @@ -1,2 +0,0 @@ -/ea each -/dozen \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/en/data/number/__init__.py b/nemo_text_processing/text_normalization/en/data/number/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/text_normalization/en/data/number/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/en/data/number/cardinal_number_name.far b/nemo_text_processing/text_normalization/en/data/number/cardinal_number_name.far deleted file mode 100755 index 2ec682541ca72e575663537b1555059536f6876f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 325078 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νgPV%4i|l}uMM5kC0o}Q`d80$5z|OvlU(t4JYtSn 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 E2SNrX6aWAK diff --git a/nemo_text_processing/text_normalization/en/data/number/digit.tsv b/nemo_text_processing/text_normalization/en/data/number/digit.tsv deleted file mode 100644 index fa329799db6f..000000000000 --- a/nemo_text_processing/text_normalization/en/data/number/digit.tsv +++ /dev/null @@ -1,9 +0,0 @@ -one 1 -two 2 -three 3 -four 4 -five 5 -six 6 -seven 7 -eight 8 -nine 9 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/en/data/number/fraction.tsv b/nemo_text_processing/text_normalization/en/data/number/fraction.tsv deleted file mode 100644 index 0085ad53ffaa..000000000000 --- a/nemo_text_processing/text_normalization/en/data/number/fraction.tsv +++ /dev/null @@ -1,18 +0,0 @@ -¼ 1/4 -½ 1/2 -¾ 3/4 -⅐ 1/7 -⅑ 1/9 -⅒ 1/10 -⅓ 1/3 -⅔ 2/3 -⅕ 1/5 -⅖ 2/5 -⅗ 3/5 -⅘ 4/5 -⅙ 1/6 -⅚ 5/6 -⅛ 1/8 -⅜ 3/8 -⅝ 5/8 -⅞ 7/8 diff --git a/nemo_text_processing/text_normalization/en/data/number/hundred.tsv b/nemo_text_processing/text_normalization/en/data/number/hundred.tsv deleted file mode 100644 index 10e2b9645a53..000000000000 --- a/nemo_text_processing/text_normalization/en/data/number/hundred.tsv +++ /dev/null @@ -1 +0,0 @@ -hundred \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/en/data/number/quantity_abbr.tsv b/nemo_text_processing/text_normalization/en/data/number/quantity_abbr.tsv deleted file mode 100644 index 7dba4f03713c..000000000000 --- a/nemo_text_processing/text_normalization/en/data/number/quantity_abbr.tsv +++ /dev/null @@ -1,10 +0,0 @@ -M million -MLN million -m million -mln million -B billion -b billion -BN billion -bn billion -K thousand -k thousand \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/en/data/number/teen.tsv b/nemo_text_processing/text_normalization/en/data/number/teen.tsv deleted file mode 100644 index 8e60fa1d3cb3..000000000000 --- a/nemo_text_processing/text_normalization/en/data/number/teen.tsv +++ /dev/null @@ -1,10 +0,0 @@ -ten 10 -eleven 11 -twelve 12 -thirteen 13 -fourteen 14 -fifteen 15 -sixteen 16 -seventeen 17 -eighteen 18 -nineteen 19 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/en/data/number/thousand.tsv b/nemo_text_processing/text_normalization/en/data/number/thousand.tsv deleted file mode 100644 index bf30542bb6bc..000000000000 --- a/nemo_text_processing/text_normalization/en/data/number/thousand.tsv +++ /dev/null @@ -1,22 +0,0 @@ -thousand -million -billion -trillion -quadrillion -quintillion -sextillion -septillion -octillion -nonillion -decillion -undecillion -duodecillion -tredecillion -quattuordecillion -quindecillion -sexdecillion -septendecillion -octodecillion -novemdecillion -vigintillion -centillion \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/en/data/number/ty.tsv b/nemo_text_processing/text_normalization/en/data/number/ty.tsv deleted file mode 100644 index 65f9839fda6d..000000000000 --- a/nemo_text_processing/text_normalization/en/data/number/ty.tsv +++ /dev/null @@ -1,8 +0,0 @@ -twenty 2 -thirty 3 -forty 4 -fifty 5 -sixty 6 -seventy 7 -eighty 8 -ninety 9 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/en/data/number/zero.tsv b/nemo_text_processing/text_normalization/en/data/number/zero.tsv deleted file mode 100644 index a1b116c10f09..000000000000 --- a/nemo_text_processing/text_normalization/en/data/number/zero.tsv +++ /dev/null @@ -1 +0,0 @@ -zero 0 diff --git a/nemo_text_processing/text_normalization/en/data/ordinal/__init__.py b/nemo_text_processing/text_normalization/en/data/ordinal/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/text_normalization/en/data/ordinal/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/en/data/ordinal/digit.tsv b/nemo_text_processing/text_normalization/en/data/ordinal/digit.tsv deleted file mode 100644 index 4b7fc24f1606..000000000000 --- a/nemo_text_processing/text_normalization/en/data/ordinal/digit.tsv +++ /dev/null @@ -1,9 +0,0 @@ -first one -second two -third three -fourth four -fifth five -sixth sixth -seventh seven -eighth eight -ninth nine \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/en/data/ordinal/teen.tsv b/nemo_text_processing/text_normalization/en/data/ordinal/teen.tsv deleted file mode 100644 index 496fefc086d1..000000000000 --- a/nemo_text_processing/text_normalization/en/data/ordinal/teen.tsv +++ /dev/null @@ -1 +0,0 @@ -twelfth twelve \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/en/data/roman/README.md b/nemo_text_processing/text_normalization/en/data/roman/README.md deleted file mode 100644 index 398e1974f823..000000000000 --- a/nemo_text_processing/text_normalization/en/data/roman/README.md +++ /dev/null @@ -1,20 +0,0 @@ -`female.tsv` - List of common female names. Copyright (c) January 1991 by Mark Kantrowitz, 4987 names, Version 1.3 (29-MAR-94) -Source: [https://www.cs.cmu.edu/Groups/AI/areas/nlp/corpora/names/female.txt](https://www.cs.cmu.edu/Groups/AI/areas/nlp/corpora/names/female.txt) - -`male.tsv` - List of common male names. Copyright (c) January 1991 by Mark Kantrowitz, 2940 names, Version 1.3 (29-MAR-94) -Source: [https://www.cs.cmu.edu/Groups/AI/areas/nlp/corpora/names/male.txt](https://www.cs.cmu.edu/Groups/AI/areas/nlp/corpora/names/male.txt) - -[Corpora Readme.txt](https://www.cs.cmu.edu/Groups/AI/areas/nlp/corpora/names/readme.txt): - -You may use the lists of names for any purpose, so long as credit is given -in any published work. You may also redistribute the list if you -provide the recipients with a copy of this README file. The lists are -not in the public domain (I retain the copyright on the lists) but are -freely redistributable. - -If you have any additions to the lists of names, I would appreciate -receiving them. - -My email address is mkant+@cs.cmu.edu. - -Mark Kantrowitz \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/en/data/roman/__init__.py b/nemo_text_processing/text_normalization/en/data/roman/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/text_normalization/en/data/roman/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/en/data/roman/female.tsv b/nemo_text_processing/text_normalization/en/data/roman/female.tsv deleted file mode 100644 index 4e248ce2ab7c..000000000000 --- a/nemo_text_processing/text_normalization/en/data/roman/female.tsv +++ /dev/null @@ -1,4998 +0,0 @@ -Abagael -Abagail -Abbe -Abbey -Abbi -Abbie -Abby -Abigael -Abigail -Abigale -Abra -Acacia -Ada -Adah -Adaline -Adara -Addie -Addis -Adel -Adela -Adelaide -Adele -Adelice -Adelina -Adelind -Adeline -Adella -Adelle -Adena -Adey -Adi -Adiana -Adina -Adora -Adore -Adoree -Adorne -Adrea -Adria -Adriaens -Adrian -Adriana -Adriane -Adrianna -Adrianne -Adrien -Adriena -Adrienne -Aeriel -Aeriela -Aeriell -Ag -Agace -Agata -Agatha -Agathe -Aggi -Aggie -Aggy -Agna -Agnella -Agnes -Agnese -Agnesse -Agneta -Agnola -Agretha -Aida -Aidan -Aigneis -Aila -Aile -Ailee -Aileen -Ailene -Ailey -Aili -Ailina -Ailyn -Aime -Aimee -Aimil -Aina -Aindrea -Ainslee -Ainsley -Ainslie -Ajay -Alaine -Alameda -Alana -Alanah -Alane -Alanna -Alayne -Alberta -Albertina -Albertine -Albina -Alecia -Aleda -Aleece -Aleecia -Aleen -Alejandra -Alejandrina -Alena -Alene -Alessandra -Aleta -Alethea -Alex -Alexa -Alexandra -Alexandrina -Alexi -Alexia -Alexina -Alexine -Alexis -Alfie -Alfreda -Ali -Alia -Alica -Alice -Alicea -Alicia -Alida -Alidia -Alina -Aline -Alis -Alisa -Alisha -Alison -Alissa -Alisun -Alix -Aliza -Alla -Alleen -Allegra -Allene -Alli -Allianora -Allie -Allina -Allis -Allison -Allissa -Allsun -Ally -Allyce -Allyn -Allys -Allyson -Alma -Almeda -Almeria -Almeta -Almira -Almire -Aloise -Aloisia -Aloysia -Alpa -Alta -Althea -Alvera -Alvina -Alvinia -Alvira -Alyce -Alyda -Alys -Alysa -Alyse -Alysia -Alyson -Alyss -Alyssa -Amabel -Amabelle -Amalea -Amalee -Amaleta -Amalia -Amalie -Amalita -Amalle -Amanda -Amandi -Amandie -Amandy -Amara -Amargo -Amata -Amber -Amberly -Ambrosia -Ambur -Ame -Amelia -Amelie -Amelina -Ameline -Amelita -Ami -Amie -Amity -Ammamaria -Amy -Ana -Anabel -Anabella -Anabelle -Anais -Analiese -Analise -Anallese -Anallise -Anastasia -Anastasie -Anastassia -Anatola -Andee -Andi -Andie -Andra -Andrea -Andreana -Andree -Andrei -Andria -Andriana -Andriette -Andromache -Andromeda -Andy -Anestassia -Anet -Anett -Anetta -Anette -Ange -Angel -Angela -Angele -Angelia -Angelica -Angelika -Angelina -Angeline -Angelique -Angelita -Angelle -Angie -Angil -Angy -Ania -Anica -Anissa -Anita -Anitra -Anja -Anjanette -Anjela -Ann -Ann-Mari -Ann-Marie -Anna -Anna-Diana -Anna-Diane -Anna-Maria -Annabal -Annabel -Annabela -Annabell -Annabella -Annabelle -Annadiana -Annadiane -Annalee -Annalena -Annaliese -Annalisa -Annalise -Annalyse -Annamari -Annamaria -Annamarie -Anne -Anne-Corinne -Anne-Mar -Anne-Marie -Annecorinne -Anneliese -Annelise -Annemarie -Annetta -Annette -Anni -Annice -Annie -Annissa -Annmaria -Annmarie -Annnora -Annora -Anny -Anselma -Ansley -Anstice -Anthe -Anthea -Anthia -Antoinette -Antonella -Antonetta -Antonia -Antonie -Antonietta -Antonina -Anya -Aphrodite -Appolonia -Aprilette -Ara -Arabel -Arabela -Arabele -Arabella -Arabelle -Arda -Ardath -Ardeen -Ardelia -Ardelis -Ardella -Ardelle -Arden -Ardene -Ardenia -Ardine -Ardis -Ardith -Ardra -Ardyce -Ardys -Ardyth -Aretha -Ariadne -Ariana -Arianne -Aridatha -Ariel -Ariela -Ariella -Arielle -Arlana -Arlee -Arleen -Arlen -Arlena -Arlene -Arleta -Arlette -Arleyne -Arlie -Arliene -Arlina -Arlinda -Arline -Arly -Arlyn -Arlyne -Aryn -Ashely -Ashlee -Ashleigh -Ashlen -Ashley -Ashli -Ashlie -Ashly -Asia -Astra -Astrid -Astrix -Atalanta -Athena -Athene -Atlanta -Atlante -Auberta -Aubine -Aubree -Aubrette -Aubrey -Aubrie -Aubry -Audi -Audie -Audra -Audre -Audrey -Audrie -Audry -Audrye -Audy -Augusta -Auguste -Augustina -Augustine -Aura -Aurea -Aurel -Aurelea -Aurelia -Aurelie -Auria -Aurie -Aurilia -Aurlie -Auroora -Aurora -Aurore -Austin -Austina -Austine -Ava -Aveline -Averil -Averyl -Avie -Avis -Aviva -Avivah -Avril -Avrit -Ayn -Bab -Babara -Babette -Babita -Babs -Bambi -Bambie -Bamby -Barb -Barbabra -Barbara -Barbara-Anne -Barbaraanne -Barbe -Barbee -Barbette -Barbey -Barbi -Barbie -Barbra -Barby -Bari -Barrie -Barry -Basia -Bathsheba -Batsheva -Bea -Beatrice -Beatrisa -Beatrix -Beatriz -Beau -Bebe -Becca -Becka -Becki -Beckie -Becky -Bee -Beilul -Beitris -Bekki -Bel -Belia -Belicia -Belinda -Belita -Bell -Bella -Bellamy -Bellanca -Belle -Bellina -Belva -Belvia -Bendite -Benedetta -Benedicta -Benedikta -Benetta -Benita -Benni -Bennie -Benny -Benoite -Berenice -Beret -Berget -Berna -Bernadene -Bernadette -Bernadina -Bernadine -Bernardina -Bernardine -Bernelle -Bernete -Bernetta -Bernette -Berni -Bernice -Bernie -Bernita -Berny -Berri -Berrie -Berry -Bert -Berta -Berte -Bertha -Berthe -Berti -Bertie -Bertina -Bertine -Berty -Beryl -Beryle -Bess -Bessie -Bessy -Beth -Bethanne -Bethany -Bethena -Bethina -Betsey -Betsy -Betta -Bette -Bette-Ann -Betteann -Betteanne -Betti -Bettie -Bettina -Bettine -Betty -Bettye -Beulah -Bev -Beverie -Beverlee -Beverlie -Beverly -Bevvy -Bianca -Bianka -Biddy -Bidget -Bill -Billi -Billie -Billy -Binni -Binnie -Binny -Bird -Birdie -Birgit -Birgitta -Blair -Blaire -Blake -Blakelee -Blakeley -Blanca -Blanch -Blancha -Blanche -Blinni -Blinnie -Blinny -Bliss -Blisse -Blithe -Blondell -Blondelle -Blondie -Blondy -Blythe -Bo -Bobbette -Bobbi -Bobbie -Bobby -Bobette -Bobina -Bobine -Bobinette -Bonita -Bonnee -Bonni -Bonnie -Bonny -Brana -Brandais -Brande -Brandea -Brandi -Brandice -Brandie -Brandise -Brandy -Brea -Breanne -Brear -Bree -Breena -Bren -Brena -Brenda -Brenn -Brenna -Brett -Bria -Briana -Brianna -Brianne -Bride -Bridget -Bridgett -Bridgette -Bridie -Brier -Brietta -Brigid -Brigida -Brigit -Brigitta -Brigitte -Brina -Briney -Briny -Brit -Brita -Britaney -Britani -Briteny -Britney -Britni -Britt -Britta -Brittan -Brittany -Britte -Brittney -Brook -Brooke -Brooks -Brunella -Brunhilda -Brunhilde -Bryana -Bryn -Bryna -Brynn -Brynna -Brynne -Buffy -Bunni -Bunnie -Bunny -Burta -Cabrina -Cacilia -Cacilie -Caitlin -Caitrin -Cal -Calida -Calla -Calley -Calli -Callida -Callie -Cally -Calypso -Cam -Camala -Camel -Camella -Camellia -Cameo -Cami -Camila -Camile -Camilla -Camille -Cammi -Cammie -Cammy -Canada -Candace -Candi -Candice -Candida -Candide -Candie -Candis -Candra -Candy -Cappella -Caprice -Cara -Caralie -Caren -Carena -Caresa -Caressa -Caresse -Carey -Cari -Caria -Carie -Caril -Carilyn -Carin -Carina -Carine -Cariotta -Carissa -Carita -Caritta -Carla -Carlee -Carleen -Carlen -Carlena -Carlene -Carley -Carli -Carlie -Carlin -Carlina -Carline -Carlisle -Carlita -Carlota -Carlotta -Carly -Carlye -Carlyn -Carlynn -Carlynne -Carma -Carmel -Carmela -Carmelia -Carmelina -Carmelita -Carmella -Carmelle -Carmen -Carmina -Carmine -Carmita -Carmon -Caro -Carol -Carol-Jean -Carola -Carolan -Carolann -Carole -Carolee -Caroleen -Carolie -Carolin -Carolina -Caroline -Caroljean -Carolyn -Carolyne -Carolynn -Caron -Carree -Carri -Carrie -Carrissa -Carrol -Carroll -Carry -Cary -Caryl -Caryn -Casandra -Casey -Casi -Casia -Casie -Cass -Cassandra -Cassandre -Cassandry -Cassaundra -Cassey -Cassi -Cassie -Cassondra -Cassy -Cat -Catarina -Cate -Caterina -Catha -Catharina -Catharine -Cathe -Cathee -Catherin -Catherina -Catherine -Cathi -Cathie -Cathleen -Cathlene -Cathrin -Cathrine -Cathryn -Cathy -Cathyleen -Cati -Catie -Catina -Catlaina -Catlee -Catlin -Catrina -Catriona -Caty -Cayla -Cecelia -Cecil -Cecile -Ceciley -Cecilia -Cecilla -Cecily -Ceil -Cele -Celene -Celesta -Celeste -Celestia -Celestina -Celestine -Celestyn -Celestyna -Celia -Celie -Celina -Celinda -Celine -Celinka -Celisse -Celle -Cesya -Chad -Chanda -Chandal -Chandra -Channa -Chantal -Chantalle -Charil -Charin -Charis -Charissa -Charisse -Charita -Charity -Charla -Charlean -Charleen -Charlena -Charlene -Charline -Charlot -Charlott -Charlotta -Charlotte -Charmain -Charmaine -Charmane -Charmian -Charmine -Charmion -Charo -Charyl -Chastity -Chelsae -Chelsea -Chelsey -Chelsie -Chelsy -Cher -Chere -Cherey -Cheri -Cherianne -Cherice -Cherida -Cherie -Cherilyn -Cherilynn -Cherin -Cherise -Cherish -Cherlyn -Cherri -Cherrita -Cherry -Chery -Cherye -Cheryl -Cheslie -Chiarra -Chickie -Chicky -Chiquita -Chloe -Chloette -Chloris -Chris -Chriss -Chrissa -Chrissie -Chrissy -Christa -Christabel -Christabella -Christabelle -Christal -Christalle -Christan -Christean -Christel -Christen -Christi -Christian -Christiana -Christiane -Christie -Christin -Christina -Christine -Christy -Christyna -Chrysa -Chrysler -Chrystal -Chryste -Chrystel -Ciara -Cicely -Cicily -Ciel -Cilka -Cinda -Cindee -Cindelyn -Cinderella -Cindi -Cindie -Cindra -Cindy -Cinnamon -Cissie -Cissy -Clair -Claire -Clara -Clarabelle -Clare -Claresta -Clareta -Claretta -Clarette -Clarey -Clari -Claribel -Clarice -Clarie -Clarinda -Clarine -Clarisa -Clarissa -Clarisse -Clarita -Clary -Claude -Claudelle -Claudetta -Claudette -Claudia -Claudie -Claudina -Claudine -Clea -Clem -Clemence -Clementia -Clementina -Clementine -Clemmie -Clemmy -Cleo -Cleopatra -Clerissa -Cleva -Clio -Clo -Cloe -Cloris -Clotilda -Clovis -Codee -Codi -Codie -Cody -Coleen -Colene -Coletta -Colette -Colleen -Collete -Collette -Collie -Colline -Colly -Con -Concettina -Conchita -Concordia -Conney -Conni -Connie -Conny -Consolata -Constance -Constancia -Constancy -Constanta -Constantia -Constantina -Constantine -Consuela -Consuelo -Cookie -Cora -Corabel -Corabella -Corabelle -Coral -Coralie -Coraline -Coralyn -Cordelia -Cordelie -Cordey -Cordie -Cordula -Cordy -Coreen -Corella -Corena -Corenda -Corene -Coretta -Corette -Corey -Cori -Corie -Corilla -Corina -Corine -Corinna -Corinne -Coriss -Corissa -Corliss -Corly -Cornela -Cornelia -Cornelle -Cornie -Corny -Correna -Correy -Corri -Corrianne -Corrie -Corrina -Corrine -Corrinne -Corry -Cortney -Cory -Cosetta -Cosette -Courtenay -Courtney -Cresa -Cris -Crissie -Crissy -Crista -Cristabel -Cristal -Cristen -Cristi -Cristie -Cristin -Cristina -Cristine -Cristionna -Cristy -Crysta -Crystal -Crystie -Cyb -Cybal -Cybel -Cybelle -Cybil -Cybill -Cyndi -Cyndy -Cynthea -Cynthia -Cynthie -Cynthy -Dacey -Dacia -Dacie -Dacy -Dael -Daffi -Daffie -Daffy -Dafna -Dagmar -Dahlia -Daile -Daisey -Daisi -Daisie -Daisy -Dale -Dalenna -Dalia -Dalila -Dallas -Daloris -Damara -Damaris -Damita -Dana -Danell -Danella -Danelle -Danette -Dani -Dania -Danica -Danice -Daniel -Daniela -Daniele -Daniella -Danielle -Danika -Danila -Danit -Danita -Danna -Danni -Dannie -Danny -Dannye -Danya -Danyelle -Danyette -Daphene -Daphna -Daphne -Dara -Darb -Darbie -Darby -Darcee -Darcey -Darci -Darcie -Darcy -Darda -Dareen -Darell -Darelle -Dari -Daria -Darice -Darla -Darleen -Darlene -Darline -Darryl -Darsey -Darsie -Darya -Daryl -Daryn -Dasha -Dasi -Dasie -Dasya -Datha -Daune -Daveen -Daveta -Davida -Davina -Davine -Davita -Dawn -Dawna -Dayle -Dayna -Dea -Deana -Deane -Deanna -Deanne -Deb -Debbi -Debbie -Debbra -Debby -Debee -Debera -Debi -Debor -Debora -Deborah -Debra -Dede -Dedie -Dedra -Dee -Dee Dee -Deeann -Deeanne -Deedee -Deena -Deerdre -Dehlia -Deidre -Deina -Deirdre -Del -Dela -Delaney -Delcina -Delcine -Delia -Delila -Delilah -Delinda -Dell -Della -Delly -Delora -Delores -Deloria -Deloris -Delphina -Delphine -Delphinia -Demeter -Demetra -Demetria -Demetris -Dena -Deni -Denice -Denise -Denna -Denni -Dennie -Denny -Deny -Denys -Denyse -Deonne -Desaree -Desdemona -Desirae -Desiree -Desiri -Deva -Devan -Devi -Devin -Devina -Devinne -Devon -Devondra -Devonna -Devonne -Devora -Dew -Di -Diahann -Diamond -Dian -Diana -Diandra -Diane -Diane-Marie -Dianemarie -Diann -Dianna -Dianne -Diannne -Didi -Dido -Diena -Dierdre -Dina -Dinah -Dinnie -Dinny -Dion -Dione -Dionis -Dionne -Dita -Dix -Dixie -Dode -Dodi -Dodie -Dody -Doe -Doll -Dolley -Dolli -Dollie -Dolly -Dolora -Dolores -Dolorita -Doloritas -Dominica -Dominique -Dona -Donella -Donelle -Donetta -Donia -Donica -Donielle -Donna -Donnajean -Donnamarie -Donni -Donnie -Donny -Dora -Doralia -Doralin -Doralyn -Doralynn -Doralynne -Dorcas -Dore -Doreen -Dorelia -Dorella -Dorelle -Dorena -Dorene -Doretta -Dorette -Dorey -Dori -Doria -Dorian -Dorice -Dorie -Dorine -Doris -Dorisa -Dorise -Dorit -Dorita -Doro -Dorolice -Dorolisa -Dorotea -Doroteya -Dorothea -Dorothee -Dorothy -Dorree -Dorri -Dorrie -Dorris -Dorry -Dorthea -Dorthy -Dory -Dosi -Dot -Doti -Dotti -Dottie -Dotty -Dove -Drea -Drew -Dulce -Dulcea -Dulci -Dulcia -Dulciana -Dulcie -Dulcine -Dulcinea -Dulcy -Dulsea -Dusty -Dyan -Dyana -Dyane -Dyann -Dyanna -Dyanne -Dyna -Dynah -E'Lane -Eada -Eadie -Eadith -Ealasaid -Eartha -Easter -Eba -Ebba -Ebonee -Ebony -Eda -Eddi -Eddie -Eddy -Ede -Edee -Edeline -Eden -Edi -Edie -Edin -Edita -Edith -Editha -Edithe -Ediva -Edna -Edwina -Edy -Edyth -Edythe -Effie -Eileen -Eilis -Eimile -Eirena -Ekaterina -Elaina -Elaine -Elana -Elane -Elayne -Elberta -Elbertina -Elbertine -Eleanor -Eleanora -Eleanore -Electra -Elena -Elene -Eleni -Elenore -Eleonora -Eleonore -Elfie -Elfreda -Elfrida -Elfrieda -Elga -Elianora -Elianore -Elicia -Elie -Elinor -Elinore -Elisa -Elisabet -Elisabeth -Elisabetta -Elise -Elisha -Elissa -Elita -Eliza -Elizabet -Elizabeth -Elka -Elke -Ella -Elladine -Elle -Ellen -Ellene -Ellette -Elli -Ellie -Ellissa -Elly -Ellyn -Ellynn -Elmira -Elna -Elnora -Elnore -Eloisa -Eloise -Elonore -Elora -Elsa -Elsbeth -Else -Elsey -Elsi -Elsie -Elsinore -Elspeth -Elsy -Elva -Elvera -Elvina -Elvira -Elwina -Elwira -Elyn -Elyse -Elysee -Elysha -Elysia -Elyssa -Em -Ema -Emalee -Emalia -Emanuela -Emelda -Emelia -Emelina -Emeline -Emelita -Emelyne -Emera -Emilee -Emili -Emilia -Emilie -Emiline -Emily -Emlyn -Emlynn -Emlynne -Emma -Emmalee -Emmaline -Emmalyn -Emmalynn -Emmalynne -Emmeline -Emmey -Emmi -Emmie -Emmy -Emmye -Emogene -Emyle -Emylee -Endora -Engracia -Enid -Enrica -Enrichetta -Enrika -Enriqueta -Enya -Eolanda -Eolande -Eran -Erda -Erena -Erica -Ericha -Ericka -Erika -Erin -Erina -Erinn -Erinna -Erma -Ermengarde -Ermentrude -Ermina -Erminia -Erminie -Erna -Ernaline -Ernesta -Ernestine -Ertha -Eryn -Esma -Esmaria -Esme -Esmeralda -Esmerelda -Essa -Essie -Essy -Esta -Estel -Estele -Estell -Estella -Estelle -Ester -Esther -Estrella -Estrellita -Ethel -Ethelda -Ethelin -Ethelind -Etheline -Ethelyn -Ethyl -Etta -Etti -Ettie -Etty -Eudora -Eugenia -Eugenie -Eugine -Eula -Eulalie -Eunice -Euphemia -Eustacia -Eva -Evaleen -Evangelia -Evangelin -Evangelina -Evangeline -Evania -Evanne -Eve -Eveleen -Evelina -Eveline -Evelyn -Evette -Evey -Evie -Evita -Evonne -Evvie -Evvy -Evy -Eyde -Eydie -Fabrianne -Fabrice -Fae -Faina -Faith -Fallon -Fan -Fanchette -Fanchon -Fancie -Fancy -Fanechka -Fania -Fanni -Fannie -Fanny -Fanya -Fara -Farah -Farand -Farica -Farra -Farrah -Farrand -Fatima -Faun -Faunie -Faustina -Faustine -Fawn -Fawna -Fawne -Fawnia -Fay -Faydra -Faye -Fayette -Fayina -Fayre -Fayth -Faythe -Federica -Fedora -Felecia -Felicdad -Felice -Felicia -Felicity -Felicle -Felipa -Felisha -Felita -Feliza -Fenelia -Feodora -Ferdinanda -Ferdinande -Fern -Fernanda -Fernande -Fernandina -Ferne -Fey -Fiann -Fianna -Fidela -Fidelia -Fidelity -Fifi -Fifine -Filia -Filide -Filippa -Fina -Fiona -Fionna -Fionnula -Fiorenze -Fleur -Fleurette -Flo -Flor -Flora -Florance -Flore -Florella -Florence -Florencia -Florentia -Florenza -Florette -Flori -Floria -Florice -Florida -Florie -Florina -Florinda -Floris -Florri -Florrie -Florry -Flory -Flossi -Flossie -Flossy -Flower -Fortuna -Fortune -Fran -France -Francene -Frances -Francesca -Francesmary -Francine -Francis -Francisca -Franciska -Francoise -Francyne -Frank -Frankie -Franky -Franni -Frannie -Franny -Frayda -Fred -Freda -Freddi -Freddie -Freddy -Fredelia -Frederica -Fredericka -Fredi -Fredia -Fredra -Fredrika -Freida -Frieda -Friederike -Fulvia -Gabbey -Gabbi -Gabbie -Gabey -Gabi -Gabie -Gabriel -Gabriela -Gabriell -Gabriella -Gabrielle -Gabriellia -Gabrila -Gaby -Gae -Gael -Gail -Gale -Gale -Galina -Garland -Garnet -Garnette -Gates -Gavra -Gavrielle -Gay -Gayla -Gayle -Gayleen -Gaylene -Gaynor -Gelya -Gen -Gena -Gene -Geneva -Genevieve -Genevra -Genia -Genna -Genni -Gennie -Gennifer -Genny -Genovera -Genvieve -George -Georgeanna -Georgeanne -Georgena -Georgeta -Georgetta -Georgette -Georgia -Georgiamay -Georgiana -Georgianna -Georgianne -Georgie -Georgina -Georgine -Gera -Geralda -Geraldina -Geraldine -Gerda -Gerhardine -Geri -Gerianna -Gerianne -Gerladina -Germain -Germaine -Germana -Gerri -Gerrie -Gerrilee -Gerry -Gert -Gerta -Gerti -Gertie -Gertrud -Gertruda -Gertrude -Gertrudis -Gerty -Giacinta -Giana -Gianina -Gianna -Gigi -Gilberta -Gilberte -Gilbertina -Gilbertine -Gilda -Gill -Gillan -Gilli -Gillian -Gillie -Gilligan -Gilly -Gina -Ginelle -Ginevra -Ginger -Ginni -Ginnie -Ginnifer -Ginny -Giorgia -Giovanna -Gipsy -Giralda -Gisela -Gisele -Gisella -Giselle -Gizela -Glad -Gladi -Gladis -Gladys -Gleda -Glen -Glenda -Glenine -Glenn -Glenna -Glennie -Glennis -Glori -Gloria -Gloriana -Gloriane -Glorianna -Glory -Glyn -Glynda -Glynis -Glynnis -Godiva -Golda -Goldarina -Goldi -Goldia -Goldie -Goldina -Goldy -Grace -Gracia -Gracie -Grata -Gratia -Gratiana -Gray -Grayce -Grazia -Gredel -Greer -Greta -Gretal -Gretchen -Grete -Gretel -Grethel -Gretna -Gretta -Grier -Griselda -Grissel -Guendolen -Guenevere -Guenna -Guglielma -Gui -Guillema -Guillemette -Guinevere -Guinna -Gunilla -Gunvor -Gus -Gusella -Gussi -Gussie -Gussy -Gusta -Gusti -Gustie -Gusty -Gwen -Gwendolen -Gwendolin -Gwendolyn -Gweneth -Gwenette -Gwenn -Gwenneth -Gwenni -Gwennie -Gwenny -Gwenora -Gwenore -Gwyn -Gwyneth -Gwynne -Gypsy -Hadria -Hailee -Haily -Haleigh -Halette -Haley -Hali -Halie -Halimeda -Halley -Halli -Hallie -Hally -Hana -Hanna -Hannah -Hanni -Hannibal -Hannie -Hannis -Hanny -Happy -Harlene -Harley -Harli -Harlie -Harmonia -Harmonie -Harmony -Harri -Harrie -Harriet -Harriett -Harrietta -Harriette -Harriot -Harriott -Hatti -Hattie -Hatty -Havivah -Hayley -Hazel -Heath -Heather -Heda -Hedda -Heddi -Heddie -Hedi -Hedvig -Hedwig -Hedy -Heida -Heide -Heidi -Heidie -Helaina -Helaine -Helen -Helen-Elizabeth -Helena -Helene -Helga -Helge -Helise -Hellene -Helli -Heloise -Helsa -Helyn -Hendrika -Henka -Henrie -Henrieta -Henrietta -Henriette -Henryetta -Hephzibah -Hermia -Hermina -Hermine -Herminia -Hermione -Herta -Hertha -Hester -Hesther -Hestia -Hetti -Hettie -Hetty -Hilarie -Hilary -Hilda -Hildagard -Hildagarde -Hilde -Hildegaard -Hildegarde -Hildy -Hillary -Hilliary -Hinda -Holley -Holli -Hollie -Holly -Holly-Anne -Hollyanne -Honey -Honor -Honoria -Hope -Horatia -Hortense -Hortensia -Hulda -Hyacinth -Hyacintha -Hyacinthe -Hyacinthia -Hyacinthie -Hynda -Ianthe -Ibbie -Ibby -Ida -Idalia -Idalina -Idaline -Idell -Idelle -Idette -Ike -Ikey -Ilana -Ileana -Ileane -Ilene -Ilise -Ilka -Illa -Ilona -Ilsa -Ilse -Ilysa -Ilyse -Ilyssa -Imelda -Imogen -Imogene -Imojean -Ina -Inci -Indira -Ines -Inesita -Inessa -Inez -Inga -Ingaberg -Ingaborg -Inge -Ingeberg -Ingeborg -Inger -Ingrid -Ingunna -Inna -Ioana -Iolande -Iolanthe -Iona -Iormina -Ira -Irena -Irene -Irina -Iris -Irita -Irma -Isa -Isabeau -Isabel -Isabelita -Isabella -Isabelle -Isador -Isadora -Isadore -Isahella -Iseabal -Isidora -Isis -Isobel -Issi -Issie -Issy -Ivett -Ivette -Ivie -Ivonne -Ivory -Ivy -Izabel -Izzi -Jacenta -Jacinda -Jacinta -Jacintha -Jacinthe -Jackelyn -Jacki -Jackie -Jacklin -Jacklyn -Jackquelin -Jackqueline -Jacky -Jaclin -Jaclyn -Jacquelin -Jacqueline -Jacquelyn -Jacquelynn -Jacquenetta -Jacquenette -Jacquetta -Jacquette -Jacqui -Jacquie -Jacynth -Jada -Jade -Jaime -Jaimie -Jaine -Jaleh -Jami -Jamie -Jamima -Jammie -Jan -Jana -Janaya -Janaye -Jandy -Jane -Janean -Janeczka -Janeen -Janel -Janela -Janella -Janelle -Janene -Janenna -Janessa -Janet -Janeta -Janetta -Janette -Janeva -Janey -Jania -Janice -Janie -Janifer -Janina -Janine -Janis -Janith -Janka -Janna -Jannel -Jannelle -Janot -Jany -Jaquelin -Jaquelyn -Jaquenetta -Jaquenette -Jaquith -Jasmin -Jasmina -Jasmine -Jayme -Jaymee -Jayne -Jaynell -Jazmin -Jean -Jeana -Jeane -Jeanelle -Jeanette -Jeanie -Jeanine -Jeanna -Jeanne -Jeannette -Jeannie -Jeannine -Jehanna -Jelene -Jemie -Jemima -Jemimah -Jemmie -Jemmy -Jen -Jena -Jenda -Jenelle -Jenette -Jeni -Jenica -Jeniece -Jenifer -Jeniffer -Jenilee -Jenine -Jenn -Jenna -Jennee -Jennette -Jenni -Jennica -Jennie -Jennifer -Jennilee -Jennine -Jenny -Jeraldine -Jeralee -Jere -Jeri -Jermaine -Jerrie -Jerrilee -Jerrilyn -Jerrine -Jerry -Jerrylee -Jess -Jessa -Jessalin -Jessalyn -Jessamine -Jessamyn -Jesse -Jesselyn -Jessi -Jessica -Jessie -Jessika -Jessy -Jewel -Jewell -Jewelle -Jill -Jillana -Jillane -Jillayne -Jilleen -Jillene -Jilli -Jillian -Jillie -Jilly -Jinny -Jo -Jo Ann -Jo-Ann -Jo-Anne -JoAnn -JoAnne -Joan -Joana -Joane -Joanie -Joann -Joanna -Joanne -Joannes -Jobey -Jobi -Jobie -Jobina -Joby -Jobye -Jobyna -Jocelin -Joceline -Jocelyn -Jocelyne -Jodee -Jodi -Jodie -Jody -Joela -Joelie -Joell -Joella -Joelle -Joellen -Joelly -Joellyn -Joelynn -Joete -Joey -Johanna -Johannah -Johnette -Johnna -Joice -Jojo -Jolee -Joleen -Jolene -Joletta -Joli -Jolie -Joline -Joly -Jolyn -Jolynn -Jonell -Joni -Jonie -Jonis -Jordain -Jordan -Jordana -Jordanna -Jorey -Jori -Jorie -Jorrie -Jorry -Joscelin -Josee -Josefa -Josefina -Joselyn -Josepha -Josephina -Josephine -Josey -Josi -Josie -Joslyn -Josselyn -Josy -Jourdan -Joy -Joya -Joyan -Joyann -Joyce -Joycelin -Joye -Joyous -Juana -Juanita -Jude -Judi -Judie -Judith -Juditha -Judy -Judye -Julee -Juli -Julia -Juliana -Juliane -Juliann -Julianna -Julianne -Julie -Julienne -Juliet -Julieta -Julietta -Juliette -Julina -Juline -Julissa -Julita -Junette -Junia -Junie -Junina -Justin -Justina -Justine -Jyoti -Kacey -Kacie -Kacy -Kai -Kaia -Kaila -Kaile -Kailey -Kaitlin -Kaitlyn -Kaitlynn -Kaja -Kakalina -Kala -Kaleena -Kali -Kalie -Kalila -Kalina -Kalinda -Kalindi -Kalli -Kally -Kameko -Kamila -Kamilah -Kamillah -Kandace -Kandy -Kania -Kanya -Kara -Kara-Lynn -Karalee -Karalynn -Kare -Karee -Karel -Karen -Karena -Kari -Karia -Karie -Karil -Karilynn -Karin -Karina -Karine -Kariotta -Karisa -Karissa -Karita -Karla -Karlee -Karleen -Karlen -Karlene -Karlie -Karlotta -Karlotte -Karly -Karlyn -Karmen -Karna -Karol -Karola -Karole -Karolina -Karoline -Karoly -Karon -Karrah -Karrie -Karry -Kary -Karyl -Karylin -Karyn -Kasey -Kass -Kassandra -Kassey -Kassi -Kassia -Kassie -Kaster -Kat -Kata -Katalin -Kate -Katee -Katerina -Katerine -Katey -Kath -Katha -Katharina -Katharine -Katharyn -Kathe -Katheleen -Katherina -Katherine -Katheryn -Kathi -Kathie -Kathleen -Kathlene -Kathlin -Kathrine -Kathryn -Kathryne -Kathy -Kathye -Kati -Katie -Katina -Katine -Katinka -Katleen -Katlin -Katrina -Katrine -Katrinka -Katti -Kattie -Katuscha -Katusha -Katy -Katya -Kay -Kaycee -Kaye -Kayla -Kayle -Kaylee -Kayley -Kaylil -Kaylyn -Kee -Keeley -Keelia -Keely -Kelcey -Kelci -Kelcie -Kelcy -Kelila -Kellen -Kelley -Kelli -Kellia -Kellie -Kellina -Kellsie -Kelly -Kellyann -Kelsey -Kelsi -Kelsy -Kendra -Kendre -Kenna -Keren -Keri -Keriann -Kerianne -Kerri -Kerrie -Kerrill -Kerrin -Kerry -Kerstin -Kesley -Keslie -Kessia -Kessiah -Ketti -Kettie -Ketty -Kevina -Kevyn -Ki -Kia -Kiah -Kial -Kiele -Kiersten -Kikelia -Kiley -Kim -Kimberlee -Kimberley -Kimberli -Kimberly -Kimberlyn -Kimbra -Kimmi -Kimmie -Kimmy -Kinna -Kip -Kipp -Kippie -Kippy -Kira -Kirbee -Kirbie -Kirby -Kiri -Kirsten -Kirsteni -Kirsti -Kirstie -Kirstin -Kirstyn -Kissee -Kissiah -Kissie -Kit -Kitti -Kittie -Kitty -Kizzee -Kizzie -Klara -Klarika -Klarrisa -Konstance -Konstanze -Koo -Kora -Koral -Koralle -Kordula -Kore -Korella -Koren -Koressa -Kori -Korie -Korney -Korrie -Korry -Kourtney -Kris -Krissie -Krissy -Krista -Kristal -Kristan -Kriste -Kristel -Kristen -Kristi -Kristien -Kristin -Kristina -Kristine -Kristy -Kristyn -Krysta -Krystal -Krystalle -Krystle -Krystyna -Kyla -Kyle -Kylen -Kylie -Kylila -Kylynn -Kym -Kynthia -Kyrstin -La -Lacee -Lacey -Lacie -Lacy -Ladonna -Laetitia -Laila -Laina -Lainey -Lamb -Lana -Lane -Lanette -Laney -Lani -Lanie -Lanita -Lanna -Lanni -Lanny -Lara -Laraine -Lari -Larina -Larine -Larisa -Larissa -Lark -Laryssa -Latashia -Latia -Latisha -Latrena -Latrina -Laura -Lauraine -Laural -Lauralee -Laure -Lauree -Laureen -Laurel -Laurella -Lauren -Laurena -Laurene -Lauretta -Laurette -Lauri -Laurianne -Laurice -Laurie -Lauryn -Lavena -Laverna -Laverne -Lavina -Lavinia -Lavinie -Layla -Layne -Layney -Lea -Leah -Leandra -Leann -Leanna -Leanne -Leanor -Leanora -Lebbie -Leda -Lee -LeeAnn -Leeann -Leeanne -Leela -Leelah -Leena -Leesa -Leese -Legra -Leia -Leiah -Leigh -Leigha -Leila -Leilah -Leisha -Lela -Lelah -Leland -Lelia -Lena -Lenee -Lenette -Lenka -Lenna -Lenora -Lenore -Leodora -Leoine -Leola -Leoline -Leona -Leonanie -Leone -Leonelle -Leonie -Leonora -Leonore -Leontine -Leontyne -Leora -Leorah -Leshia -Lesley -Lesli -Leslie -Lesly -Lesya -Leta -Lethia -Leticia -Letisha -Letitia -Letta -Letti -Lettie -Letty -Leyla -Lezlie -Lia -Lian -Liana -Liane -Lianna -Lianne -Lib -Libbey -Libbi -Libbie -Libby -Licha -Lida -Lidia -Lil -Lila -Lilah -Lilas -Lilia -Lilian -Liliane -Lilias -Lilith -Lilla -Lilli -Lillian -Lillis -Lilllie -Lilly -Lily -Lilyan -Lin -Lina -Lind -Linda -Lindi -Lindie -Lindsay -Lindsey -Lindsy -Lindy -Linea -Linell -Linet -Linette -Linn -Linnea -Linnell -Linnet -Linnie -Linzy -Liora -Liorah -Lira -Lisa -Lisabeth -Lisandra -Lisbeth -Lise -Lisetta -Lisette -Lisha -Lishe -Lissa -Lissi -Lissie -Lissy -Lita -Liuka -Livia -Liz -Liza -Lizabeth -Lizbeth -Lizette -Lizzie -Lizzy -Loella -Lois -Loise -Lola -Lolande -Loleta -Lolita -Lolly -Lona -Lonee -Loni -Lonna -Lonni -Lonnie -Lora -Lorain -Loraine -Loralee -Loralie -Loralyn -Loree -Loreen -Lorelei -Lorelle -Loren -Lorena -Lorene -Lorenza -Loretta -Lorettalorna -Lorette -Lori -Loria -Lorianna -Lorianne -Lorie -Lorilee -Lorilyn -Lorinda -Lorine -Lorita -Lorna -Lorne -Lorraine -Lorrayne -Lorri -Lorrie -Lorrin -Lorry -Lory -Lotta -Lotte -Lotti -Lottie -Lotty -Lou -Louella -Louisa -Louise -Louisette -Love -Luana -Luanna -Luce -Luci -Lucia -Luciana -Lucie -Lucienne -Lucila -Lucilia -Lucille -Lucina -Lucinda -Lucine -Lucita -Lucky -Lucretia -Lucy -Luella -Luelle -Luisa -Luise -Lula -Lulita -Lulu -Luna -Lura -Lurette -Lurleen -Lurlene -Lurline -Lusa -Lust -Lyda -Lydia -Lydie -Lyn -Lynda -Lynde -Lyndel -Lyndell -Lyndsay -Lyndsey -Lyndsie -Lyndy -Lynea -Lynelle -Lynett -Lynette -Lynn -Lynna -Lynne -Lynnea -Lynnell -Lynnelle -Lynnet -Lynnett -Lynnette -Lynsey -Lysandra -Lyssa -Mab -Mabel -Mabelle -Mable -Mada -Madalena -Madalyn -Maddalena -Maddi -Maddie -Maddy -Madel -Madelaine -Madeleine -Madelena -Madelene -Madelin -Madelina -Madeline -Madella -Madelle -Madelon -Madelyn -Madge -Madlen -Madlin -Madona -Madonna -Mady -Mae -Maegan -Mag -Magda -Magdaia -Magdalen -Magdalena -Magdalene -Maggee -Maggi -Maggie -Maggy -Magna -Mahala -Mahalia -Maia -Maible -Maiga -Mair -Maire -Mairead -Maisey -Maisie -Mala -Malanie -Malcah -Malena -Malia -Malina -Malinda -Malinde -Malissa -Malissia -Malka -Malkah -Mallissa -Mallorie -Mallory -Malorie -Malory -Malva -Malvina -Malynda -Mame -Mamie -Manda -Mandi -Mandie -Mandy -Manon -Manya -Mara -Marabel -Marcela -Marcelia -Marcella -Marcelle -Marcellina -Marcelline -Marchelle -Marci -Marcia -Marcie -Marcile -Marcille -Marcy -Mareah -Maren -Marena -Maressa -Marga -Margalit -Margalo -Margaret -Margareta -Margarete -Margaretha -Margarethe -Margaretta -Margarette -Margarita -Margaux -Marge -Margeaux -Margery -Marget -Margette -Margi -Margie -Margit -Marglerite -Margo -Margot -Margret -Marguerite -Margurite -Margy -Mari -Maria -Mariam -Marian -Mariana -Mariann -Marianna -Marianne -Maribel -Maribelle -Maribeth -Marice -Maridel -Marie -Marie-Ann -Marie-Jeanne -Marieann -Mariejeanne -Mariel -Mariele -Marielle -Mariellen -Marietta -Mariette -Marigold -Marijo -Marika -Marilee -Marilin -Marillin -Marilyn -Marin -Marina -Marinna -Marion -Mariquilla -Maris -Marisa -Mariska -Marissa -Marit -Marita -Maritsa -Mariya -Marj -Marja -Marje -Marji -Marjie -Marjorie -Marjory -Marjy -Marketa -Marla -Marlane -Marleah -Marlee -Marleen -Marlena -Marlene -Marley -Marlie -Marline -Marlo -Marlyn -Marna -Marne -Marney -Marni -Marnia -Marnie -Marquita -Marrilee -Marris -Marrissa -Marry -Marsha -Marsiella -Marta -Martelle -Martguerita -Martha -Marthe -Marthena -Marti -Martica -Martie -Martina -Martita -Marty -Martynne -Mary -Marya -Maryangelyn -Maryann -Maryanna -Maryanne -Marybelle -Marybeth -Maryellen -Maryjane -Maryjo -Maryl -Marylee -Marylin -Marylinda -Marylou -Marylynne -Maryrose -Marys -Marysa -Masha -Matelda -Mathilda -Mathilde -Matilda -Matilde -Matti -Mattie -Matty -Maud -Maude -Maudie -Maura -Maure -Maureen -Maureene -Maurene -Maurine -Maurise -Maurita -Mavis -Mavra -Max -Maxi -Maxie -Maxine -Maxy -Maya -Maybelle -Mayda -Maye -Mead -Meade -Meagan -Meaghan -Meara -Mechelle -Meg -Megan -Megen -Meggan -Meggi -Meggie -Meggy -Meghan -Meghann -Mehetabel -Mei -Meira -Mel -Mela -Melamie -Melania -Melanie -Melantha -Melany -Melba -Melesa -Melessa -Melicent -Melina -Melinda -Melinde -Melisa -Melisande -Melisandra -Melisenda -Melisent -Melissa -Melisse -Melita -Melitta -Mella -Melli -Mellicent -Mellie -Mellisa -Mellisent -Mellissa -Melloney -Melly -Melodee -Melodie -Melody -Melonie -Melony -Melosa -Melva -Mercedes -Merci -Mercie -Mercy -Meredith -Meredithe -Meridel -Meridith -Meriel -Merilee -Merilyn -Meris -Merissa -Merl -Merla -Merle -Merlina -Merline -Merna -Merola -Merralee -Merridie -Merrie -Merrielle -Merrile -Merrilee -Merrili -Merrill -Merrily -Merry -Mersey -Meryl -Meta -Mia -Micaela -Michaela -Michaelina -Michaeline -Michaella -Michal -Michel -Michele -Michelina -Micheline -Michell -Michelle -Micki -Mickie -Micky -Midge -Mignon -Mignonne -Miguela -Miguelita -Mildred -Mildrid -Milena -Milicent -Milissent -Milka -Milli -Millicent -Millie -Millisent -Milly -Milzie -Mimi -Min -Mina -Minda -Mindy -Minerva -Minetta -Minette -Minna -Minni -Minnie -Minny -Minta -Miquela -Mira -Mirabel -Mirabella -Mirabelle -Miran -Miranda -Mireielle -Mireille -Mirella -Mirelle -Miriam -Mirilla -Mirna -Misha -Missie -Missy -Misti -Misty -Mitra -Mitzi -Mmarianne -Modesta -Modestia -Modestine -Modesty -Moina -Moira -Moll -Mollee -Molli -Mollie -Molly -Mommy -Mona -Monah -Monica -Monika -Monique -Mora -Moreen -Morena -Morgan -Morgana -Morganica -Morganne -Morgen -Moria -Morissa -Morlee -Morna -Moselle -Moya -Moyna -Moyra -Mozelle -Muffin -Mufi -Mufinella -Muire -Mureil -Murial -Muriel -Murielle -Myna -Myra -Myrah -Myranda -Myriam -Myrilla -Myrle -Myrlene -Myrna -Myrta -Myrtia -Myrtice -Myrtie -Myrtle -Nada -Nadean -Nadeen -Nadia -Nadine -Nadiya -Nady -Nadya -Nalani -Nan -Nana -Nananne -Nance -Nancee -Nancey -Nanci -Nancie -Nancy -Nanete -Nanette -Nani -Nanice -Nanine -Nannette -Nanni -Nannie -Nanny -Nanon -Naoma -Naomi -Nara -Nari -Nariko -Nat -Nata -Natala -Natalee -Natalia -Natalie -Natalina -Nataline -Natalya -Natasha -Natassia -Nathalia -Nathalie -Natka -Natty -Neala -Neda -Nedda -Nedi -Neely -Neila -Neile -Neilla -Neille -Nela -Nelia -Nelie -Nell -Nelle -Nelli -Nellie -Nelly -Nena -Nerissa -Nerita -Nert -Nerta -Nerte -Nerti -Nertie -Nerty -Nessa -Nessi -Nessie -Nessy -Nesta -Netta -Netti -Nettie -Nettle -Netty -Nevsa -Neysa -Nichol -Nichole -Nicholle -Nicki -Nickie -Nicky -Nicol -Nicola -Nicole -Nicolea -Nicolette -Nicoli -Nicolina -Nicoline -Nicolle -Nidia -Nike -Niki -Nikki -Nikkie -Nikoletta -Nikolia -Nil -Nina -Ninetta -Ninette -Ninnetta -Ninnette -Ninon -Nisa -Nissa -Nisse -Nissie -Nissy -Nita -Nitin -Nixie -Noami -Noel -Noelani -Noell -Noella -Noelle -Noellyn -Noelyn -Noemi -Nola -Nolana -Nolie -Nollie -Nomi -Nona -Nonah -Noni -Nonie -Nonna -Nonnah -Nora -Norah -Norean -Noreen -Norene -Norina -Norine -Norma -Norri -Norrie -Norry -Nova -Novelia -Nydia -Nyssa -Octavia -Odele -Odelia -Odelinda -Odella -Odelle -Odessa -Odetta -Odette -Odilia -Odille -Ofelia -Ofella -Ofilia -Ola -Olenka -Olga -Olia -Olimpia -Olive -Olivette -Olivia -Olivie -Oliy -Ollie -Olly -Olva -Olwen -Olympe -Olympia -Olympie -Ondrea -Oneida -Onida -Onlea -Oona -Opal -Opalina -Opaline -Ophelia -Ophelie -Oprah -Ora -Oralee -Oralia -Oralie -Oralla -Oralle -Orel -Orelee -Orelia -Orelie -Orella -Orelle -Oreste -Oriana -Orly -Orsa -Orsola -Ortensia -Otha -Othelia -Othella -Othilia -Othilie -Ottilie -Pacifica -Page -Paige -Paloma -Pam -Pamela -Pamelina -Pamella -Pammi -Pammie -Pammy -Pandora -Pansie -Pansy -Paola -Paolina -Parwane -Pat -Patience -Patrica -Patrice -Patricia -Patrizia -Patsy -Patti -Pattie -Patty -Paula -Paula-Grace -Paule -Pauletta -Paulette -Pauli -Paulie -Paulina -Pauline -Paulita -Pauly -Pavia -Pavla -Pearl -Pearla -Pearle -Pearline -Peg -Pegeen -Peggi -Peggie -Peggy -Pen -Penelopa -Penelope -Penni -Pennie -Penny -Pepi -Pepita -Peri -Peria -Perl -Perla -Perle -Perri -Perrine -Perry -Persis -Pet -Peta -Petra -Petrina -Petronella -Petronia -Petronilla -Petronille -Petunia -Phaedra -Phaidra -Phebe -Phedra -Phelia -Phil -Philipa -Philippa -Philippe -Philippine -Philis -Phillida -Phillie -Phillis -Philly -Philomena -Phoebe -Phylis -Phyllida -Phyllis -Phyllys -Phylys -Pia -Pier -Pierette -Pierrette -Pietra -Piper -Pippa -Pippy -Polly -Pollyanna -Pooh -Poppy -Portia -Pris -Prisca -Priscella -Priscilla -Prissie -Pru -Prudence -Prudi -Prudy -Prue -Prunella -Queada -Queenie -Quentin -Querida -Quinn -Quinta -Quintana -Quintilla -Quintina -Rachael -Rachel -Rachele -Rachelle -Rae -Raf -Rafa -Rafaela -Rafaelia -Rafaelita -Ragnhild -Rahal -Rahel -Raina -Raine -Rakel -Ralina -Ramona -Ramonda -Rana -Randa -Randee -Randene -Randi -Randie -Randy -Ranee -Rani -Rania -Ranice -Ranique -Ranna -Raphaela -Raquel -Raquela -Rasia -Rasla -Raven -Ray -Raychel -Raye -Rayna -Raynell -Rayshell -Rea -Reba -Rebbecca -Rebe -Rebeca -Rebecca -Rebecka -Rebeka -Rebekah -Rebekkah -Ree -Reeba -Reena -Reeta -Reeva -Regan -Reggi -Reggie -Regina -Regine -Reiko -Reina -Reine -Remy -Rena -Renae -Renata -Renate -Rene -Renee -Renel -Renell -Renelle -Renie -Rennie -Reta -Retha -Revkah -Rey -Reyna -Rhea -Rheba -Rheta -Rhetta -Rhiamon -Rhianna -Rhianon -Rhoda -Rhodia -Rhodie -Rhody -Rhona -Rhonda -Riane -Riannon -Rianon -Rica -Ricca -Rici -Ricki -Rickie -Ricky -Riki -Rikki -Rina -Risa -Rissa -Rita -Riva -Rivalee -Rivi -Rivkah -Rivy -Roana -Roanna -Roanne -Robbi -Robbie -Robbin -Robby -Robbyn -Robena -Robenia -Roberta -Robin -Robina -Robinet -Robinett -Robinetta -Robinette -Robinia -Roby -Robyn -Roch -Rochell -Rochella -Rochelle -Rochette -Roda -Rodi -Rodie -Rodina -Romola -Romona -Romonda -Romy -Rona -Ronalda -Ronda -Ronica -Ronna -Ronni -Ronnica -Ronnie -Ronny -Roobbie -Rora -Rori -Rorie -Rory -Ros -Rosa -Rosabel -Rosabella -Rosabelle -Rosaleen -Rosalia -Rosalie -Rosalind -Rosalinda -Rosalinde -Rosaline -Rosalyn -Rosalynd -Rosamond -Rosamund -Rosana -Rosanna -Rosanne -Rosario -Rose -Roseann -Roseanna -Roseanne -Roselia -Roselin -Roseline -Rosella -Roselle -Roselyn -Rosemaria -Rosemarie -Rosemary -Rosemonde -Rosene -Rosetta -Rosette -Roshelle -Rosie -Rosina -Rosita -Roslyn -Rosmunda -Rosy -Row -Rowe -Rowena -Roxana -Roxane -Roxanna -Roxanne -Roxi -Roxie -Roxine -Roxy -Roz -Rozalie -Rozalin -Rozamond -Rozanna -Rozanne -Roze -Rozele -Rozella -Rozelle -Rozina -Rubetta -Rubi -Rubia -Rubie -Rubina -Ruby -Ruella -Ruperta -Ruth -Ruthann -Ruthanne -Ruthe -Ruthi -Ruthie -Ruthy -Ryann -Rycca -Saba -Sabina -Sabine -Sabra -Sabrina -Sacha -Sada -Sadella -Sadie -Sal -Sallee -Salli -Sallie -Sally -Sallyann -Sallyanne -Salome -Sam -Samantha -Samara -Samaria -Sammy -Samuela -Samuella -Sande -Sandi -Sandie -Sandra -Sandy -Sandye -Sapphira -Sapphire -Sara -Sara-Ann -Saraann -Sarah -Sarajane -Saree -Sarena -Sarene -Sarette -Sari -Sarina -Sarine -Sarita -Sascha -Sasha -Sashenka -Saudra -Saundra -Savina -Sayre -Scarlet -Scarlett -Scotty -Sean -Seana -Secunda -Seka -Sela -Selena -Selene -Selestina -Selia -Selie -Selina -Selinda -Seline -Sella -Selle -Selma -Sena -Sephira -Serena -Serene -Shaina -Shaine -Shalna -Shalne -Shamit -Shana -Shanda -Shandee -Shandie -Shandra -Shandy -Shane -Shani -Shanie -Shanna -Shannah -Shannen -Shannon -Shanon -Shanta -Shantee -Shara -Sharai -Shari -Sharia -Sharie -Sharity -Sharl -Sharla -Sharleen -Sharlene -Sharline -Sharna -Sharon -Sharona -Sharra -Sharron -Sharyl -Shaun -Shauna -Shawn -Shawna -Shawnee -Shay -Shayla -Shaylah -Shaylyn -Shaylynn -Shayna -Shayne -Shea -Sheba -Sheela -Sheelagh -Sheelah -Sheena -Sheeree -Sheila -Sheila-Kathryn -Sheilah -Sheilakathryn -Shel -Shela -Shelagh -Shelba -Shelbi -Shelby -Shelia -Shell -Shelley -Shelli -Shellie -Shelly -Shena -Sher -Sheree -Sheri -Sherie -Sheril -Sherill -Sherilyn -Sherline -Sherri -Sherrie -Sherry -Sherye -Sheryl -Shilpa -Shina -Shir -Shira -Shirah -Shirl -Shirlee -Shirleen -Shirlene -Shirley -Shirline -Shoshana -Shoshanna -Shoshie -Siana -Sianna -Sib -Sibbie -Sibby -Sibeal -Sibel -Sibella -Sibelle -Sibilla -Sibley -Sibyl -Sibylla -Sibylle -Sidoney -Sidonia -Sidonnie -Sigrid -Sile -Sileas -Silva -Silvana -Silvia -Silvie -Simona -Simone -Simonette -Simonne -Sindee -Sinead -Siobhan -Sioux -Siouxie -Sisely -Sisile -Sissie -Sissy -Sofia -Sofie -Solange -Sondra -Sonia -Sonja -Sonni -Sonnie -Sonnnie -Sonny -Sonya -Sophey -Sophi -Sophia -Sophie -Sophronia -Sorcha -Sosanna -Stace -Stacee -Stacey -Staci -Stacia -Stacie -Stacy -Stafani -Star -Starla -Starlene -Starlin -Starr -Stefa -Stefania -Stefanie -Steffane -Steffi -Steffie -Stella -Stepha -Stephana -Stephani -Stephanie -Stephannie -Stephenie -Stephi -Stephie -Stephine -Stesha -Stevana -Stevena -Stoddard -Storey -Storm -Stormi -Stormie -Stormy -Sue -Sue-elle -Suellen -Sukey -Suki -Sula -Sunny -Sunshine -Susan -Susana -Susanetta -Susann -Susanna -Susannah -Susanne -Susette -Susi -Susie -Sussi -Susy -Suzan -Suzann -Suzanna -Suzanne -Suzetta -Suzette -Suzi -Suzie -Suzy -Suzzy -Sybil -Sybila -Sybilla -Sybille -Sybyl -Sydel -Sydelle -Sydney -Sylvia -Sylvie -Tabatha -Tabbatha -Tabbi -Tabbie -Tabbitha -Tabby -Tabina -Tabitha -Taffy -Talia -Tallia -Tallie -Tally -Talya -Talyah -Tamar -Tamara -Tamarah -Tamarra -Tamera -Tami -Tamiko -Tamma -Tammara -Tammi -Tammie -Tammy -Tamra -Tana -Tandi -Tandie -Tandy -Tani -Tania -Tansy -Tanya -Tara -Tarah -Tarra -Tarrah -Taryn -Tasha -Tasia -Tate -Tatiana -Tatiania -Tatum -Tawnya -Tawsha -Teane -Ted -Tedda -Teddi -Teddie -Teddy -Tedi -Tedra -Teena -Tella -Teodora -Tera -Teresa -TeresaAnne -Terese -Teresina -Teresita -Teressa -Teri -Teriann -Terina -Terra -Terri -Terri-Jo -Terrianne -Terrie -Terry -Terrye -Tersina -Teryl -Terza -Tess -Tessa -Tessi -Tessie -Tessy -Thalia -Thea -Theada -Theadora -Theda -Thekla -Thelma -Theo -Theodora -Theodosia -Theresa -Theresa-Marie -Therese -Theresina -Theresita -Theressa -Therine -Thia -Thomasa -Thomasin -Thomasina -Thomasine -Tia -Tiana -Tiena -Tierney -Tiertza -Tiff -Tiffani -Tiffanie -Tiffany -Tiffi -Tiffie -Tiffy -Tilda -Tildi -Tildie -Tildy -Tillie -Tilly -Tim -Timi -Timmi -Timmie -Timmy -Timothea -Tina -Tine -Tiphani -Tiphanie -Tiphany -Tish -Tisha -Tobe -Tobey -Tobi -Tobie -Toby -Tobye -Toinette -Toma -Tomasina -Tomasine -Tomi -Tomiko -Tommi -Tommie -Tommy -Toni -Tonia -Tonie -Tony -Tonya -Tootsie -Torey -Tori -Torie -Torrie -Tory -Tova -Tove -Trace -Tracee -Tracey -Traci -Tracie -Tracy -Trenna -Tresa -Trescha -Tressa -Tricia -Trina -Trish -Trisha -Trista -Trix -Trixi -Trixie -Trixy -Truda -Trude -Trudey -Trudi -Trudie -Trudy -Trula -Tuesday -Twila -Twyla -Tybi -Tybie -Tyne -Ula -Ulla -Ulrica -Ulrika -Ulrike -Umeko -Una -Ursa -Ursala -Ursola -Ursula -Ursulina -Ursuline -Uta -Val -Valaree -Valaria -Vale -Valeda -Valencia -Valene -Valenka -Valentia -Valentina -Valentine -Valera -Valeria -Valerie -Valery -Valerye -Valida -Valina -Valli -Vallie -Vally -Valma -Valry -Van -Vanda -Vanessa -Vania -Vanna -Vanni -Vannie -Vanny -Vanya -Veda -Velma -Velvet -Vena -Venita -Ventura -Venus -Vera -Veradis -Vere -Verena -Verene -Veriee -Verile -Verina -Verine -Verla -Verna -Vernice -Veronica -Veronika -Veronike -Veronique -Vi -Vicki -Vickie -Vicky -Victoria -Vida -Viki -Vikki -Vikkie -Vikky -Vilhelmina -Vilma -Vin -Vina -Vinita -Vinni -Vinnie -Vinny -Viola -Violante -Viole -Violet -Violetta -Violette -Virgie -Virgina -Virginia -Virginie -Vita -Vitia -Vitoria -Vittoria -Viv -Viva -Vivi -Vivia -Vivian -Viviana -Vivianna -Vivianne -Vivie -Vivien -Viviene -Vivienne -Viviyan -Vivyan -Vivyanne -Vonni -Vonnie -Vonny -Wallie -Wallis -Wally -Waly -Wanda -Wandie -Wandis -Waneta -Wenda -Wendeline -Wendi -Wendie -Wendy -Wenona -Wenonah -Whitney -Wileen -Wilhelmina -Wilhelmine -Wilie -Willa -Willabella -Willamina -Willetta -Willette -Willi -Willie -Willow -Willy -Willyt -Wilma -Wilmette -Wilona -Wilone -Wilow -Windy -Wini -Winifred -Winna -Winnah -Winne -Winni -Winnie -Winnifred -Winny -Winona -Winonah -Wren -Wrennie -Wylma -Wynn -Wynne -Wynnie -Wynny -Xaviera -Xena -Xenia -Xylia -Xylina -Yalonda -Yehudit -Yelena -Yetta -Yettie -Yetty -Yevette -Yoko -Yolanda -Yolande -Yolane -Yolanthe -Yonina -Yoshi -Yoshiko -Yovonnda -Yvette -Yvonne -Zabrina -Zahara -Zandra -Zaneta -Zara -Zarah -Zaria -Zarla -Zea -Zelda -Zelma -Zena -Zenia -Zia -Zilvia -Zita -Zitella -Zoe -Zola -Zonda -Zondra -Zonnya -Zora -Zorah -Zorana -Zorina -Zorine -Zsa Zsa -Zsazsa -Zulema -Zuzana -Mikako -Kaari -Gita -Geeta diff --git a/nemo_text_processing/text_normalization/en/data/roman/key_word.tsv b/nemo_text_processing/text_normalization/en/data/roman/key_word.tsv deleted file mode 100644 index 861285b439e6..000000000000 --- a/nemo_text_processing/text_normalization/en/data/roman/key_word.tsv +++ /dev/null @@ -1,6 +0,0 @@ -chapter -class -part -article -section -paragraph diff --git a/nemo_text_processing/text_normalization/en/data/roman/male.tsv b/nemo_text_processing/text_normalization/en/data/roman/male.tsv deleted file mode 100644 index 08236aad9b69..000000000000 --- a/nemo_text_processing/text_normalization/en/data/roman/male.tsv +++ /dev/null @@ -1,2946 +0,0 @@ -Aamir -Aaron -Abbey -Abbie -Abbot -Abbott -Abby -Abdel -Abdul -Abdulkarim -Abdullah -Abe -Abel -Abelard -Abner -Abraham -Abram -Ace -Adair -Adam -Adams -Addie -Adger -Aditya -Adlai -Adnan -Adolf -Adolfo -Adolph -Adolphe -Adolpho -Adolphus -Adrian -Adrick -Adrien -Agamemnon -Aguinaldo -Aguste -Agustin -Aharon -Ahmad -Ahmed -Ahmet -Ajai -Ajay -Al -Alaa -Alain -Alan -Alasdair -Alastair -Albatros -Albert -Alberto -Albrecht -Alden -Aldis -Aldo -Aldric -Aldrich -Aldus -Aldwin -Alec -Aleck -Alejandro -Aleks -Aleksandrs -Alessandro -Alex -Alexander -Alexei -Alexis -Alf -Alfie -Alfonse -Alfonso -Alfonzo -Alford -Alfred -Alfredo -Algernon -Ali -Alic -Alister -Alix -Allah -Allan -Allen -Alley -Allie -Allin -Allyn -Alonso -Alonzo -Aloysius -Alphonse -Alphonso -Alston -Alton -Alvin -Alwin -Amadeus -Ambros -Ambrose -Ambrosi -Ambrosio -Ambrosius -Amenhotep -Amery -Amory -Amos -Anatol -Anatole -Anatollo -Anatoly -Anders -Andie -Andonis -Andre -Andrea -Andreas -Andrej -Andres -Andrew -Andrey -Andri -Andros -Andrus -Andrzej -Andy -Angel -Angelico -Angelo -Angie -Angus -Ansel -Ansell -Anselm -Anson -Anthony -Antin -Antoine -Anton -Antone -Antoni -Antonin -Antonino -Antonio -Antonius -Antony -Anurag -Apollo -Apostolos -Aram -Archibald -Archibold -Archie -Archon -Archy -Arel -Ari -Arie -Ariel -Aristotle -Arlo -Armand -Armando -Armond -Armstrong -Arne -Arnie -Arnold -Arnoldo -Aron -Arron -Art -Arther -Arthur -Artie -Artur -Arturo -Arvie -Arvin -Arvind -Arvy -Ash -Ashby -Ashish -Ashley -Ashton -Aub -Aube -Aubert -Aubrey -Augie -Augustin -Augustine -Augusto -Augustus -Austen -Austin -Ave -Averell -Averil -Averill -Avery -Avi -Avraham -Avram -Avrom -Axel -Aylmer -Aziz -Bailey -Bailie -Baillie -Baily -Baird -Baldwin -Bancroft -Barbabas -Barclay -Bard -Barde -Barn -Barnabas -Barnabe -Barnaby -Barnard -Barnebas -Barnett -Barney -Barnie -Barny -Baron -Barr -Barret -Barrett -Barri -Barrie -Barris -Barron -Barry -Bart -Bartel -Barth -Barthel -Bartholemy -Bartholomeo -Bartholomeus -Bartholomew -Bartie -Bartlet -Bartlett -Bartolemo -Bartolomei -Bartolomeo -Barton -Barty -Bary -Basil -Batholomew -Baxter -Bay -Bayard -Beale -Bealle -Bear -Bearnard -Beau -Beaufort -Beauregard -Beck -Bela -Ben -Benedict -Bengt -Benito -Benjamen -Benjamin -Benji -Benjie -Benjy -Benn -Bennet -Bennett -Bennie -Benny -Benson -Bentley -Benton -Beowulf -Berchtold -Berk -Berke -Berkeley -Berkie -Berkley -Bernard -Bernardo -Bernd -Bernhard -Bernie -Bert -Bertie -Bertram -Bertrand -Bharat -Biff -Bill -Billie -Billy -Bing -Binky -Bishop -Bjorn -Bjorne -Blaine -Blair -Blake -Blare -Blayne -Bo -Bob -Bobbie -Bobby -Bogart -Bogdan -Boniface -Boris -Boyce -Boyd -Brad -Braden -Bradford -Bradley -Bradly -Brady -Brandon -Brandy -Brant -Brendan -Brent -Bret -Brett -Brewer -Brewster -Brian -Brice -Briggs -Brinkley -Britt -Brock -Broddie -Broddy -Broderic -Broderick -Brodie -Brody -Bronson -Brook -Brooke -Brooks -Bruce -Bruno -Bryan -Bryant -Bryce -Bryn -Bryon -Bubba -Buck -Bucky -Bud -Buddy -Burgess -Burke -Burl -Burnaby -Burt -Burton -Buster -Butch -Butler -Byram -Byron -Caesar -Cain -Cal -Caldwell -Caleb -Calhoun -Calvin -Cam -Cameron -Cammy -Carey -Carl -Carleigh -Carlie -Carlin -Carlo -Carlos -Carlton -Carlyle -Carmine -Carroll -Carson -Carsten -Carter -Cary -Caryl -Case -Casey -Caspar -Casper -Cass -Cat -Cecil -Cesar -Chad -Chadd -Chaddie -Chaddy -Chadwick -Chaim -Chalmers -Chan -Chance -Chancey -Chanderjit -Chandler -Chane -Chariot -Charles -Charleton -Charley -Charlie -Charlton -Chas -Chase -Chaunce -Chauncey -Che -Chelton -Chen -Chester -Cheston -Chet -Chev -Chevalier -Chevy -Chip -Chris -Chrissy -Christ -Christian -Christiano -Christie -Christof -Christofer -Christoph -Christophe -Christopher -Christorpher -Christos -Christy -Chrisy -Chuck -Churchill -Clair -Claire -Clancy -Clarance -Clare -Clarence -Clark -Clarke -Claude -Claudio -Claudius -Claus -Clay -Clayborn -Clayborne -Claybourne -Clayton -Cleland -Clem -Clemens -Clement -Clemente -Clemmie -Cletus -Cleveland -Cliff -Clifford -Clifton -Clint -Clinten -Clinton -Clive -Clyde -Cob -Cobb -Cobbie -Cobby -Cody -Colbert -Cole -Coleman -Colin -Collin -Collins -Conan -Connie -Connolly -Connor -Conrad -Conroy -Constantin -Constantine -Constantinos -Conway -Cooper -Corbin -Corby -Corey -Corky -Cornelius -Cornellis -Corrie -Cortese -Corwin -Cory -Cosmo -Costa -Courtney -Craig -Crawford -Creighton -Cris -Cristopher -Curt -Curtice -Curtis -Cy -Cyril -Cyrill -Cyrille -Cyrillus -Cyrus -Dabney -Daffy -Dale -Dallas -Dalton -Damian -Damien -Damon -Dan -Dana -Dane -Dani -Danie -Daniel -Dannie -Danny -Dante -Darby -Darcy -Daren -Darian -Darien -Darin -Dario -Darius -Darrel -Darrell -Darren -Darrick -Darrin -Darryl -Darth -Darwin -Daryl -Daryle -Dave -Davey -David -Davidde -Davide -Davidson -Davie -Davin -Davis -Davon -Davoud -Davy -Dawson -Dean -Deane -Del -Delbert -Dell -Delmar -Demetre -Demetri -Demetris -Demetrius -Demosthenis -Denis -Dennie -Dennis -Denny -Derby -Derek -Derick -Derk -Derrek -Derrick -Derrin -Derrol -Derron -Deryl -Desmond -Desmund -Devin -Devon -Dewey -Dewitt -Dexter -Dick -Dickey -Dickie -Diego -Dieter -Dietrich -Dillon -Dimitri -Dimitrios -Dimitris -Dimitrou -Dimitry -Dino -Dion -Dionis -Dionysus -Dirk -Dmitri -Dom -Domenic -Domenico -Dominic -Dominick -Dominique -Don -Donal -Donald -Donn -Donnie -Donny -Donovan -Dorian -Dory -Doug -Douggie -Dougie -Douglas -Douglass -Douglis -Dov -Doyle -Drake -Drew -Dru -Dryke -Duane -Dudley -Duffie -Duffy -Dugan -Duke -Dunc -Duncan -Dunstan -Durand -Durant -Durante -Durward -Dustin -Dwain -Dwaine -Dwane -Dwayne -Dwight -Dylan -Dyson -Earl -Earle -Easton -Eben -Ebeneser -Ebenezer -Eberhard -Ed -Eddie -Eddy -Edgar -Edgardo -Edie -Edmond -Edmund -Edouard -Edsel -Eduard -Eduardo -Edward -Edwin -Efram -Egbert -Ehud -Elbert -Elden -Eldon -Eli -Elias -Elihu -Elijah -Eliot -Eliott -Elisha -Elliot -Elliott -Ellis -Ellsworth -Ellwood -Elmer -Elmore -Elnar -Elric -Elroy -Elton -Elvin -Elvis -Elwin -Elwood -Elwyn -Ely -Emanuel -Emerson -Emery -Emil -Emile -Emilio -Emmanuel -Emmery -Emmet -Emmett -Emmit -Emmott -Emmy -Emory -Ender -Engelbart -Engelbert -Englebart -Englebert -Enoch -Enrico -Enrique -Ephraim -Ephram -Ephrayim -Ephrem -Er -Erasmus -Erastus -Erek -Erhard -Erhart -Eric -Erich -Erick -Erik -Erin -Erl -Ernest -Ernesto -Ernie -Ernst -Erny -Errol -Ervin -Erwin -Esau -Esme -Esteban -Ethan -Ethelbert -Ethelred -Etienne -Euclid -Eugen -Eugene -Eustace -Ev -Evan -Evelyn -Everard -Everett -Ewan -Ewart -Ez -Ezechiel -Ezekiel -Ezra -Fabian -Fabio -Fairfax -Farley -Fazeel -Federico -Felice -Felicio -Felipe -Felix -Ferd -Ferdie -Ferdinand -Ferdy -Fergus -Ferguson -Ferinand -Fernando -Fidel -Filbert -Filip -Filipe -Filmore -Finley -Finn -Fitz -Fitzgerald -Flem -Fleming -Flemming -Fletch -Fletcher -Flin -Flinn -Flint -Flipper -Florian -Floyd -Flynn -Fons -Fonsie -Fonz -Fonzie -Forbes -Ford -Forest -Forester -Forrest -Forrester -Forster -Foster -Fowler -Fox -Fran -Francesco -Francis -Francisco -Francois -Frank -Frankie -Franklin -Franklyn -Franky -Frans -Franz -Fraser -Frazier -Fred -Freddie -Freddy -Frederic -Frederich -Frederick -Frederico -Frederik -Fredric -Fredrick -Freeman -Freemon -Fremont -French -Friedric -Friedrich -Friedrick -Fritz -Fulton -Fyodor -Gabe -Gabriel -Gabriele -Gabriell -Gabriello -Gail -Gale -Galen -Gallagher -Gamaliel -Garcia -Garcon -Gardener -Gardiner -Gardner -Garey -Garfield -Garfinkel -Garold -Garp -Garret -Garrett -Garrot -Garrott -Garry -Garth -Garv -Garvey -Garvin -Garvy -Garwin -Garwood -Gary -Gaspar -Gasper -Gaston -Gav -Gaven -Gavin -Gavriel -Gay -Gayle -Gearard -Gene -Geo -Geof -Geoff -Geoffrey -Geoffry -Georg -George -Georges -Georgia -Georgie -Georgy -Gerald -Geraldo -Gerard -Gere -Gerhard -Gerhardt -Geri -Germaine -Gerold -Gerome -Gerrard -Gerri -Gerrit -Gerry -Gershom -Gershon -Giacomo -Gian -Giancarlo -Giavani -Gibb -Gideon -Giff -Giffard -Giffer -Giffie -Gifford -Giffy -Gil -Gilbert -Gilberto -Gilburt -Giles -Gill -Gilles -Ginger -Gino -Giordano -Giorgi -Giorgio -Giovanne -Giovanni -Giraldo -Giraud -Giuseppe -Glen -Glenn -Glynn -Godard -Godart -Goddard -Goddart -Godfree -Godfrey -Godfry -Godwin -Gomer -Gonzales -Gonzalo -Goober -Goose -Gordan -Gordie -Gordon -Grace -Grady -Graehme -Graeme -Graham -Graig -Grant -Granville -Greg -Gregg -Greggory -Gregor -Gregorio -Gregory -Gretchen -Griff -Griffin -Griffith -Griswold -Grove -Grover -Guido -Guillaume -Guillermo -Gunner -Gunter -Gunther -Gus -Gustaf -Gustav -Gustave -Gustavo -Gustavus -Guthrey -Guthrie -Guthry -Guy -Hadleigh -Hadley -Hadrian -Hagan -Hagen -Hailey -Hakeem -Hakim -Hal -Hale -Haleigh -Haley -Hall -Hallam -Halvard -Ham -Hamel -Hamid -Hamil -Hamilton -Hamish -Hamlen -Hamlet -Hamlin -Hammad -Hamnet -Han -Hanan -Hanford -Hank -Hannibal -Hans -Hans-Peter -Hansel -Hanson -Harald -Harcourt -Hari -Harlan -Harland -Harley -Harlin -Harman -Harmon -Harold -Harris -Harrison -Harrold -Harry -Hart -Hartley -Hartwell -Harv -Harvard -Harvey -Harvie -Harwell -Hasheem -Hashim -Haskel -Haskell -Hassan -Hastings -Hasty -Haven -Hayden -Haydon -Hayes -Hayward -Haywood -Hazel -Heath -Heathcliff -Hebert -Hector -Heinrich -Heinz -Helmuth -Henderson -Hendrick -Hendrik -Henri -Henrie -Henrik -Henrique -Henry -Herb -Herbert -Herbie -Herby -Hercule -Hercules -Herculie -Herman -Hermann -Hermon -Hermy -Hernando -Herold -Herrick -Herrmann -Hersch -Herschel -Hersh -Hershel -Herve -Hervey -Hew -Hewe -Hewet -Hewett -Hewie -Hewitt -Heywood -Hezekiah -Higgins -Hilary -Hilbert -Hill -Hillard -Hillary -Hillel -Hillery -Hilliard -Hilton -Hiralal -Hiram -Hiro -Hirsch -Hobart -Hodge -Hogan -Hollis -Holly -Homer -Horace -Horacio -Horatio -Horatius -Horst -Howard -Howie -Hoyt -Hubert -Hudson -Huey -Hugh -Hugo -Humbert -Humphrey -Hunt -Hunter -Huntington -Huntlee -Huntley -Hurley -Husain -Husein -Hussein -Hy -Hyatt -Hyman -Hymie -Iago -Iain -Ian -Ibrahim -Ichabod -Iggie -Iggy -Ignace -Ignacio -Ignacius -Ignatius -Ignaz -Ignazio -Igor -Ike -Ikey -Immanuel -Ingamar -Ingelbert -Ingemar -Inglebert -Ingmar -Ingram -Inigo -Innocent -Ira -Irvin -Irvine -Irving -Irwin -Isa -Isaac -Isaak -Isador -Isadore -Isaiah -Ishmael -Isidore -Ismail -Israel -Istvan -Ivan -Ivor -Izaak -Izak -Izzy -Jabez -Jack -Jackie -Jackson -Jacob -Jacques -Jae -Jaime -Jake -Jakob -James -Jameson -Jamey -Jamie -Jan -Janos -Janus -Jared -Jarrett -Jarvis -Jason -Jasper -Javier -Jay -Jean -Jean-Christophe -Jean-Francois -Jean-Lou -Jean-Luc -Jean-Marc -Jean-Paul -Jean-Pierre -Jeb -Jed -Jedediah -Jef -Jeff -Jefferey -Jefferson -Jeffery -Jeffie -Jeffrey -Jeffry -Jefry -Jehu -Jennings -Jens -Jephthah -Jerald -Jeramie -Jere -Jereme -Jeremiah -Jeremias -Jeremie -Jeremy -Jermain -Jermaine -Jermayne -Jerold -Jerome -Jeromy -Jerri -Jerrie -Jerrold -Jerrome -Jerry -Jervis -Jerzy -Jess -Jesse -Jessee -Jessey -Jessie -Jesus -Jeth -Jethro -Jim -Jimbo -Jimmie -Jimmy -Jo -Joab -Joachim -Joao -Joaquin -Job -Jock -Jodi -Jodie -Jody -Joe -Joel -Joey -Johan -Johann -Johannes -John -John-David -John-Patrick -Johnathan -Johnathon -Johnnie -Johnny -Johny -Jon -Jonah -Jonas -Jonathan -Jonathon -Jonny -Jordan -Jordon -Jordy -Jorge -Jory -Jose -Josef -Joseph -Josephus -Josh -Joshua -Joshuah -Josiah -Jotham -Juan -Juanita -Jud -Judah -Judas -Judd -Jude -Judith -Judson -Judy -Juergen -Jule -Jules -Julian -Julie -Julio -Julius -Justin -Justis -Kaiser -Kaleb -Kalil -Kalle -Kalman -Kalvin -Kam -Kane -Kareem -Karel -Karim -Karl -Karsten -Kaspar -Keefe -Keenan -Keene -Keil -Keith -Kellen -Kelley -Kelly -Kelsey -Kelvin -Kelwin -Ken -Kendal -Kendall -Kendrick -Kenn -Kennedy -Kenneth -Kenny -Kent -Kenton -Kenyon -Kermie -Kermit -Kerry -Kevan -Kevin -Kim -Kimball -Kimmo -Kin -Kincaid -King -Kingsley -Kingsly -Kingston -Kip -Kirby -Kirk -Kit -Klaus -Klee -Knox -Konrad -Konstantin -Kory -Kostas -Kraig -Kris -Krishna -Kristian -Kristopher -Kristos -Kurt -Kurtis -Kyle -Laird -Lamar -Lambert -Lamont -Lance -Lancelot -Lane -Langston -Lanny -Larry -Lars -Laurance -Lauren -Laurence -Laurens -Laurent -Laurie -Lawerence -Lawrence -Lawson -Lawton -Lay -Layton -Lazar -Lazare -Lazaro -Lazarus -Lazlo -Lee -Lefty -Leif -Leigh -Leighton -Leland -Lem -Lemar -Lemmie -Lemmy -Lemuel -Len -Lenard -Lennie -Lenny -Leo -Leon -Leonard -Leonardo -Leonerd -Leonhard -Leonid -Leonidas -Leopold -Leroy -Les -Lesley -Leslie -Lester -Lev -Levi -Levin -Levon -Levy -Lew -Lewis -Lex -Liam -Lin -Lincoln -Lind -Lindsay -Lindsey -Lindy -Linoel -Linus -Lion -Lionel -Lionello -Llewellyn -Lloyd -Locke -Lockwood -Logan -Lon -Lonnie -Lonny -Loren -Lorenzo -Lorne -Lorrie -Lothar -Lou -Louie -Louis -Lovell -Lowell -Lucas -Luce -Lucian -Luciano -Lucien -Lucio -Lucius -Ludvig -Ludwig -Luigi -Luis -Lukas -Luke -Luther -Lyle -Lyn -Lyndon -Lynn -Mac -Mace -Mack -Mackenzie -Maddie -Maddy -Madison -Magnum -Magnus -Mahesh -Mahmoud -Mahmud -Maison -Major -Malcolm -Manfred -Manish -Manny -Manuel -Marc -Marcel -Marcello -Marcellus -Marcelo -Marchall -Marcio -Marco -Marcos -Marcus -Marietta -Marilu -Mario -Marion -Marius -Mark -Marko -Markos -Markus -Marlin -Marlo -Marlon -Marlow -Marlowe -Marmaduke -Marsh -Marshal -Marshall -Mart -Martainn -Marten -Martie -Martin -Martino -Marty -Martyn -Marv -Marve -Marven -Marvin -Marwin -Mason -Mateo -Mathew -Mathias -Matias -Matt -Matteo -Matthaeus -Mattheus -Matthew -Matthias -Matthieu -Matthiew -Matthus -Mattias -Mattie -Matty -Maurice -Mauricio -Maurie -Maurise -Maurits -Mauritz -Maury -Max -Maxfield -Maxie -Maxim -Maximilian -Maximilien -Maxwell -Mayer -Maynard -Maynord -Mayor -Mead -Meade -Meier -Meir -Mel -Melvin -Melvyn -Menard -Mendel -Mendie -Meredeth -Meredith -Merell -Merill -Merle -Merlin -Merrel -Merrick -Merril -Merrill -Merry -Merv -Mervin -Merwin -Meryl -Meyer -Mic -Micah -Michael -Michail -Michal -Michale -Micheal -Micheil -Michel -Michele -Mick -Mickey -Mickie -Micky -Miguel -Mika -Mikael -Mike -Mikel -Mikey -Mikhail -Miles -Millicent -Milo -Milt -Milton -Mischa -Mitch -Mitchael -Mitchel -Mitchell -Moe -Mohamad -Mohamed -Mohammad -Mohammed -Mohan -Moise -Moises -Moishe -Monroe -Montague -Monte -Montgomery -Monty -Moore -Mordecai -Morgan -Morlee -Morley -Morly -Morrie -Morris -Morry -Morse -Mort -Morten -Mortie -Mortimer -Morton -Morty -Mose -Moses -Moshe -Moss -Muffin -Mugsy -Muhammad -Munmro -Munroe -Murdoch -Murdock -Murphy -Murray -Mustafa -Myke -Myles -Mylo -Myron -Nahum -Napoleon -Nat -Natale -Nate -Nathan -Nathanael -Nathanial -Nathaniel -Nathanil -Neal -Neale -Neall -Nealon -Nealson -Nealy -Ned -Neddie -Neddy -Neel -Neil -Nels -Nelsen -Nelson -Nero -Neron -Nester -Nestor -Nev -Nevil -Nevile -Neville -Nevin -Nevins -Newton -Niall -Niccolo -Nicholas -Nichole -Nichols -Nick -Nickey -Nickie -Nickolas -Nicky -Nico -Nicolas -Niels -Nigel -Niki -Nikita -Nikki -Nikolai -Nikos -Niles -Nils -Nilson -Niven -Noach -Noah -Noam -Noble -Noe -Noel -Nolan -Noland -Norbert -Norm -Norman -Normand -Normie -Norris -Northrop -Northrup -Norton -Norwood -Nunzio -Obadiah -Obadias -Oberon -Obie -Octavius -Odell -Odie -Odin -Odysseus -Olaf -Olag -Ole -Oleg -Olin -Oliver -Olivier -Olle -Ollie -Omar -Oral -Oran -Orazio -Orbadiah -Oren -Orin -Orion -Orlando -Orren -Orrin -Orson -Orton -Orville -Osbert -Osborn -Osborne -Osbourn -Osbourne -Oscar -Osgood -Osmond -Osmund -Ossie -Oswald -Oswell -Otes -Othello -Otho -Otis -Otto -Owen -Ozzie -Ozzy -Pablo -Pace -Paco -Paddie -Paddy -Padraig -Page -Paige -Pail -Palmer -Paolo -Park -Parke -Parker -Parnell -Parrnell -Parry -Parsifal -Partha -Pascal -Pascale -Pasquale -Pat -Pate -Patel -Paten -Patin -Paton -Patric -Patrice -Patricio -Patrick -Patrik -Patsy -Pattie -Patty -Paul -Paulo -Pavel -Pearce -Pedro -Peirce -Pembroke -Pen -Penn -Pennie -Penny -Penrod -Pepe -Pepillo -Pepito -Perceval -Percival -Percy -Perry -Pete -Peter -Petey -Petr -Peyter -Peyton -Phil -Philbert -Philip -Phillip -Phillipe -Phillipp -Phineas -Phip -Pierce -Pierre -Pierson -Piet -Pieter -Pietro -Piggy -Pincas -Pinchas -Pincus -Piotr -Pip -Pius -Plato -Pooh -Porter -Poul -Powell -Praneetf -Prasad -Prasun -Prent -Prentice -Prentiss -Prescott -Preston -Price -Prince -Pryce -Puff -Purcell -Putnam -Pyotr -Quent -Quentin -Quiggly -Quigly -Quigman -Quill -Quillan -Quincey -Quincy -Quinlan -Quinn -Quint -Quintin -Quinton -Quintus -Rab -Rabbi -Rabi -Rad -Radcliffe -Rafael -Rafe -Ragnar -Raimund -Rainer -Raj -Rajeev -Raleigh -Ralf -Ralph -Ram -Rameses -Ramesh -Ramon -Ramsay -Ramsey -Rand -Randal -Randall -Randell -Randi -Randie -Randolf -Randolph -Randy -Ransell -Ransom -Raoul -Raphael -Raul -Ravi -Ravil -Rawley -Ray -Raymond -Raymund -Raymundo -Raynard -Rayner -Raynor -Reagan -Red -Redford -Redmond -Reece -Reed -Rees -Reese -Reg -Regan -Regen -Reggie -Reggis -Reggy -Reginald -Reginauld -Reid -Reilly -Reinhard -Reinhold -Rem -Remington -Remus -Renado -Renaldo -Renard -Renato -Renaud -Renault -Rene -Reube -Reuben -Reuven -Rex -Rey -Reynard -Reynold -Reynolds -Reza -Rhett -Ric -Ricard -Ricardo -Riccardo -Rice -Rich -Richard -Richardo -Richie -Richmond -Richy -Rick -Rickard -Rickey -Ricki -Rickie -Ricky -Rik -Rikki -Riley -Rinaldo -Ripley -Ritch -Ritchie -Roarke -Rob -Robb -Robbert -Robbie -Robert -Roberto -Robin -Robinson -Rochester -Rock -Rockwell -Rocky -Rod -Rodd -Roddie -Roddy -Roderic -Roderich -Roderick -Roderigo -Rodge -Rodger -Rodney -Rodolfo -Rodolph -Rodolphe -Rodrick -Rodrigo -Rodrique -Rog -Roger -Rogers -Roice -Roland -Rolando -Rolf -Rolfe -Rolland -Rollin -Rollins -Rollo -Rolph -Romain -Roman -Romeo -Ron -Ronald -Ronen -Roni -Ronnie -Ronny -Roosevelt -Rory -Roscoe -Ross -Roth -Rourke -Rowland -Roy -Royal -Royce -Rube -Ruben -Rubin -Ruby -Rudd -Ruddie -Ruddy -Rudie -Rudiger -Rudolf -Rudolfo -Rudolph -Rudy -Rudyard -Rufe -Rufus -Rupert -Ruperto -Russ -Russel -Russell -Rustie -Rustin -Rusty -Rutger -Rutherford -Rutledge -Rutter -Ryan -Sal -Salem -Salim -Salman -Salmon -Salomo -Salomon -Salomone -Salvador -Salvatore -Salvidor -Sam -Sammie -Sammy -Sampson -Samson -Samuel -Samuele -Sancho -Sander -Sanders -Sanderson -Sandor -Sandro -Sandy -Sanford -Sanson -Sansone -Sarge -Sargent -Sascha -Sasha -Saul -Sauncho -Saunder -Saunders -Saunderson -Saundra -Saw -Sawyer -Sawyere -Sax -Saxe -Saxon -Say -Sayer -Sayers -Sayre -Sayres -Scarface -Schroeder -Schuyler -Scot -Scott -Scotti -Scottie -Scotty -Seamus -Sean -Sebastian -Sebastiano -Sebastien -See -Selby -Selig -Serge -Sergeant -Sergei -Sergent -Sergio -Seth -Seymour -Shadow -Shaine -Shalom -Shamus -Shamshi-Adad -Shanan -Shane -Shannan -Shannon -Shaughn -Shaun -Shaw -Shawn -Shay -Shayne -Shea -Sheff -Sheffie -Sheffield -Sheffy -Shelby -Shelden -Sheldon -Shell -Shelley -Shelton -Shem -Shep -Shepard -Shepherd -Sheppard -Shepperd -Sheridan -Sherlock -Sherlocke -Sherman -Sherwin -Sherwood -Sherwynd -Shimon -Shlomo -Sholom -Shorty -Shurlock -Shurlocke -Shurwood -Si -Sibyl -Sid -Siddhartha -Sidnee -Sidney -Siegfried -Siffre -Sig -Sigfrid -Sigfried -Sigmund -Silas -Silvain -Silvan -Silvano -Silvanus -Silvester -Silvio -Sim -Simeon -Simmonds -Simon -Simone -Sinclair -Sinclare -Sivert -Siward -Sixtus -Skell -Skelly -Skip -Skipp -Skipper -Skippie -Skippy -Skipton -Sky -Skye -Skylar -Skyler -Slade -Slim -Sloan -Sloane -Sly -Smith -Smitty -Socrates -Sol -Sollie -Solly -Solomon -Somerset -Son -Sonnie -Sonny -Sparky -Spence -Spencer -Spense -Spenser -Spike -Spiro -Spiros -Spud -Srinivas -Stacy -Staffard -Stafford -Staford -Stan -Standford -Stanfield -Stanford -Stanislaw -Stanleigh -Stanley -Stanly -Stanton -Stanwood -Stavros -Stearn -Stearne -Stefan -Stefano -Steffen -Stephan -Stephanus -Stephen -Sterling -Stern -Sterne -Steve -Steven -Stevie -Stevy -Stew -Steward -Stewart -Stig -Stillman -Stillmann -Sting -Stinky -Stirling -Stu -Stuart -Sturgis -Sullivan -Sully -Sumner -Sunny -Sutherland -Sutton -Sven -Swen -Syd -Sydney -Sylvan -Sylvester -Tab -Tabb -Tabbie -Tabby -Taber -Tabor -Tad -Tadd -Taddeo -Taddeus -Tadeas -Tailor -Tait -Taite -Talbert -Talbot -Tallie -Tally -Tam -Tamas -Tammie -Tammy -Tan -Tann -Tanner -Tanney -Tannie -Tanny -Tarrance -Tarrant -Tarzan -Tate -Taylor -Teador -Ted -Tedd -Teddie -Teddy -Tedie -Tedman -Tedmund -Tedrick -Temp -Temple -Templeton -Teodoor -Teodor -Teodorico -Teodoro -Terence -Terencio -Terrance -Terrel -Terrell -Terrence -Terri -Terrill -Terry -Thacher -Thad -Thaddeus -Thaddius -Thaddus -Thadeus -Thain -Thaine -Thane -Tharen -Thatch -Thatcher -Thaxter -Thayne -Thebault -Thedric -Thedrick -Theo -Theobald -Theodor -Theodore -Theodoric -Theophyllus -Thibaud -Thibaut -Thom -Thomas -Thor -Thorn -Thorndike -Thornie -Thornton -Thorny -Thorpe -Thorstein -Thorsten -Thorvald -Thurstan -Thurston -Tibold -Tiebold -Tiebout -Tiler -Tim -Timmie -Timmy -Timothee -Timotheus -Timothy -Tirrell -Tito -Titos -Titus -Tobe -Tobiah -Tobias -Tobie -Tobin -Tobit -Toby -Tod -Todd -Toddie -Toddy -Tom -Tomas -Tome -Tomkin -Tomlin -Tommie -Tommy -Tonnie -Tony -Tore -Torey -Torin -Torr -Torrance -Torre -Torrence -Torrey -Torrin -Torry -Town -Towney -Townie -Townsend -Towny -Trace -Tracey -Tracie -Tracy -Traver -Travers -Travis -Tray -Tre -Tremain -Tremaine -Tremayne -Trent -Trenton -Trev -Trevar -Trever -Trevor -Trey -Trip -Tristan -Troy -Truman -Tuck -Tucker -Tuckie -Tucky -Tudor -Tull -Tulley -Tully -Turner -Ty -Tybalt -Tye -Tyler -Tymon -Tymothy -Tynan -Tyrone -Tyrus -Tyson -Udale -Udall -Udell -Ugo -Ulberto -Uli -Ulick -Ulises -Ulric -Ulrich -Ulrick -Ulysses -Umberto -Upton -Urbain -Urban -Urbano -Urbanus -Uri -Uriah -Uriel -Urson -Vachel -Vaclav -Vail -Val -Valdemar -Vale -Valentin -Valentine -Valentinian -Van -Vance -Vasili -Vasilis -Vasily -Vassili -Vassily -Vaughan -Vaughn -Venkat -Verge -Vergil -Vern -Verne -Vernen -Verney -Vernon -Vernor -Vic -Vick -Victor -Vijay -Vilhelm -Vin -Vince -Vincent -Vincents -Vinnie -Vinny -Vinod -Virge -Virgie -Virgil -Virgilio -Vite -Vito -Vlad -Vladamir -Vladimir -Voltaire -Von -Wade -Wadsworth -Wain -Waine -Wainwright -Wait -Waite -Waiter -Wake -Wakefield -Wald -Waldemar -Walden -Waldo -Waldon -Waleed -Walker -Wallace -Wallache -Wallas -Wallie -Wallis -Wally -Walsh -Walt -Walter -Walther -Walton -Wang -Ward -Warde -Warden -Ware -Waring -Warner -Warren -Wash -Washington -Wat -Waverley -Waverly -Way -Waylan -Wayland -Waylen -Waylin -Waylon -Wayne -Web -Webb -Weber -Webster -Weidar -Weider -Welbie -Welby -Welch -Wells -Welsh -Wendall -Wendel -Wendell -Werner -Wes -Wesley -Weslie -West -Westbrook -Westbrooke -Westleigh -Westley -Weston -Weylin -Wheeler -Whit -Whitaker -Whitby -Whitman -Whitney -Whittaker -Wiatt -Wilber -Wilbert -Wilbur -Wilburn -Wilburt -Wilden -Wildon -Wilek -Wiley -Wilfred -Wilfrid -Wilhelm -Will -Willard -Willdon -Willem -Willey -Willi -William -Willie -Willis -Willmott -Willy -Wilmar -Wilmer -Wilson -Wilt -Wilton -Win -Windham -Winfield -Winford -Winfred -Winifield -Winn -Winnie -Winny -Winslow -Winston -Winthrop -Winton -Wit -Witold -Wittie -Witty -Wojciech -Wolfgang -Wolfie -Wolfram -Wolfy -Woochang -Woodie -Woodman -Woodrow -Woody -Worden -Worthington -Worthy -Wright -Wyatan -Wyatt -Wye -Wylie -Wyn -Wyndham -Wynn -Wynton -Xavier -Xenos -Xerxes -Xever -Ximenes -Ximenez -Xymenes -Yaakov -Yacov -Yale -Yanaton -Yance -Yancey -Yancy -Yank -Yankee -Yard -Yardley -Yehudi -Yigal -Yule -Yuri -Yves -Zach -Zacharia -Zachariah -Zacharias -Zacharie -Zachary -Zacherie -Zachery -Zack -Zackariah -Zak -Zalman -Zane -Zared -Zary -Zeb -Zebadiah -Zebedee -Zebulen -Zebulon -Zechariah -Zed -Zedekiah -Zeke -Zelig -Zerk -Zeus -Zippy -Zollie -Zolly -Zorro -Rahul -Shumeet -Vibhu diff --git a/nemo_text_processing/text_normalization/en/data/roman/roman_to_spoken.tsv b/nemo_text_processing/text_normalization/en/data/roman/roman_to_spoken.tsv deleted file mode 100644 index 971c90e37b22..000000000000 --- a/nemo_text_processing/text_normalization/en/data/roman/roman_to_spoken.tsv +++ /dev/null @@ -1,2000 +0,0 @@ -I one -II two -III three -IV four -V five -VI six -VII seven -VIII eight -IX nine -X ten -XI eleven -XII twelve -XIII thirteen -XIV fourteen -XV fifteen -XVI sixteen -XVII seventeen -XVIII eighteen -XIX nineteen -XX twenty -XXI twenty one -XXII twenty two -XXIII twenty three -XXIV twenty four -XXV twenty five -XXVI twenty six -XXVII twenty seven -XXVIII twenty eight -XXIX twenty nine -XXX thirty -XXXI thirty one -XXXII thirty two -XXXIII thirty three -XXXIV thirty four -XXXV thirty five -XXXVI thirty six -XXXVII thirty seven -XXXVIII thirty eight -XXXIX thirty nine -XL forty -XLI forty one -XLII forty two -XLIII forty three -XLIV forty four -XLV forty five -XLVI forty six -XLVII forty seven -XLVIII forty eight -XLIX forty nine -L fifty -LI fifty one -LII fifty two -LIII fifty three -LIV fifty four -LV fifty five -LVI fifty six -LVII fifty seven -LVIII fifty eight -LIX fifty nine -LX sixty -LXI sixty one -LXII sixty two -LXIII sixty three -LXIV sixty four -LXV sixty five -LXVI sixty six -LXVII sixty seven -LXVIII sixty eight -LXIX sixty nine -LXX seventy -LXXI seventy one -LXXII seventy two -LXXIII seventy three -LXXIV seventy four -LXXV seventy five -LXXVI seventy six -LXXVII seventy seven -LXXVIII seventy eight -LXXIX seventy nine -LXXX eighty -LXXXI eighty one -LXXXII eighty two -LXXXIII eighty three -LXXXIV eighty four -LXXXV eighty five -LXXXVI eighty six -LXXXVII eighty seven -LXXXVIII eighty eight -LXXXIX eighty nine -XC ninety -XCI ninety one -XCII ninety two -XCIII ninety three -XCIV ninety four -XCV ninety five -XCVI ninety six -XCVII ninety seven -XCVIII ninety eight -XCIX ninety nine -C one hundred -CI one hundred one -CII one hundred two -CIII one hundred three -CIV one hundred four -CV one hundred five -CVI one hundred six -CVII one hundred seven -CVIII one hundred eight -CIX one hundred nine -CX one hundred ten -CXI one hundred eleven -CXII one hundred twelve -CXIII one hundred thirteen -CXIV one hundred fourteen -CXV one hundred fifteen -CXVI one hundred sixteen -CXVII one hundred seventeen -CXVIII one hundred eighteen -CXIX one hundred nineteen -CXX one hundred twenty -CXXI one hundred twenty one -CXXII one hundred twenty two -CXXIII one hundred twenty three -CXXIV one hundred twenty four -CXXV one hundred twenty five -CXXVI one hundred twenty six -CXXVII one hundred twenty seven -CXXVIII one hundred twenty eight -CXXIX one hundred twenty nine -CXXX one hundred thirty -CXXXI one hundred thirty one -CXXXII one hundred thirty two -CXXXIII one hundred thirty three -CXXXIV one hundred thirty four -CXXXV one hundred thirty five -CXXXVI one hundred thirty six -CXXXVII one hundred thirty seven -CXXXVIII one hundred thirty eight -CXXXIX one hundred thirty nine -CXL one hundred forty -CXLI one hundred forty one -CXLII one hundred forty two -CXLIII one hundred forty three -CXLIV one hundred forty four -CXLV one hundred forty five -CXLVI one hundred forty six -CXLVII one hundred forty seven -CXLVIII one hundred forty eight -CXLIX one hundred forty nine -CL one hundred fifty -CLI one hundred fifty one -CLII one hundred fifty two -CLIII one hundred fifty three -CLIV one hundred fifty four -CLV one hundred fifty five -CLVI one hundred fifty six -CLVII one hundred fifty seven -CLVIII one hundred fifty eight -CLIX one hundred fifty nine -CLX one hundred sixty -CLXI one hundred sixty one -CLXII one hundred sixty two -CLXIII one hundred sixty three -CLXIV one hundred sixty four -CLXV one hundred sixty five -CLXVI one hundred sixty six -CLXVII one hundred sixty seven -CLXVIII one hundred sixty eight -CLXIX one hundred sixty nine -CLXX one hundred seventy -CLXXI one hundred seventy one -CLXXII one hundred seventy two -CLXXIII one hundred seventy three -CLXXIV one hundred seventy four -CLXXV one hundred seventy five -CLXXVI one hundred seventy six -CLXXVII one hundred seventy seven -CLXXVIII one hundred seventy eight -CLXXIX one hundred seventy nine -CLXXX one hundred eighty -CLXXXI one hundred eighty one -CLXXXII one hundred eighty two -CLXXXIII one hundred eighty three -CLXXXIV one hundred eighty four -CLXXXV one hundred eighty five -CLXXXVI one hundred eighty six -CLXXXVII one hundred eighty seven -CLXXXVIII one hundred eighty eight -CLXXXIX one hundred eighty nine -CXC one hundred ninety -CXCI one hundred ninety one -CXCII one hundred ninety two -CXCIII one hundred ninety three -CXCIV one hundred ninety four -CXCV one hundred ninety five -CXCVI one hundred ninety six -CXCVII one hundred ninety seven -CXCVIII one hundred ninety eight -CXCIX one hundred ninety nine -CC two hundred -CCI two hundred one -CCII two hundred two -CCIII two hundred three -CCIV two hundred four -CCV two hundred five -CCVI two hundred six -CCVII two hundred seven -CCVIII two hundred eight -CCIX two hundred nine -CCX two hundred ten -CCXI two hundred eleven -CCXII two hundred twelve -CCXIII two hundred thirteen -CCXIV two hundred fourteen -CCXV two hundred fifteen -CCXVI two hundred sixteen -CCXVII two hundred seventeen -CCXVIII two hundred eighteen -CCXIX two hundred nineteen -CCXX two hundred twenty -CCXXI two hundred twenty one -CCXXII two hundred twenty two -CCXXIII two hundred twenty three -CCXXIV two hundred twenty four -CCXXV two hundred twenty five -CCXXVI two hundred twenty six -CCXXVII two hundred twenty seven -CCXXVIII two hundred twenty eight -CCXXIX two hundred twenty nine -CCXXX two hundred thirty -CCXXXI two hundred thirty one -CCXXXII two hundred thirty two -CCXXXIII two hundred thirty three -CCXXXIV two hundred thirty four -CCXXXV two hundred thirty five -CCXXXVI two hundred thirty six -CCXXXVII two hundred thirty seven -CCXXXVIII two hundred thirty eight -CCXXXIX two hundred thirty nine -CCXL two hundred forty -CCXLI two hundred forty one -CCXLII two hundred forty two -CCXLIII two hundred forty three -CCXLIV two hundred forty four -CCXLV two hundred forty five -CCXLVI two hundred forty six -CCXLVII two hundred forty seven -CCXLVIII two hundred forty eight -CCXLIX two hundred forty nine -CCL two hundred fifty -CCLI two hundred fifty one -CCLII two hundred fifty two -CCLIII two hundred fifty three -CCLIV two hundred fifty four -CCLV two hundred fifty five -CCLVI two hundred fifty six -CCLVII two hundred fifty seven -CCLVIII two hundred fifty eight -CCLIX two hundred fifty nine -CCLX two hundred sixty -CCLXI two hundred sixty one -CCLXII two hundred sixty two -CCLXIII two hundred sixty three -CCLXIV two hundred sixty four -CCLXV two hundred sixty five -CCLXVI two hundred sixty six -CCLXVII two hundred sixty seven -CCLXVIII two hundred sixty eight -CCLXIX two hundred sixty nine -CCLXX two hundred seventy -CCLXXI two hundred seventy one -CCLXXII two hundred seventy two -CCLXXIII two hundred seventy three -CCLXXIV two hundred seventy four -CCLXXV two hundred seventy five -CCLXXVI two hundred seventy six -CCLXXVII two hundred seventy seven -CCLXXVIII two hundred seventy eight -CCLXXIX two hundred seventy nine -CCLXXX two hundred eighty -CCLXXXI two hundred eighty one -CCLXXXII two hundred eighty two -CCLXXXIII two hundred eighty three -CCLXXXIV two hundred eighty four -CCLXXXV two hundred eighty five -CCLXXXVI two hundred eighty six -CCLXXXVII two hundred eighty seven -CCLXXXVIII two hundred eighty eight -CCLXXXIX two hundred eighty nine -CCXC two hundred ninety -CCXCI two hundred ninety one -CCXCII two hundred ninety two -CCXCIII two hundred ninety three -CCXCIV two hundred ninety four -CCXCV two hundred ninety five -CCXCVI two hundred ninety six -CCXCVII two hundred ninety seven -CCXCVIII two hundred ninety eight -CCXCIX two hundred ninety nine -CCC three hundred -CCCI three hundred one -CCCII three hundred two -CCCIII three hundred three -CCCIV three hundred four -CCCV three hundred five -CCCVI three hundred six -CCCVII three hundred seven -CCCVIII three hundred eight -CCCIX three hundred nine -CCCX three hundred ten -CCCXI three hundred eleven -CCCXII three hundred twelve -CCCXIII three hundred thirteen -CCCXIV three hundred fourteen -CCCXV three hundred fifteen -CCCXVI three hundred sixteen -CCCXVII three hundred seventeen -CCCXVIII three hundred eighteen -CCCXIX three hundred nineteen -CCCXX three hundred twenty -CCCXXI three hundred twenty one -CCCXXII three hundred twenty two -CCCXXIII three hundred twenty three -CCCXXIV three hundred twenty four -CCCXXV three hundred twenty five -CCCXXVI three hundred twenty six -CCCXXVII three hundred twenty seven -CCCXXVIII three hundred twenty eight -CCCXXIX three hundred twenty nine -CCCXXX three hundred thirty -CCCXXXI three hundred thirty one -CCCXXXII three hundred thirty two -CCCXXXIII three hundred thirty three -CCCXXXIV three hundred thirty four -CCCXXXV three hundred thirty five -CCCXXXVI three hundred thirty six -CCCXXXVII three hundred thirty seven -CCCXXXVIII three hundred thirty eight -CCCXXXIX three hundred thirty nine -CCCXL three hundred forty -CCCXLI three hundred forty one -CCCXLII three hundred forty two -CCCXLIII three hundred forty three -CCCXLIV three hundred forty four -CCCXLV three hundred forty five -CCCXLVI three hundred forty six -CCCXLVII three hundred forty seven -CCCXLVIII three hundred forty eight -CCCXLIX three hundred forty nine -CCCL three hundred fifty -CCCLI three hundred fifty one -CCCLII three hundred fifty two -CCCLIII three hundred fifty three -CCCLIV three hundred fifty four -CCCLV three hundred fifty five -CCCLVI three hundred fifty six -CCCLVII three hundred fifty seven -CCCLVIII three hundred fifty eight -CCCLIX three hundred fifty nine -CCCLX three hundred sixty -CCCLXI three hundred sixty one -CCCLXII three hundred sixty two -CCCLXIII three hundred sixty three -CCCLXIV three hundred sixty four -CCCLXV three hundred sixty five -CCCLXVI three hundred sixty six -CCCLXVII three hundred sixty seven -CCCLXVIII three hundred sixty eight -CCCLXIX three hundred sixty nine -CCCLXX three hundred seventy -CCCLXXI three hundred seventy one -CCCLXXII three hundred seventy two -CCCLXXIII three hundred seventy three -CCCLXXIV three hundred seventy four -CCCLXXV three hundred seventy five -CCCLXXVI three hundred seventy six -CCCLXXVII three hundred seventy seven -CCCLXXVIII three hundred seventy eight -CCCLXXIX three hundred seventy nine -CCCLXXX three hundred eighty -CCCLXXXI three hundred eighty one -CCCLXXXII three hundred eighty two -CCCLXXXIII three hundred eighty three -CCCLXXXIV three hundred eighty four -CCCLXXXV three hundred eighty five -CCCLXXXVI three hundred eighty six -CCCLXXXVII three hundred eighty seven -CCCLXXXVIII three hundred eighty eight -CCCLXXXIX three hundred eighty nine -CCCXC three hundred ninety -CCCXCI three hundred ninety one -CCCXCII three hundred ninety two -CCCXCIII three hundred ninety three -CCCXCIV three hundred ninety four -CCCXCV three hundred ninety five -CCCXCVI three hundred ninety six -CCCXCVII three hundred ninety seven -CCCXCVIII three hundred ninety eight -CCCXCIX three hundred ninety nine -CD four hundred -CDI four hundred one -CDII four hundred two -CDIII four hundred three -CDIV four hundred four -CDV four hundred five -CDVI four hundred six -CDVII four hundred seven -CDVIII four hundred eight -CDIX four hundred nine -CDX four hundred ten -CDXI four hundred eleven -CDXII four hundred twelve -CDXIII four hundred thirteen -CDXIV four hundred fourteen -CDXV four hundred fifteen -CDXVI four hundred sixteen -CDXVII four hundred seventeen -CDXVIII four hundred eighteen -CDXIX four hundred nineteen -CDXX four hundred twenty -CDXXI four hundred twenty one -CDXXII four hundred twenty two -CDXXIII four hundred twenty three -CDXXIV four hundred twenty four -CDXXV four hundred twenty five -CDXXVI four hundred twenty six -CDXXVII four hundred twenty seven -CDXXVIII four hundred twenty eight -CDXXIX four hundred twenty nine -CDXXX four hundred thirty -CDXXXI four hundred thirty one -CDXXXII four hundred thirty two -CDXXXIII four hundred thirty three -CDXXXIV four hundred thirty four -CDXXXV four hundred thirty five -CDXXXVI four hundred thirty six -CDXXXVII four hundred thirty seven -CDXXXVIII four hundred thirty eight -CDXXXIX four hundred thirty nine -CDXL four hundred forty -CDXLI four hundred forty one -CDXLII four hundred forty two -CDXLIII four hundred forty three -CDXLIV four hundred forty four -CDXLV four hundred forty five -CDXLVI four hundred forty six -CDXLVII four hundred forty seven -CDXLVIII four hundred forty eight -CDXLIX four hundred forty nine -CDL four hundred fifty -CDLI four hundred fifty one -CDLII four hundred fifty two -CDLIII four hundred fifty three -CDLIV four hundred fifty four -CDLV four hundred fifty five -CDLVI four hundred fifty six -CDLVII four hundred fifty seven -CDLVIII four hundred fifty eight -CDLIX four hundred fifty nine -CDLX four hundred sixty -CDLXI four hundred sixty one -CDLXII four hundred sixty two -CDLXIII four hundred sixty three -CDLXIV four hundred sixty four -CDLXV four hundred sixty five -CDLXVI four hundred sixty six -CDLXVII four hundred sixty seven -CDLXVIII four hundred sixty eight -CDLXIX four hundred sixty nine -CDLXX four hundred seventy -CDLXXI four hundred seventy one -CDLXXII four hundred seventy two -CDLXXIII four hundred seventy three -CDLXXIV four hundred seventy four -CDLXXV four hundred seventy five -CDLXXVI four hundred seventy six -CDLXXVII four hundred seventy seven -CDLXXVIII four hundred seventy eight -CDLXXIX four hundred seventy nine -CDLXXX four hundred eighty -CDLXXXI four hundred eighty one -CDLXXXII four hundred eighty two -CDLXXXIII four hundred eighty three -CDLXXXIV four hundred eighty four -CDLXXXV four hundred eighty five -CDLXXXVI four hundred eighty six -CDLXXXVII four hundred eighty seven -CDLXXXVIII four hundred eighty eight -CDLXXXIX four hundred eighty nine -CDXC four hundred ninety -CDXCI four hundred ninety one -CDXCII four hundred ninety two -CDXCIII four hundred ninety three -CDXCIV four hundred ninety four -CDXCV four hundred ninety five -CDXCVI four hundred ninety six -CDXCVII four hundred ninety seven -CDXCVIII four hundred ninety eight -CDXCIX four hundred ninety nine -D five hundred -DI five hundred one -DII five hundred two -DIII five hundred three -DIV five hundred four -DV five hundred five -DVI five hundred six -DVII five hundred seven -DVIII five hundred eight -DIX five hundred nine -DX five hundred ten -DXI five hundred eleven -DXII five hundred twelve -DXIII five hundred thirteen -DXIV five hundred fourteen -DXV five hundred fifteen -DXVI five hundred sixteen -DXVII five hundred seventeen -DXVIII five hundred eighteen -DXIX five hundred nineteen -DXX five hundred twenty -DXXI five hundred twenty one -DXXII five hundred twenty two -DXXIII five hundred twenty three -DXXIV five hundred twenty four -DXXV five hundred twenty five -DXXVI five hundred twenty six -DXXVII five hundred twenty seven -DXXVIII five hundred twenty eight -DXXIX five hundred twenty nine -DXXX five hundred thirty -DXXXI five hundred thirty one -DXXXII five hundred thirty two -DXXXIII five hundred thirty three -DXXXIV five hundred thirty four -DXXXV five hundred thirty five -DXXXVI five hundred thirty six -DXXXVII five hundred thirty seven -DXXXVIII five hundred thirty eight -DXXXIX five hundred thirty nine -DXL five hundred forty -DXLI five hundred forty one -DXLII five hundred forty two -DXLIII five hundred forty three -DXLIV five hundred forty four -DXLV five hundred forty five -DXLVI five hundred forty six -DXLVII five hundred forty seven -DXLVIII five hundred forty eight -DXLIX five hundred forty nine -DL five hundred fifty -DLI five hundred fifty one -DLII five hundred fifty two -DLIII five hundred fifty three -DLIV five hundred fifty four -DLV five hundred fifty five -DLVI five hundred fifty six -DLVII five hundred fifty seven -DLVIII five hundred fifty eight -DLIX five hundred fifty nine -DLX five hundred sixty -DLXI five hundred sixty one -DLXII five hundred sixty two -DLXIII five hundred sixty three -DLXIV five hundred sixty four -DLXV five hundred sixty five -DLXVI five hundred sixty six -DLXVII five hundred sixty seven -DLXVIII five hundred sixty eight -DLXIX five hundred sixty nine -DLXX five hundred seventy -DLXXI five hundred seventy one -DLXXII five hundred seventy two -DLXXIII five hundred seventy three -DLXXIV five hundred seventy four -DLXXV five hundred seventy five -DLXXVI five hundred seventy six -DLXXVII five hundred seventy seven -DLXXVIII five hundred seventy eight -DLXXIX five hundred seventy nine -DLXXX five hundred eighty -DLXXXI five hundred eighty one -DLXXXII five hundred eighty two -DLXXXIII five hundred eighty three -DLXXXIV five hundred eighty four -DLXXXV five hundred eighty five -DLXXXVI five hundred eighty six -DLXXXVII five hundred eighty seven -DLXXXVIII five hundred eighty eight -DLXXXIX five hundred eighty nine -DXC five hundred ninety -DXCI five hundred ninety one -DXCII five hundred ninety two -DXCIII five hundred ninety three -DXCIV five hundred ninety four -DXCV five hundred ninety five -DXCVI five hundred ninety six -DXCVII five hundred ninety seven -DXCVIII five hundred ninety eight -DXCIX five hundred ninety nine -DC six hundred -DCI six hundred one -DCII six hundred two -DCIII six hundred three -DCIV six hundred four -DCV six hundred five -DCVI six hundred six -DCVII six hundred seven -DCVIII six hundred eight -DCIX six hundred nine -DCX six hundred ten -DCXI six hundred eleven -DCXII six hundred twelve -DCXIII six hundred thirteen -DCXIV six hundred fourteen -DCXV six hundred fifteen -DCXVI six hundred sixteen -DCXVII six hundred seventeen -DCXVIII six hundred eighteen -DCXIX six hundred nineteen -DCXX six hundred twenty -DCXXI six hundred twenty one -DCXXII six hundred twenty two -DCXXIII six hundred twenty three -DCXXIV six hundred twenty four -DCXXV six hundred twenty five -DCXXVI six hundred twenty six -DCXXVII six hundred twenty seven -DCXXVIII six hundred twenty eight -DCXXIX six hundred twenty nine -DCXXX six hundred thirty -DCXXXI six hundred thirty one -DCXXXII six hundred thirty two -DCXXXIII six hundred thirty three -DCXXXIV six hundred thirty four -DCXXXV six hundred thirty five -DCXXXVI six hundred thirty six -DCXXXVII six hundred thirty seven -DCXXXVIII six hundred thirty eight -DCXXXIX six hundred thirty nine -DCXL six hundred forty -DCXLI six hundred forty one -DCXLII six hundred forty two -DCXLIII six hundred forty three -DCXLIV six hundred forty four -DCXLV six hundred forty five -DCXLVI six hundred forty six -DCXLVII six hundred forty seven -DCXLVIII six hundred forty eight -DCXLIX six hundred forty nine -DCL six hundred fifty -DCLI six hundred fifty one -DCLII six hundred fifty two -DCLIII six hundred fifty three -DCLIV six hundred fifty four -DCLV six hundred fifty five -DCLVI six hundred fifty six -DCLVII six hundred fifty seven -DCLVIII six hundred fifty eight -DCLIX six hundred fifty nine -DCLX six hundred sixty -DCLXI six hundred sixty one -DCLXII six hundred sixty two -DCLXIII six hundred sixty three -DCLXIV six hundred sixty four -DCLXV six hundred sixty five -DCLXVI six hundred sixty six -DCLXVII six hundred sixty seven -DCLXVIII six hundred sixty eight -DCLXIX six hundred sixty nine -DCLXX six hundred seventy -DCLXXI six hundred seventy one -DCLXXII six hundred seventy two -DCLXXIII six hundred seventy three -DCLXXIV six hundred seventy four -DCLXXV six hundred seventy five -DCLXXVI six hundred seventy six -DCLXXVII six hundred seventy seven -DCLXXVIII six hundred seventy eight -DCLXXIX six hundred seventy nine -DCLXXX six hundred eighty -DCLXXXI six hundred eighty one -DCLXXXII six hundred eighty two -DCLXXXIII six hundred eighty three -DCLXXXIV six hundred eighty four -DCLXXXV six hundred eighty five -DCLXXXVI six hundred eighty six -DCLXXXVII six hundred eighty seven -DCLXXXVIII six hundred eighty eight -DCLXXXIX six hundred eighty nine -DCXC six hundred ninety -DCXCI six hundred ninety one -DCXCII six hundred ninety two -DCXCIII six hundred ninety three -DCXCIV six hundred ninety four -DCXCV six hundred ninety five -DCXCVI six hundred ninety six -DCXCVII six hundred ninety seven -DCXCVIII six hundred ninety eight -DCXCIX six hundred ninety nine -DCC seven hundred -DCCI seven hundred one -DCCII seven hundred two -DCCIII seven hundred three -DCCIV seven hundred four -DCCV seven hundred five -DCCVI seven hundred six -DCCVII seven hundred seven -DCCVIII seven hundred eight -DCCIX seven hundred nine -DCCX seven hundred ten -DCCXI seven hundred eleven -DCCXII seven hundred twelve -DCCXIII seven hundred thirteen -DCCXIV seven hundred fourteen -DCCXV seven hundred fifteen -DCCXVI seven hundred sixteen -DCCXVII seven hundred seventeen -DCCXVIII seven hundred eighteen -DCCXIX seven hundred nineteen -DCCXX seven hundred twenty -DCCXXI seven hundred twenty one -DCCXXII seven hundred twenty two -DCCXXIII seven hundred twenty three -DCCXXIV seven hundred twenty four -DCCXXV seven hundred twenty five -DCCXXVI seven hundred twenty six -DCCXXVII seven hundred twenty seven -DCCXXVIII seven hundred twenty eight -DCCXXIX seven hundred twenty nine -DCCXXX seven hundred thirty -DCCXXXI seven hundred thirty one -DCCXXXII seven hundred thirty two -DCCXXXIII seven hundred thirty three -DCCXXXIV seven hundred thirty four -DCCXXXV seven hundred thirty five -DCCXXXVI seven hundred thirty six -DCCXXXVII seven hundred thirty seven -DCCXXXVIII seven hundred thirty eight -DCCXXXIX seven hundred thirty nine -DCCXL seven hundred forty -DCCXLI seven hundred forty one -DCCXLII seven hundred forty two -DCCXLIII seven hundred forty three -DCCXLIV seven hundred forty four -DCCXLV seven hundred forty five -DCCXLVI seven hundred forty six -DCCXLVII seven hundred forty seven -DCCXLVIII seven hundred forty eight -DCCXLIX seven hundred forty nine -DCCL seven hundred fifty -DCCLI seven hundred fifty one -DCCLII seven hundred fifty two -DCCLIII seven hundred fifty three -DCCLIV seven hundred fifty four -DCCLV seven hundred fifty five -DCCLVI seven hundred fifty six -DCCLVII seven hundred fifty seven -DCCLVIII seven hundred fifty eight -DCCLIX seven hundred fifty nine -DCCLX seven hundred sixty -DCCLXI seven hundred sixty one -DCCLXII seven hundred sixty two -DCCLXIII seven hundred sixty three -DCCLXIV seven hundred sixty four -DCCLXV seven hundred sixty five -DCCLXVI seven hundred sixty six -DCCLXVII seven hundred sixty seven -DCCLXVIII seven hundred sixty eight -DCCLXIX seven hundred sixty nine -DCCLXX seven hundred seventy -DCCLXXI seven hundred seventy one -DCCLXXII seven hundred seventy two -DCCLXXIII seven hundred seventy three -DCCLXXIV seven hundred seventy four -DCCLXXV seven hundred seventy five -DCCLXXVI seven hundred seventy six -DCCLXXVII seven hundred seventy seven -DCCLXXVIII seven hundred seventy eight -DCCLXXIX seven hundred seventy nine -DCCLXXX seven hundred eighty -DCCLXXXI seven hundred eighty one -DCCLXXXII seven hundred eighty two -DCCLXXXIII seven hundred eighty three -DCCLXXXIV seven hundred eighty four -DCCLXXXV seven hundred eighty five -DCCLXXXVI seven hundred eighty six -DCCLXXXVII seven hundred eighty seven -DCCLXXXVIII seven hundred eighty eight -DCCLXXXIX seven hundred eighty nine -DCCXC seven hundred ninety -DCCXCI seven hundred ninety one -DCCXCII seven hundred ninety two -DCCXCIII seven hundred ninety three -DCCXCIV seven hundred ninety four -DCCXCV seven hundred ninety five -DCCXCVI seven hundred ninety six -DCCXCVII seven hundred ninety seven -DCCXCVIII seven hundred ninety eight -DCCXCIX seven hundred ninety nine -DCCC eight hundred -DCCCI eight hundred one -DCCCII eight hundred two -DCCCIII eight hundred three -DCCCIV eight hundred four -DCCCV eight hundred five -DCCCVI eight hundred six -DCCCVII eight hundred seven -DCCCVIII eight hundred eight -DCCCIX eight hundred nine -DCCCX eight hundred ten -DCCCXI eight hundred eleven -DCCCXII eight hundred twelve -DCCCXIII eight hundred thirteen -DCCCXIV eight hundred fourteen -DCCCXV eight hundred fifteen -DCCCXVI eight hundred sixteen -DCCCXVII eight hundred seventeen -DCCCXVIII eight hundred eighteen -DCCCXIX eight hundred nineteen -DCCCXX eight hundred twenty -DCCCXXI eight hundred twenty one -DCCCXXII eight hundred twenty two -DCCCXXIII eight hundred twenty three -DCCCXXIV eight hundred twenty four -DCCCXXV eight hundred twenty five -DCCCXXVI eight hundred twenty six -DCCCXXVII eight hundred twenty seven -DCCCXXVIII eight hundred twenty eight -DCCCXXIX eight hundred twenty nine -DCCCXXX eight hundred thirty -DCCCXXXI eight hundred thirty one -DCCCXXXII eight hundred thirty two -DCCCXXXIII eight hundred thirty three -DCCCXXXIV eight hundred thirty four -DCCCXXXV eight hundred thirty five -DCCCXXXVI eight hundred thirty six -DCCCXXXVII eight hundred thirty seven -DCCCXXXVIII eight hundred thirty eight -DCCCXXXIX eight hundred thirty nine -DCCCXL eight hundred forty -DCCCXLI eight hundred forty one -DCCCXLII eight hundred forty two -DCCCXLIII eight hundred forty three -DCCCXLIV eight hundred forty four -DCCCXLV eight hundred forty five -DCCCXLVI eight hundred forty six -DCCCXLVII eight hundred forty seven -DCCCXLVIII eight hundred forty eight -DCCCXLIX eight hundred forty nine -DCCCL eight hundred fifty -DCCCLI eight hundred fifty one -DCCCLII eight hundred fifty two -DCCCLIII eight hundred fifty three -DCCCLIV eight hundred fifty four -DCCCLV eight hundred fifty five -DCCCLVI eight hundred fifty six -DCCCLVII eight hundred fifty seven -DCCCLVIII eight hundred fifty eight -DCCCLIX eight hundred fifty nine -DCCCLX eight hundred sixty -DCCCLXI eight hundred sixty one -DCCCLXII eight hundred sixty two -DCCCLXIII eight hundred sixty three -DCCCLXIV eight hundred sixty four -DCCCLXV eight hundred sixty five -DCCCLXVI eight hundred sixty six -DCCCLXVII eight hundred sixty seven -DCCCLXVIII eight hundred sixty eight -DCCCLXIX eight hundred sixty nine -DCCCLXX eight hundred seventy -DCCCLXXI eight hundred seventy one -DCCCLXXII eight hundred seventy two -DCCCLXXIII eight hundred seventy three -DCCCLXXIV eight hundred seventy four -DCCCLXXV eight hundred seventy five -DCCCLXXVI eight hundred seventy six -DCCCLXXVII eight hundred seventy seven -DCCCLXXVIII eight hundred seventy eight -DCCCLXXIX eight hundred seventy nine -DCCCLXXX eight hundred eighty -DCCCLXXXI eight hundred eighty one -DCCCLXXXII eight hundred eighty two -DCCCLXXXIII eight hundred eighty three -DCCCLXXXIV eight hundred eighty four -DCCCLXXXV eight hundred eighty five -DCCCLXXXVI eight hundred eighty six -DCCCLXXXVII eight hundred eighty seven -DCCCLXXXVIII eight hundred eighty eight -DCCCLXXXIX eight hundred eighty nine -DCCCXC eight hundred ninety -DCCCXCI eight hundred ninety one -DCCCXCII eight hundred ninety two -DCCCXCIII eight hundred ninety three -DCCCXCIV eight hundred ninety four -DCCCXCV eight hundred ninety five -DCCCXCVI eight hundred ninety six -DCCCXCVII eight hundred ninety seven -DCCCXCVIII eight hundred ninety eight -DCCCXCIX eight hundred ninety nine -CM nine hundred -CMI nine hundred one -CMII nine hundred two -CMIII nine hundred three -CMIV nine hundred four -CMV nine hundred five -CMVI nine hundred six -CMVII nine hundred seven -CMVIII nine hundred eight -CMIX nine hundred nine -CMX nine hundred ten -CMXI nine hundred eleven -CMXII nine hundred twelve -CMXIII nine hundred thirteen -CMXIV nine hundred fourteen -CMXV nine hundred fifteen -CMXVI nine hundred sixteen -CMXVII nine hundred seventeen -CMXVIII nine hundred eighteen -CMXIX nine hundred nineteen -CMXX nine hundred twenty -CMXXI nine hundred twenty one -CMXXII nine hundred twenty two -CMXXIII nine hundred twenty three -CMXXIV nine hundred twenty four -CMXXV nine hundred twenty five -CMXXVI nine hundred twenty six -CMXXVII nine hundred twenty seven -CMXXVIII nine hundred twenty eight -CMXXIX nine hundred twenty nine -CMXXX nine hundred thirty -CMXXXI nine hundred thirty one -CMXXXII nine hundred thirty two -CMXXXIII nine hundred thirty three -CMXXXIV nine hundred thirty four -CMXXXV nine hundred thirty five -CMXXXVI nine hundred thirty six -CMXXXVII nine hundred thirty seven -CMXXXVIII nine hundred thirty eight -CMXXXIX nine hundred thirty nine -CMXL nine hundred forty -CMXLI nine hundred forty one -CMXLII nine hundred forty two -CMXLIII nine hundred forty three -CMXLIV nine hundred forty four -CMXLV nine hundred forty five -CMXLVI nine hundred forty six -CMXLVII nine hundred forty seven -CMXLVIII nine hundred forty eight -CMXLIX nine hundred forty nine -CML nine hundred fifty -CMLI nine hundred fifty one -CMLII nine hundred fifty two -CMLIII nine hundred fifty three -CMLIV nine hundred fifty four -CMLV nine hundred fifty five -CMLVI nine hundred fifty six -CMLVII nine hundred fifty seven -CMLVIII nine hundred fifty eight -CMLIX nine hundred fifty nine -CMLX nine hundred sixty -CMLXI nine hundred sixty one -CMLXII nine hundred sixty two -CMLXIII nine hundred sixty three -CMLXIV nine hundred sixty four -CMLXV nine hundred sixty five -CMLXVI nine hundred sixty six -CMLXVII nine hundred sixty seven -CMLXVIII nine hundred sixty eight -CMLXIX nine hundred sixty nine -CMLXX nine hundred seventy -CMLXXI nine hundred seventy one -CMLXXII nine hundred seventy two -CMLXXIII nine hundred seventy three -CMLXXIV nine hundred seventy four -CMLXXV nine hundred seventy five -CMLXXVI nine hundred seventy six -CMLXXVII nine hundred seventy seven -CMLXXVIII nine hundred seventy eight -CMLXXIX nine hundred seventy nine -CMLXXX nine hundred eighty -CMLXXXI nine hundred eighty one -CMLXXXII nine hundred eighty two -CMLXXXIII nine hundred eighty three -CMLXXXIV nine hundred eighty four -CMLXXXV nine hundred eighty five -CMLXXXVI nine hundred eighty six -CMLXXXVII nine hundred eighty seven -CMLXXXVIII nine hundred eighty eight -CMLXXXIX nine hundred eighty nine -CMXC nine hundred ninety -CMXCI nine hundred ninety one -CMXCII nine hundred ninety two -CMXCIII nine hundred ninety three -CMXCIV nine hundred ninety four -CMXCV nine hundred ninety five -CMXCVI nine hundred ninety six -CMXCVII nine hundred ninety seven -CMXCVIII nine hundred ninety eight -CMXCIX nine hundred ninety nine -M one thousand -MI one thousand one -MII one thousand two -MIII one thousand three -MIV one thousand four -MV one thousand five -MVI one thousand six -MVII one thousand seven -MVIII one thousand eight -MIX one thousand nine -MX one thousand ten -MXI one thousand eleven -MXII one thousand twelve -MXIII one thousand thirteen -MXIV one thousand fourteen -MXV one thousand fifteen -MXVI one thousand sixteen -MXVII one thousand seventeen -MXVIII one thousand eighteen -MXIX one thousand nineteen -MXX one thousand twenty -MXXI one thousand twenty one -MXXII one thousand twenty two -MXXIII one thousand twenty three -MXXIV one thousand twenty four -MXXV one thousand twenty five -MXXVI one thousand twenty six -MXXVII one thousand twenty seven -MXXVIII one thousand twenty eight -MXXIX one thousand twenty nine -MXXX one thousand thirty -MXXXI one thousand thirty one -MXXXII one thousand thirty two -MXXXIII one thousand thirty three -MXXXIV one thousand thirty four -MXXXV one thousand thirty five -MXXXVI one thousand thirty six -MXXXVII one thousand thirty seven -MXXXVIII one thousand thirty eight -MXXXIX one thousand thirty nine -MXL one thousand forty -MXLI one thousand forty one -MXLII one thousand forty two -MXLIII one thousand forty three -MXLIV one thousand forty four -MXLV one thousand forty five -MXLVI one thousand forty six -MXLVII one thousand forty seven -MXLVIII one thousand forty eight -MXLIX one thousand forty nine -ML one thousand fifty -MLI one thousand fifty one -MLII one thousand fifty two -MLIII one thousand fifty three -MLIV one thousand fifty four -MLV one thousand fifty five -MLVI one thousand fifty six -MLVII one thousand fifty seven -MLVIII one thousand fifty eight -MLIX one thousand fifty nine -MLX one thousand sixty -MLXI one thousand sixty one -MLXII one thousand sixty two -MLXIII one thousand sixty three -MLXIV one thousand sixty four -MLXV one thousand sixty five -MLXVI one thousand sixty six -MLXVII one thousand sixty seven -MLXVIII one thousand sixty eight -MLXIX one thousand sixty nine -MLXX one thousand seventy -MLXXI one thousand seventy one -MLXXII one thousand seventy two -MLXXIII one thousand seventy three -MLXXIV one thousand seventy four -MLXXV one thousand seventy five -MLXXVI one thousand seventy six -MLXXVII one thousand seventy seven -MLXXVIII one thousand seventy eight -MLXXIX one thousand seventy nine -MLXXX one thousand eighty -MLXXXI one thousand eighty one -MLXXXII one thousand eighty two -MLXXXIII one thousand eighty three -MLXXXIV one thousand eighty four -MLXXXV one thousand eighty five -MLXXXVI one thousand eighty six -MLXXXVII one thousand eighty seven -MLXXXVIII one thousand eighty eight -MLXXXIX one thousand eighty nine -MXC one thousand ninety -MXCI one thousand ninety one -MXCII one thousand ninety two -MXCIII one thousand ninety three -MXCIV one thousand ninety four -MXCV one thousand ninety five -MXCVI one thousand ninety six -MXCVII one thousand ninety seven -MXCVIII one thousand ninety eight -MXCIX one thousand ninety nine -MC one thousand, one hundred -MCI one thousand, one hundred one -MCII one thousand, one hundred two -MCIII one thousand, one hundred three -MCIV one thousand, one hundred four -MCV one thousand, one hundred five -MCVI one thousand, one hundred six -MCVII one thousand, one hundred seven -MCVIII one thousand, one hundred eight -MCIX one thousand, one hundred nine -MCX one thousand, one hundred ten -MCXI one thousand, one hundred eleven -MCXII one thousand, one hundred twelve -MCXIII one thousand, one hundred thirteen -MCXIV one thousand, one hundred fourteen -MCXV one thousand, one hundred fifteen -MCXVI one thousand, one hundred sixteen -MCXVII one thousand, one hundred seventeen -MCXVIII one thousand, one hundred eighteen -MCXIX one thousand, one hundred nineteen -MCXX one thousand, one hundred twenty -MCXXI one thousand, one hundred twenty one -MCXXII one thousand, one hundred twenty two -MCXXIII one thousand, one hundred twenty three -MCXXIV one thousand, one hundred twenty four -MCXXV one thousand, one hundred twenty five -MCXXVI one thousand, one hundred twenty six -MCXXVII one thousand, one hundred twenty seven -MCXXVIII one thousand, one hundred twenty eight -MCXXIX one thousand, one hundred twenty nine -MCXXX one thousand, one hundred thirty -MCXXXI one thousand, one hundred thirty one -MCXXXII one thousand, one hundred thirty two -MCXXXIII one thousand, one hundred thirty three -MCXXXIV one thousand, one hundred thirty four -MCXXXV one thousand, one hundred thirty five -MCXXXVI one thousand, one hundred thirty six -MCXXXVII one thousand, one hundred thirty seven -MCXXXVIII one thousand, one hundred thirty eight -MCXXXIX one thousand, one hundred thirty nine -MCXL one thousand, one hundred forty -MCXLI one thousand, one hundred forty one -MCXLII one thousand, one hundred forty two -MCXLIII one thousand, one hundred forty three -MCXLIV one thousand, one hundred forty four -MCXLV one thousand, one hundred forty five -MCXLVI one thousand, one hundred forty six -MCXLVII one thousand, one hundred forty seven -MCXLVIII one thousand, one hundred forty eight -MCXLIX one thousand, one hundred forty nine -MCL one thousand, one hundred fifty -MCLI one thousand, one hundred fifty one -MCLII one thousand, one hundred fifty two -MCLIII one thousand, one hundred fifty three -MCLIV one thousand, one hundred fifty four -MCLV one thousand, one hundred fifty five -MCLVI one thousand, one hundred fifty six -MCLVII one thousand, one hundred fifty seven -MCLVIII one thousand, one hundred fifty eight -MCLIX one thousand, one hundred fifty nine -MCLX one thousand, one hundred sixty -MCLXI one thousand, one hundred sixty one -MCLXII one thousand, one hundred sixty two -MCLXIII one thousand, one hundred sixty three -MCLXIV one thousand, one hundred sixty four -MCLXV one thousand, one hundred sixty five -MCLXVI one thousand, one hundred sixty six -MCLXVII one thousand, one hundred sixty seven -MCLXVIII one thousand, one hundred sixty eight -MCLXIX one thousand, one hundred sixty nine -MCLXX one thousand, one hundred seventy -MCLXXI one thousand, one hundred seventy one -MCLXXII one thousand, one hundred seventy two -MCLXXIII one thousand, one hundred seventy three -MCLXXIV one thousand, one hundred seventy four -MCLXXV one thousand, one hundred seventy five -MCLXXVI one thousand, one hundred seventy six -MCLXXVII one thousand, one hundred seventy seven -MCLXXVIII one thousand, one hundred seventy eight -MCLXXIX one thousand, one hundred seventy nine -MCLXXX one thousand, one hundred eighty -MCLXXXI one thousand, one hundred eighty one -MCLXXXII one thousand, one hundred eighty two -MCLXXXIII one thousand, one hundred eighty three -MCLXXXIV one thousand, one hundred eighty four -MCLXXXV one thousand, one hundred eighty five -MCLXXXVI one thousand, one hundred eighty six -MCLXXXVII one thousand, one hundred eighty seven -MCLXXXVIII one thousand, one hundred eighty eight -MCLXXXIX one thousand, one hundred eighty nine -MCXC one thousand, one hundred ninety -MCXCI one thousand, one hundred ninety one -MCXCII one thousand, one hundred ninety two -MCXCIII one thousand, one hundred ninety three -MCXCIV one thousand, one hundred ninety four -MCXCV one thousand, one hundred ninety five -MCXCVI one thousand, one hundred ninety six -MCXCVII one thousand, one hundred ninety seven -MCXCVIII one thousand, one hundred ninety eight -MCXCIX one thousand, one hundred ninety nine -MCC one thousand, two hundred -MCCI one thousand, two hundred one -MCCII one thousand, two hundred two -MCCIII one thousand, two hundred three -MCCIV one thousand, two hundred four -MCCV one thousand, two hundred five -MCCVI one thousand, two hundred six -MCCVII one thousand, two hundred seven -MCCVIII one thousand, two hundred eight -MCCIX one thousand, two hundred nine -MCCX one thousand, two hundred ten -MCCXI one thousand, two hundred eleven -MCCXII one thousand, two hundred twelve -MCCXIII one thousand, two hundred thirteen -MCCXIV one thousand, two hundred fourteen -MCCXV one thousand, two hundred fifteen -MCCXVI one thousand, two hundred sixteen -MCCXVII one thousand, two hundred seventeen -MCCXVIII one thousand, two hundred eighteen -MCCXIX one thousand, two hundred nineteen -MCCXX one thousand, two hundred twenty -MCCXXI one thousand, two hundred twenty one -MCCXXII one thousand, two hundred twenty two -MCCXXIII one thousand, two hundred twenty three -MCCXXIV one thousand, two hundred twenty four -MCCXXV one thousand, two hundred twenty five -MCCXXVI one thousand, two hundred twenty six -MCCXXVII one thousand, two hundred twenty seven -MCCXXVIII one thousand, two hundred twenty eight -MCCXXIX one thousand, two hundred twenty nine -MCCXXX one thousand, two hundred thirty -MCCXXXI one thousand, two hundred thirty one -MCCXXXII one thousand, two hundred thirty two -MCCXXXIII one thousand, two hundred thirty three -MCCXXXIV one thousand, two hundred thirty four -MCCXXXV one thousand, two hundred thirty five -MCCXXXVI one thousand, two hundred thirty six -MCCXXXVII one thousand, two hundred thirty seven -MCCXXXVIII one thousand, two hundred thirty eight -MCCXXXIX one thousand, two hundred thirty nine -MCCXL one thousand, two hundred forty -MCCXLI one thousand, two hundred forty one -MCCXLII one thousand, two hundred forty two -MCCXLIII one thousand, two hundred forty three -MCCXLIV one thousand, two hundred forty four -MCCXLV one thousand, two hundred forty five -MCCXLVI one thousand, two hundred forty six -MCCXLVII one thousand, two hundred forty seven -MCCXLVIII one thousand, two hundred forty eight -MCCXLIX one thousand, two hundred forty nine -MCCL one thousand, two hundred fifty -MCCLI one thousand, two hundred fifty one -MCCLII one thousand, two hundred fifty two -MCCLIII one thousand, two hundred fifty three -MCCLIV one thousand, two hundred fifty four -MCCLV one thousand, two hundred fifty five -MCCLVI one thousand, two hundred fifty six -MCCLVII one thousand, two hundred fifty seven -MCCLVIII one thousand, two hundred fifty eight -MCCLIX one thousand, two hundred fifty nine -MCCLX one thousand, two hundred sixty -MCCLXI one thousand, two hundred sixty one -MCCLXII one thousand, two hundred sixty two -MCCLXIII one thousand, two hundred sixty three -MCCLXIV one thousand, two hundred sixty four -MCCLXV one thousand, two hundred sixty five -MCCLXVI one thousand, two hundred sixty six -MCCLXVII one thousand, two hundred sixty seven -MCCLXVIII one thousand, two hundred sixty eight -MCCLXIX one thousand, two hundred sixty nine -MCCLXX one thousand, two hundred seventy -MCCLXXI one thousand, two hundred seventy one -MCCLXXII one thousand, two hundred seventy two -MCCLXXIII one thousand, two hundred seventy three -MCCLXXIV one thousand, two hundred seventy four -MCCLXXV one thousand, two hundred seventy five -MCCLXXVI one thousand, two hundred seventy six -MCCLXXVII one thousand, two hundred seventy seven -MCCLXXVIII one thousand, two hundred seventy eight -MCCLXXIX one thousand, two hundred seventy nine -MCCLXXX one thousand, two hundred eighty -MCCLXXXI one thousand, two hundred eighty one -MCCLXXXII one thousand, two hundred eighty two -MCCLXXXIII one thousand, two hundred eighty three -MCCLXXXIV one thousand, two hundred eighty four -MCCLXXXV one thousand, two hundred eighty five -MCCLXXXVI one thousand, two hundred eighty six -MCCLXXXVII one thousand, two hundred eighty seven -MCCLXXXVIII one thousand, two hundred eighty eight -MCCLXXXIX one thousand, two hundred eighty nine -MCCXC one thousand, two hundred ninety -MCCXCI one thousand, two hundred ninety one -MCCXCII one thousand, two hundred ninety two -MCCXCIII one thousand, two hundred ninety three -MCCXCIV one thousand, two hundred ninety four -MCCXCV one thousand, two hundred ninety five -MCCXCVI one thousand, two hundred ninety six -MCCXCVII one thousand, two hundred ninety seven -MCCXCVIII one thousand, two hundred ninety eight -MCCXCIX one thousand, two hundred ninety nine -MCCC one thousand, three hundred -MCCCI one thousand, three hundred one -MCCCII one thousand, three hundred two -MCCCIII one thousand, three hundred three -MCCCIV one thousand, three hundred four -MCCCV one thousand, three hundred five -MCCCVI one thousand, three hundred six -MCCCVII one thousand, three hundred seven -MCCCVIII one thousand, three hundred eight -MCCCIX one thousand, three hundred nine -MCCCX one thousand, three hundred ten -MCCCXI one thousand, three hundred eleven -MCCCXII one thousand, three hundred twelve -MCCCXIII one thousand, three hundred thirteen -MCCCXIV one thousand, three hundred fourteen -MCCCXV one thousand, three hundred fifteen -MCCCXVI one thousand, three hundred sixteen -MCCCXVII one thousand, three hundred seventeen -MCCCXVIII one thousand, three hundred eighteen -MCCCXIX one thousand, three hundred nineteen -MCCCXX one thousand, three hundred twenty -MCCCXXI one thousand, three hundred twenty one -MCCCXXII one thousand, three hundred twenty two -MCCCXXIII one thousand, three hundred twenty three -MCCCXXIV one thousand, three hundred twenty four -MCCCXXV one thousand, three hundred twenty five -MCCCXXVI one thousand, three hundred twenty six -MCCCXXVII one thousand, three hundred twenty seven -MCCCXXVIII one thousand, three hundred twenty eight -MCCCXXIX one thousand, three hundred twenty nine -MCCCXXX one thousand, three hundred thirty -MCCCXXXI one thousand, three hundred thirty one -MCCCXXXII one thousand, three hundred thirty two -MCCCXXXIII one thousand, three hundred thirty three -MCCCXXXIV one thousand, three hundred thirty four -MCCCXXXV one thousand, three hundred thirty five -MCCCXXXVI one thousand, three hundred thirty six -MCCCXXXVII one thousand, three hundred thirty seven -MCCCXXXVIII one thousand, three hundred thirty eight -MCCCXXXIX one thousand, three hundred thirty nine -MCCCXL one thousand, three hundred forty -MCCCXLI one thousand, three hundred forty one -MCCCXLII one thousand, three hundred forty two -MCCCXLIII one thousand, three hundred forty three -MCCCXLIV one thousand, three hundred forty four -MCCCXLV one thousand, three hundred forty five -MCCCXLVI one thousand, three hundred forty six -MCCCXLVII one thousand, three hundred forty seven -MCCCXLVIII one thousand, three hundred forty eight -MCCCXLIX one thousand, three hundred forty nine -MCCCL one thousand, three hundred fifty -MCCCLI one thousand, three hundred fifty one -MCCCLII one thousand, three hundred fifty two -MCCCLIII one thousand, three hundred fifty three -MCCCLIV one thousand, three hundred fifty four -MCCCLV one thousand, three hundred fifty five -MCCCLVI one thousand, three hundred fifty six -MCCCLVII one thousand, three hundred fifty seven -MCCCLVIII one thousand, three hundred fifty eight -MCCCLIX one thousand, three hundred fifty nine -MCCCLX one thousand, three hundred sixty -MCCCLXI one thousand, three hundred sixty one -MCCCLXII one thousand, three hundred sixty two -MCCCLXIII one thousand, three hundred sixty three -MCCCLXIV one thousand, three hundred sixty four -MCCCLXV one thousand, three hundred sixty five -MCCCLXVI one thousand, three hundred sixty six -MCCCLXVII one thousand, three hundred sixty seven -MCCCLXVIII one thousand, three hundred sixty eight -MCCCLXIX one thousand, three hundred sixty nine -MCCCLXX one thousand, three hundred seventy -MCCCLXXI one thousand, three hundred seventy one -MCCCLXXII one thousand, three hundred seventy two -MCCCLXXIII one thousand, three hundred seventy three -MCCCLXXIV one thousand, three hundred seventy four -MCCCLXXV one thousand, three hundred seventy five -MCCCLXXVI one thousand, three hundred seventy six -MCCCLXXVII one thousand, three hundred seventy seven -MCCCLXXVIII one thousand, three hundred seventy eight -MCCCLXXIX one thousand, three hundred seventy nine -MCCCLXXX one thousand, three hundred eighty -MCCCLXXXI one thousand, three hundred eighty one -MCCCLXXXII one thousand, three hundred eighty two -MCCCLXXXIII one thousand, three hundred eighty three -MCCCLXXXIV one thousand, three hundred eighty four -MCCCLXXXV one thousand, three hundred eighty five -MCCCLXXXVI one thousand, three hundred eighty six -MCCCLXXXVII one thousand, three hundred eighty seven -MCCCLXXXVIII one thousand, three hundred eighty eight -MCCCLXXXIX one thousand, three hundred eighty nine -MCCCXC one thousand, three hundred ninety -MCCCXCI one thousand, three hundred ninety one -MCCCXCII one thousand, three hundred ninety two -MCCCXCIII one thousand, three hundred ninety three -MCCCXCIV one thousand, three hundred ninety four -MCCCXCV one thousand, three hundred ninety five -MCCCXCVI one thousand, three hundred ninety six -MCCCXCVII one thousand, three hundred ninety seven -MCCCXCVIII one thousand, three hundred ninety eight -MCCCXCIX one thousand, three hundred ninety nine -MCD one thousand, four hundred -MCDI one thousand, four hundred one -MCDII one thousand, four hundred two -MCDIII one thousand, four hundred three -MCDIV one thousand, four hundred four -MCDV one thousand, four hundred five -MCDVI one thousand, four hundred six -MCDVII one thousand, four hundred seven -MCDVIII one thousand, four hundred eight -MCDIX one thousand, four hundred nine -MCDX one thousand, four hundred ten -MCDXI one thousand, four hundred eleven -MCDXII one thousand, four hundred twelve -MCDXIII one thousand, four hundred thirteen -MCDXIV one thousand, four hundred fourteen -MCDXV one thousand, four hundred fifteen -MCDXVI one thousand, four hundred sixteen -MCDXVII one thousand, four hundred seventeen -MCDXVIII one thousand, four hundred eighteen -MCDXIX one thousand, four hundred nineteen -MCDXX one thousand, four hundred twenty -MCDXXI one thousand, four hundred twenty one -MCDXXII one thousand, four hundred twenty two -MCDXXIII one thousand, four hundred twenty three -MCDXXIV one thousand, four hundred twenty four -MCDXXV one thousand, four hundred twenty five -MCDXXVI one thousand, four hundred twenty six -MCDXXVII one thousand, four hundred twenty seven -MCDXXVIII one thousand, four hundred twenty eight -MCDXXIX one thousand, four hundred twenty nine -MCDXXX one thousand, four hundred thirty -MCDXXXI one thousand, four hundred thirty one -MCDXXXII one thousand, four hundred thirty two -MCDXXXIII one thousand, four hundred thirty three -MCDXXXIV one thousand, four hundred thirty four -MCDXXXV one thousand, four hundred thirty five -MCDXXXVI one thousand, four hundred thirty six -MCDXXXVII one thousand, four hundred thirty seven -MCDXXXVIII one thousand, four hundred thirty eight -MCDXXXIX one thousand, four hundred thirty nine -MCDXL one thousand, four hundred forty -MCDXLI one thousand, four hundred forty one -MCDXLII one thousand, four hundred forty two -MCDXLIII one thousand, four hundred forty three -MCDXLIV one thousand, four hundred forty four -MCDXLV one thousand, four hundred forty five -MCDXLVI one thousand, four hundred forty six -MCDXLVII one thousand, four hundred forty seven -MCDXLVIII one thousand, four hundred forty eight -MCDXLIX one thousand, four hundred forty nine -MCDL one thousand, four hundred fifty -MCDLI one thousand, four hundred fifty one -MCDLII one thousand, four hundred fifty two -MCDLIII one thousand, four hundred fifty three -MCDLIV one thousand, four hundred fifty four -MCDLV one thousand, four hundred fifty five -MCDLVI one thousand, four hundred fifty six -MCDLVII one thousand, four hundred fifty seven -MCDLVIII one thousand, four hundred fifty eight -MCDLIX one thousand, four hundred fifty nine -MCDLX one thousand, four hundred sixty -MCDLXI one thousand, four hundred sixty one -MCDLXII one thousand, four hundred sixty two -MCDLXIII one thousand, four hundred sixty three -MCDLXIV one thousand, four hundred sixty four -MCDLXV one thousand, four hundred sixty five -MCDLXVI one thousand, four hundred sixty six -MCDLXVII one thousand, four hundred sixty seven -MCDLXVIII one thousand, four hundred sixty eight -MCDLXIX one thousand, four hundred sixty nine -MCDLXX one thousand, four hundred seventy -MCDLXXI one thousand, four hundred seventy one -MCDLXXII one thousand, four hundred seventy two -MCDLXXIII one thousand, four hundred seventy three -MCDLXXIV one thousand, four hundred seventy four -MCDLXXV one thousand, four hundred seventy five -MCDLXXVI one thousand, four hundred seventy six -MCDLXXVII one thousand, four hundred seventy seven -MCDLXXVIII one thousand, four hundred seventy eight -MCDLXXIX one thousand, four hundred seventy nine -MCDLXXX one thousand, four hundred eighty -MCDLXXXI one thousand, four hundred eighty one -MCDLXXXII one thousand, four hundred eighty two -MCDLXXXIII one thousand, four hundred eighty three -MCDLXXXIV one thousand, four hundred eighty four -MCDLXXXV one thousand, four hundred eighty five -MCDLXXXVI one thousand, four hundred eighty six -MCDLXXXVII one thousand, four hundred eighty seven -MCDLXXXVIII one thousand, four hundred eighty eight -MCDLXXXIX one thousand, four hundred eighty nine -MCDXC one thousand, four hundred ninety -MCDXCI one thousand, four hundred ninety one -MCDXCII one thousand, four hundred ninety two -MCDXCIII one thousand, four hundred ninety three -MCDXCIV one thousand, four hundred ninety four -MCDXCV one thousand, four hundred ninety five -MCDXCVI one thousand, four hundred ninety six -MCDXCVII one thousand, four hundred ninety seven -MCDXCVIII one thousand, four hundred ninety eight -MCDXCIX one thousand, four hundred ninety nine -MD one thousand, five hundred -MDI one thousand, five hundred one -MDII one thousand, five hundred two -MDIII one thousand, five hundred three -MDIV one thousand, five hundred four -MDV one thousand, five hundred five -MDVI one thousand, five hundred six -MDVII one thousand, five hundred seven -MDVIII one thousand, five hundred eight -MDIX one thousand, five hundred nine -MDX one thousand, five hundred ten -MDXI one thousand, five hundred eleven -MDXII one thousand, five hundred twelve -MDXIII one thousand, five hundred thirteen -MDXIV one thousand, five hundred fourteen -MDXV one thousand, five hundred fifteen -MDXVI one thousand, five hundred sixteen -MDXVII one thousand, five hundred seventeen -MDXVIII one thousand, five hundred eighteen -MDXIX one thousand, five hundred nineteen -MDXX one thousand, five hundred twenty -MDXXI one thousand, five hundred twenty one -MDXXII one thousand, five hundred twenty two -MDXXIII one thousand, five hundred twenty three -MDXXIV one thousand, five hundred twenty four -MDXXV one thousand, five hundred twenty five -MDXXVI one thousand, five hundred twenty six -MDXXVII one thousand, five hundred twenty seven -MDXXVIII one thousand, five hundred twenty eight -MDXXIX one thousand, five hundred twenty nine -MDXXX one thousand, five hundred thirty -MDXXXI one thousand, five hundred thirty one -MDXXXII one thousand, five hundred thirty two -MDXXXIII one thousand, five hundred thirty three -MDXXXIV one thousand, five hundred thirty four -MDXXXV one thousand, five hundred thirty five -MDXXXVI one thousand, five hundred thirty six -MDXXXVII one thousand, five hundred thirty seven -MDXXXVIII one thousand, five hundred thirty eight -MDXXXIX one thousand, five hundred thirty nine -MDXL one thousand, five hundred forty -MDXLI one thousand, five hundred forty one -MDXLII one thousand, five hundred forty two -MDXLIII one thousand, five hundred forty three -MDXLIV one thousand, five hundred forty four -MDXLV one thousand, five hundred forty five -MDXLVI one thousand, five hundred forty six -MDXLVII one thousand, five hundred forty seven -MDXLVIII one thousand, five hundred forty eight -MDXLIX one thousand, five hundred forty nine -MDL one thousand, five hundred fifty -MDLI one thousand, five hundred fifty one -MDLII one thousand, five hundred fifty two -MDLIII one thousand, five hundred fifty three -MDLIV one thousand, five hundred fifty four -MDLV one thousand, five hundred fifty five -MDLVI one thousand, five hundred fifty six -MDLVII one thousand, five hundred fifty seven -MDLVIII one thousand, five hundred fifty eight -MDLIX one thousand, five hundred fifty nine -MDLX one thousand, five hundred sixty -MDLXI one thousand, five hundred sixty one -MDLXII one thousand, five hundred sixty two -MDLXIII one thousand, five hundred sixty three -MDLXIV one thousand, five hundred sixty four -MDLXV one thousand, five hundred sixty five -MDLXVI one thousand, five hundred sixty six -MDLXVII one thousand, five hundred sixty seven -MDLXVIII one thousand, five hundred sixty eight -MDLXIX one thousand, five hundred sixty nine -MDLXX one thousand, five hundred seventy -MDLXXI one thousand, five hundred seventy one -MDLXXII one thousand, five hundred seventy two -MDLXXIII one thousand, five hundred seventy three -MDLXXIV one thousand, five hundred seventy four -MDLXXV one thousand, five hundred seventy five -MDLXXVI one thousand, five hundred seventy six -MDLXXVII one thousand, five hundred seventy seven -MDLXXVIII one thousand, five hundred seventy eight -MDLXXIX one thousand, five hundred seventy nine -MDLXXX one thousand, five hundred eighty -MDLXXXI one thousand, five hundred eighty one -MDLXXXII one thousand, five hundred eighty two -MDLXXXIII one thousand, five hundred eighty three -MDLXXXIV one thousand, five hundred eighty four -MDLXXXV one thousand, five hundred eighty five -MDLXXXVI one thousand, five hundred eighty six -MDLXXXVII one thousand, five hundred eighty seven -MDLXXXVIII one thousand, five hundred eighty eight -MDLXXXIX one thousand, five hundred eighty nine -MDXC one thousand, five hundred ninety -MDXCI one thousand, five hundred ninety one -MDXCII one thousand, five hundred ninety two -MDXCIII one thousand, five hundred ninety three -MDXCIV one thousand, five hundred ninety four -MDXCV one thousand, five hundred ninety five -MDXCVI one thousand, five hundred ninety six -MDXCVII one thousand, five hundred ninety seven -MDXCVIII one thousand, five hundred ninety eight -MDXCIX one thousand, five hundred ninety nine -MDC one thousand, six hundred -MDCI one thousand, six hundred one -MDCII one thousand, six hundred two -MDCIII one thousand, six hundred three -MDCIV one thousand, six hundred four -MDCV one thousand, six hundred five -MDCVI one thousand, six hundred six -MDCVII one thousand, six hundred seven -MDCVIII one thousand, six hundred eight -MDCIX one thousand, six hundred nine -MDCX one thousand, six hundred ten -MDCXI one thousand, six hundred eleven -MDCXII one thousand, six hundred twelve -MDCXIII one thousand, six hundred thirteen -MDCXIV one thousand, six hundred fourteen -MDCXV one thousand, six hundred fifteen -MDCXVI one thousand, six hundred sixteen -MDCXVII one thousand, six hundred seventeen -MDCXVIII one thousand, six hundred eighteen -MDCXIX one thousand, six hundred nineteen -MDCXX one thousand, six hundred twenty -MDCXXI one thousand, six hundred twenty one -MDCXXII one thousand, six hundred twenty two -MDCXXIII one thousand, six hundred twenty three -MDCXXIV one thousand, six hundred twenty four -MDCXXV one thousand, six hundred twenty five -MDCXXVI one thousand, six hundred twenty six -MDCXXVII one thousand, six hundred twenty seven -MDCXXVIII one thousand, six hundred twenty eight -MDCXXIX one thousand, six hundred twenty nine -MDCXXX one thousand, six hundred thirty -MDCXXXI one thousand, six hundred thirty one -MDCXXXII one thousand, six hundred thirty two -MDCXXXIII one thousand, six hundred thirty three -MDCXXXIV one thousand, six hundred thirty four -MDCXXXV one thousand, six hundred thirty five -MDCXXXVI one thousand, six hundred thirty six -MDCXXXVII one thousand, six hundred thirty seven -MDCXXXVIII one thousand, six hundred thirty eight -MDCXXXIX one thousand, six hundred thirty nine -MDCXL one thousand, six hundred forty -MDCXLI one thousand, six hundred forty one -MDCXLII one thousand, six hundred forty two -MDCXLIII one thousand, six hundred forty three -MDCXLIV one thousand, six hundred forty four -MDCXLV one thousand, six hundred forty five -MDCXLVI one thousand, six hundred forty six -MDCXLVII one thousand, six hundred forty seven -MDCXLVIII one thousand, six hundred forty eight -MDCXLIX one thousand, six hundred forty nine -MDCL one thousand, six hundred fifty -MDCLI one thousand, six hundred fifty one -MDCLII one thousand, six hundred fifty two -MDCLIII one thousand, six hundred fifty three -MDCLIV one thousand, six hundred fifty four -MDCLV one thousand, six hundred fifty five -MDCLVI one thousand, six hundred fifty six -MDCLVII one thousand, six hundred fifty seven -MDCLVIII one thousand, six hundred fifty eight -MDCLIX one thousand, six hundred fifty nine -MDCLX one thousand, six hundred sixty -MDCLXI one thousand, six hundred sixty one -MDCLXII one thousand, six hundred sixty two -MDCLXIII one thousand, six hundred sixty three -MDCLXIV one thousand, six hundred sixty four -MDCLXV one thousand, six hundred sixty five -MDCLXVI one thousand, six hundred sixty six -MDCLXVII one thousand, six hundred sixty seven -MDCLXVIII one thousand, six hundred sixty eight -MDCLXIX one thousand, six hundred sixty nine -MDCLXX one thousand, six hundred seventy -MDCLXXI one thousand, six hundred seventy one -MDCLXXII one thousand, six hundred seventy two -MDCLXXIII one thousand, six hundred seventy three -MDCLXXIV one thousand, six hundred seventy four -MDCLXXV one thousand, six hundred seventy five -MDCLXXVI one thousand, six hundred seventy six -MDCLXXVII one thousand, six hundred seventy seven -MDCLXXVIII one thousand, six hundred seventy eight -MDCLXXIX one thousand, six hundred seventy nine -MDCLXXX one thousand, six hundred eighty -MDCLXXXI one thousand, six hundred eighty one -MDCLXXXII one thousand, six hundred eighty two -MDCLXXXIII one thousand, six hundred eighty three -MDCLXXXIV one thousand, six hundred eighty four -MDCLXXXV one thousand, six hundred eighty five -MDCLXXXVI one thousand, six hundred eighty six -MDCLXXXVII one thousand, six hundred eighty seven -MDCLXXXVIII one thousand, six hundred eighty eight -MDCLXXXIX one thousand, six hundred eighty nine -MDCXC one thousand, six hundred ninety -MDCXCI one thousand, six hundred ninety one -MDCXCII one thousand, six hundred ninety two -MDCXCIII one thousand, six hundred ninety three -MDCXCIV one thousand, six hundred ninety four -MDCXCV one thousand, six hundred ninety five -MDCXCVI one thousand, six hundred ninety six -MDCXCVII one thousand, six hundred ninety seven -MDCXCVIII one thousand, six hundred ninety eight -MDCXCIX one thousand, six hundred ninety nine -MDCC one thousand, seven hundred -MDCCI one thousand, seven hundred one -MDCCII one thousand, seven hundred two -MDCCIII one thousand, seven hundred three -MDCCIV one thousand, seven hundred four -MDCCV one thousand, seven hundred five -MDCCVI one thousand, seven hundred six -MDCCVII one thousand, seven hundred seven -MDCCVIII one thousand, seven hundred eight -MDCCIX one thousand, seven hundred nine -MDCCX one thousand, seven hundred ten -MDCCXI one thousand, seven hundred eleven -MDCCXII one thousand, seven hundred twelve -MDCCXIII one thousand, seven hundred thirteen -MDCCXIV one thousand, seven hundred fourteen -MDCCXV one thousand, seven hundred fifteen -MDCCXVI one thousand, seven hundred sixteen -MDCCXVII one thousand, seven hundred seventeen -MDCCXVIII one thousand, seven hundred eighteen -MDCCXIX one thousand, seven hundred nineteen -MDCCXX one thousand, seven hundred twenty -MDCCXXI one thousand, seven hundred twenty one -MDCCXXII one thousand, seven hundred twenty two -MDCCXXIII one thousand, seven hundred twenty three -MDCCXXIV one thousand, seven hundred twenty four -MDCCXXV one thousand, seven hundred twenty five -MDCCXXVI one thousand, seven hundred twenty six -MDCCXXVII one thousand, seven hundred twenty seven -MDCCXXVIII one thousand, seven hundred twenty eight -MDCCXXIX one thousand, seven hundred twenty nine -MDCCXXX one thousand, seven hundred thirty -MDCCXXXI one thousand, seven hundred thirty one -MDCCXXXII one thousand, seven hundred thirty two -MDCCXXXIII one thousand, seven hundred thirty three -MDCCXXXIV one thousand, seven hundred thirty four -MDCCXXXV one thousand, seven hundred thirty five -MDCCXXXVI one thousand, seven hundred thirty six -MDCCXXXVII one thousand, seven hundred thirty seven -MDCCXXXVIII one thousand, seven hundred thirty eight -MDCCXXXIX one thousand, seven hundred thirty nine -MDCCXL one thousand, seven hundred forty -MDCCXLI one thousand, seven hundred forty one -MDCCXLII one thousand, seven hundred forty two -MDCCXLIII one thousand, seven hundred forty three -MDCCXLIV one thousand, seven hundred forty four -MDCCXLV one thousand, seven hundred forty five -MDCCXLVI one thousand, seven hundred forty six -MDCCXLVII one thousand, seven hundred forty seven -MDCCXLVIII one thousand, seven hundred forty eight -MDCCXLIX one thousand, seven hundred forty nine -MDCCL one thousand, seven hundred fifty -MDCCLI one thousand, seven hundred fifty one -MDCCLII one thousand, seven hundred fifty two -MDCCLIII one thousand, seven hundred fifty three -MDCCLIV one thousand, seven hundred fifty four -MDCCLV one thousand, seven hundred fifty five -MDCCLVI one thousand, seven hundred fifty six -MDCCLVII one thousand, seven hundred fifty seven -MDCCLVIII one thousand, seven hundred fifty eight -MDCCLIX one thousand, seven hundred fifty nine -MDCCLX one thousand, seven hundred sixty -MDCCLXI one thousand, seven hundred sixty one -MDCCLXII one thousand, seven hundred sixty two -MDCCLXIII one thousand, seven hundred sixty three -MDCCLXIV one thousand, seven hundred sixty four -MDCCLXV one thousand, seven hundred sixty five -MDCCLXVI one thousand, seven hundred sixty six -MDCCLXVII one thousand, seven hundred sixty seven -MDCCLXVIII one thousand, seven hundred sixty eight -MDCCLXIX one thousand, seven hundred sixty nine -MDCCLXX one thousand, seven hundred seventy -MDCCLXXI one thousand, seven hundred seventy one -MDCCLXXII one thousand, seven hundred seventy two -MDCCLXXIII one thousand, seven hundred seventy three -MDCCLXXIV one thousand, seven hundred seventy four -MDCCLXXV one thousand, seven hundred seventy five -MDCCLXXVI one thousand, seven hundred seventy six -MDCCLXXVII one thousand, seven hundred seventy seven -MDCCLXXVIII one thousand, seven hundred seventy eight -MDCCLXXIX one thousand, seven hundred seventy nine -MDCCLXXX one thousand, seven hundred eighty -MDCCLXXXI one thousand, seven hundred eighty one -MDCCLXXXII one thousand, seven hundred eighty two -MDCCLXXXIII one thousand, seven hundred eighty three -MDCCLXXXIV one thousand, seven hundred eighty four -MDCCLXXXV one thousand, seven hundred eighty five -MDCCLXXXVI one thousand, seven hundred eighty six -MDCCLXXXVII one thousand, seven hundred eighty seven -MDCCLXXXVIII one thousand, seven hundred eighty eight -MDCCLXXXIX one thousand, seven hundred eighty nine -MDCCXC one thousand, seven hundred ninety -MDCCXCI one thousand, seven hundred ninety one -MDCCXCII one thousand, seven hundred ninety two -MDCCXCIII one thousand, seven hundred ninety three -MDCCXCIV one thousand, seven hundred ninety four -MDCCXCV one thousand, seven hundred ninety five -MDCCXCVI one thousand, seven hundred ninety six -MDCCXCVII one thousand, seven hundred ninety seven -MDCCXCVIII one thousand, seven hundred ninety eight -MDCCXCIX one thousand, seven hundred ninety nine -MDCCC one thousand, eight hundred -MDCCCI one thousand, eight hundred one -MDCCCII one thousand, eight hundred two -MDCCCIII one thousand, eight hundred three -MDCCCIV one thousand, eight hundred four -MDCCCV one thousand, eight hundred five -MDCCCVI one thousand, eight hundred six -MDCCCVII one thousand, eight hundred seven -MDCCCVIII one thousand, eight hundred eight -MDCCCIX one thousand, eight hundred nine -MDCCCX one thousand, eight hundred ten -MDCCCXI one thousand, eight hundred eleven -MDCCCXII one thousand, eight hundred twelve -MDCCCXIII one thousand, eight hundred thirteen -MDCCCXIV one thousand, eight hundred fourteen -MDCCCXV one thousand, eight hundred fifteen -MDCCCXVI one thousand, eight hundred sixteen -MDCCCXVII one thousand, eight hundred seventeen -MDCCCXVIII one thousand, eight hundred eighteen -MDCCCXIX one thousand, eight hundred nineteen -MDCCCXX one thousand, eight hundred twenty -MDCCCXXI one thousand, eight hundred twenty one -MDCCCXXII one thousand, eight hundred twenty two -MDCCCXXIII one thousand, eight hundred twenty three -MDCCCXXIV one thousand, eight hundred twenty four -MDCCCXXV one thousand, eight hundred twenty five -MDCCCXXVI one thousand, eight hundred twenty six -MDCCCXXVII one thousand, eight hundred twenty seven -MDCCCXXVIII one thousand, eight hundred twenty eight -MDCCCXXIX one thousand, eight hundred twenty nine -MDCCCXXX one thousand, eight hundred thirty -MDCCCXXXI one thousand, eight hundred thirty one -MDCCCXXXII one thousand, eight hundred thirty two -MDCCCXXXIII one thousand, eight hundred thirty three -MDCCCXXXIV one thousand, eight hundred thirty four -MDCCCXXXV one thousand, eight hundred thirty five -MDCCCXXXVI one thousand, eight hundred thirty six -MDCCCXXXVII one thousand, eight hundred thirty seven -MDCCCXXXVIII one thousand, eight hundred thirty eight -MDCCCXXXIX one thousand, eight hundred thirty nine -MDCCCXL one thousand, eight hundred forty -MDCCCXLI one thousand, eight hundred forty one -MDCCCXLII one thousand, eight hundred forty two -MDCCCXLIII one thousand, eight hundred forty three -MDCCCXLIV one thousand, eight hundred forty four -MDCCCXLV one thousand, eight hundred forty five -MDCCCXLVI one thousand, eight hundred forty six -MDCCCXLVII one thousand, eight hundred forty seven -MDCCCXLVIII one thousand, eight hundred forty eight -MDCCCXLIX one thousand, eight hundred forty nine -MDCCCL one thousand, eight hundred fifty -MDCCCLI one thousand, eight hundred fifty one -MDCCCLII one thousand, eight hundred fifty two -MDCCCLIII one thousand, eight hundred fifty three -MDCCCLIV one thousand, eight hundred fifty four -MDCCCLV one thousand, eight hundred fifty five -MDCCCLVI one thousand, eight hundred fifty six -MDCCCLVII one thousand, eight hundred fifty seven -MDCCCLVIII one thousand, eight hundred fifty eight -MDCCCLIX one thousand, eight hundred fifty nine -MDCCCLX one thousand, eight hundred sixty -MDCCCLXI one thousand, eight hundred sixty one -MDCCCLXII one thousand, eight hundred sixty two -MDCCCLXIII one thousand, eight hundred sixty three -MDCCCLXIV one thousand, eight hundred sixty four -MDCCCLXV one thousand, eight hundred sixty five -MDCCCLXVI one thousand, eight hundred sixty six -MDCCCLXVII one thousand, eight hundred sixty seven -MDCCCLXVIII one thousand, eight hundred sixty eight -MDCCCLXIX one thousand, eight hundred sixty nine -MDCCCLXX one thousand, eight hundred seventy -MDCCCLXXI one thousand, eight hundred seventy one -MDCCCLXXII one thousand, eight hundred seventy two -MDCCCLXXIII one thousand, eight hundred seventy three -MDCCCLXXIV one thousand, eight hundred seventy four -MDCCCLXXV one thousand, eight hundred seventy five -MDCCCLXXVI one thousand, eight hundred seventy six -MDCCCLXXVII one thousand, eight hundred seventy seven -MDCCCLXXVIII one thousand, eight hundred seventy eight -MDCCCLXXIX one thousand, eight hundred seventy nine -MDCCCLXXX one thousand, eight hundred eighty -MDCCCLXXXI one thousand, eight hundred eighty one -MDCCCLXXXII one thousand, eight hundred eighty two -MDCCCLXXXIII one thousand, eight hundred eighty three -MDCCCLXXXIV one thousand, eight hundred eighty four -MDCCCLXXXV one thousand, eight hundred eighty five -MDCCCLXXXVI one thousand, eight hundred eighty six -MDCCCLXXXVII one thousand, eight hundred eighty seven -MDCCCLXXXVIII one thousand, eight hundred eighty eight -MDCCCLXXXIX one thousand, eight hundred eighty nine -MDCCCXC one thousand, eight hundred ninety -MDCCCXCI one thousand, eight hundred ninety one -MDCCCXCII one thousand, eight hundred ninety two -MDCCCXCIII one thousand, eight hundred ninety three -MDCCCXCIV one thousand, eight hundred ninety four -MDCCCXCV one thousand, eight hundred ninety five -MDCCCXCVI one thousand, eight hundred ninety six -MDCCCXCVII one thousand, eight hundred ninety seven -MDCCCXCVIII one thousand, eight hundred ninety eight -MDCCCXCIX one thousand, eight hundred ninety nine -MCM one thousand, nine hundred -MCMI one thousand, nine hundred one -MCMII one thousand, nine hundred two -MCMIII one thousand, nine hundred three -MCMIV one thousand, nine hundred four -MCMV one thousand, nine hundred five -MCMVI one thousand, nine hundred six -MCMVII one thousand, nine hundred seven -MCMVIII one thousand, nine hundred eight -MCMIX one thousand, nine hundred nine -MCMX one thousand, nine hundred ten -MCMXI one thousand, nine hundred eleven -MCMXII one thousand, nine hundred twelve -MCMXIII one thousand, nine hundred thirteen -MCMXIV one thousand, nine hundred fourteen -MCMXV one thousand, nine hundred fifteen -MCMXVI one thousand, nine hundred sixteen -MCMXVII one thousand, nine hundred seventeen -MCMXVIII one thousand, nine hundred eighteen -MCMXIX one thousand, nine hundred nineteen -MCMXX one thousand, nine hundred twenty -MCMXXI one thousand, nine hundred twenty one -MCMXXII one thousand, nine hundred twenty two -MCMXXIII one thousand, nine hundred twenty three -MCMXXIV one thousand, nine hundred twenty four -MCMXXV one thousand, nine hundred twenty five -MCMXXVI one thousand, nine hundred twenty six -MCMXXVII one thousand, nine hundred twenty seven -MCMXXVIII one thousand, nine hundred twenty eight -MCMXXIX one thousand, nine hundred twenty nine -MCMXXX one thousand, nine hundred thirty -MCMXXXI one thousand, nine hundred thirty one -MCMXXXII one thousand, nine hundred thirty two -MCMXXXIII one thousand, nine hundred thirty three -MCMXXXIV one thousand, nine hundred thirty four -MCMXXXV one thousand, nine hundred thirty five -MCMXXXVI one thousand, nine hundred thirty six -MCMXXXVII one thousand, nine hundred thirty seven -MCMXXXVIII one thousand, nine hundred thirty eight -MCMXXXIX one thousand, nine hundred thirty nine -MCMXL one thousand, nine hundred forty -MCMXLI one thousand, nine hundred forty one -MCMXLII one thousand, nine hundred forty two -MCMXLIII one thousand, nine hundred forty three -MCMXLIV one thousand, nine hundred forty four -MCMXLV one thousand, nine hundred forty five -MCMXLVI one thousand, nine hundred forty six -MCMXLVII one thousand, nine hundred forty seven -MCMXLVIII one thousand, nine hundred forty eight -MCMXLIX one thousand, nine hundred forty nine -MCML one thousand, nine hundred fifty -MCMLI one thousand, nine hundred fifty one -MCMLII one thousand, nine hundred fifty two -MCMLIII one thousand, nine hundred fifty three -MCMLIV one thousand, nine hundred fifty four -MCMLV one thousand, nine hundred fifty five -MCMLVI one thousand, nine hundred fifty six -MCMLVII one thousand, nine hundred fifty seven -MCMLVIII one thousand, nine hundred fifty eight -MCMLIX one thousand, nine hundred fifty nine -MCMLX one thousand, nine hundred sixty -MCMLXI one thousand, nine hundred sixty one -MCMLXII one thousand, nine hundred sixty two -MCMLXIII one thousand, nine hundred sixty three -MCMLXIV one thousand, nine hundred sixty four -MCMLXV one thousand, nine hundred sixty five -MCMLXVI one thousand, nine hundred sixty six -MCMLXVII one thousand, nine hundred sixty seven -MCMLXVIII one thousand, nine hundred sixty eight -MCMLXIX one thousand, nine hundred sixty nine -MCMLXX one thousand, nine hundred seventy -MCMLXXI one thousand, nine hundred seventy one -MCMLXXII one thousand, nine hundred seventy two -MCMLXXIII one thousand, nine hundred seventy three -MCMLXXIV one thousand, nine hundred seventy four -MCMLXXV one thousand, nine hundred seventy five -MCMLXXVI one thousand, nine hundred seventy six -MCMLXXVII one thousand, nine hundred seventy seven -MCMLXXVIII one thousand, nine hundred seventy eight -MCMLXXIX one thousand, nine hundred seventy nine -MCMLXXX one thousand, nine hundred eighty -MCMLXXXI one thousand, nine hundred eighty one -MCMLXXXII one thousand, nine hundred eighty two -MCMLXXXIII one thousand, nine hundred eighty three -MCMLXXXIV one thousand, nine hundred eighty four -MCMLXXXV one thousand, nine hundred eighty five -MCMLXXXVI one thousand, nine hundred eighty six -MCMLXXXVII one thousand, nine hundred eighty seven -MCMLXXXVIII one thousand, nine hundred eighty eight -MCMLXXXIX one thousand, nine hundred eighty nine -MCMXC one thousand, nine hundred ninety -MCMXCI one thousand, nine hundred ninety one -MCMXCII one thousand, nine hundred ninety two -MCMXCIII one thousand, nine hundred ninety three -MCMXCIV one thousand, nine hundred ninety four -MCMXCV one thousand, nine hundred ninety five -MCMXCVI one thousand, nine hundred ninety six -MCMXCVII one thousand, nine hundred ninety seven -MCMXCVIII one thousand, nine hundred ninety eight -MCMXCIX one thousand, nine hundred ninety nine -MM two thousand diff --git a/nemo_text_processing/text_normalization/en/data/suppletive.tsv b/nemo_text_processing/text_normalization/en/data/suppletive.tsv deleted file mode 100644 index 115460aa2fae..000000000000 --- a/nemo_text_processing/text_normalization/en/data/suppletive.tsv +++ /dev/null @@ -1,83 +0,0 @@ -deer -fish -sheep -foot feet -goose geese -man men -mouse mice -tooth teeth -woman women -won -child children -ox oxen -wife wives -wolf wolves -analysis analyses -criterion criteria -lbs -focus foci -percent -hertz -kroner krone -inch inches -calory calories -yen -megahertz -gigahertz -kilohertz -hertz -CC -c c -horsepower -hundredweight -kilogram force kilograms force -mega siemens -revolution per minute revolutions per minute -mile per hour miles per hour -megabit per second megabits per second -square foot square feet -kilobit per second kilobits per second -degree Celsius degrees Celsius -degree Fahrenheit degrees Fahrenheit -ATM -AU -BQ -CC -CD -DA -EB -EV -F -GB -G -GL -GPA -GY -HA -H -HL -GP -HS -KB -KL -KN -KT -KV -LM -MA -MA -MB -MC -MF -M -MM -MS -MV -MW -PB -PG -PS -S -TB -YB -ZB \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/en/data/telephone/__init__.py b/nemo_text_processing/text_normalization/en/data/telephone/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/en/data/telephone/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/en/data/telephone/ip_prompt.tsv b/nemo_text_processing/text_normalization/en/data/telephone/ip_prompt.tsv deleted file mode 100644 index 03e2529895d9..000000000000 --- a/nemo_text_processing/text_normalization/en/data/telephone/ip_prompt.tsv +++ /dev/null @@ -1,2 +0,0 @@ -IP address is -IP is \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/en/data/telephone/ssn_prompt.tsv b/nemo_text_processing/text_normalization/en/data/telephone/ssn_prompt.tsv deleted file mode 100644 index 8bbdb9f752f7..000000000000 --- a/nemo_text_processing/text_normalization/en/data/telephone/ssn_prompt.tsv +++ /dev/null @@ -1,4 +0,0 @@ -ssn is SSN is -ssn is SSN is -SSN is -SSN \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/en/data/telephone/telephone_prompt.tsv b/nemo_text_processing/text_normalization/en/data/telephone/telephone_prompt.tsv deleted file mode 100644 index 6dcfb6cad223..000000000000 --- a/nemo_text_processing/text_normalization/en/data/telephone/telephone_prompt.tsv +++ /dev/null @@ -1,5 +0,0 @@ -call me at -reach at -reached at -my number is -hit me up at \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/en/data/time/__init__.py b/nemo_text_processing/text_normalization/en/data/time/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/text_normalization/en/data/time/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/en/data/time/suffix.tsv b/nemo_text_processing/text_normalization/en/data/time/suffix.tsv deleted file mode 100644 index 026a6a90ccce..000000000000 --- a/nemo_text_processing/text_normalization/en/data/time/suffix.tsv +++ /dev/null @@ -1,12 +0,0 @@ -p.m. PM -p.m PM -pm PM -P.M. PM -P.M PM -PM PM -a.m. AM -a.m AM -am AM -A.M. AM -A.M AM -AM AM diff --git a/nemo_text_processing/text_normalization/en/data/time/zone.tsv b/nemo_text_processing/text_normalization/en/data/time/zone.tsv deleted file mode 100644 index 0fda04208c87..000000000000 --- a/nemo_text_processing/text_normalization/en/data/time/zone.tsv +++ /dev/null @@ -1,14 +0,0 @@ -cst CST -c.s.t CST -cet CET -c.e.t CET -pst PST -p.s.t PST -est EST -e.s.t EST -pt PT -p.t PT -et ET -e.t ET -gmt GMT -g.m.t GMT diff --git a/nemo_text_processing/text_normalization/en/data/whitelist/__init__.py b/nemo_text_processing/text_normalization/en/data/whitelist/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/text_normalization/en/data/whitelist/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/en/data/whitelist/alternatives.tsv b/nemo_text_processing/text_normalization/en/data/whitelist/alternatives.tsv deleted file mode 100644 index 0ee8e2c1d79b..000000000000 --- a/nemo_text_processing/text_normalization/en/data/whitelist/alternatives.tsv +++ /dev/null @@ -1,45 +0,0 @@ -Hon. Honorable -Mr. Mister -Mrs. Misses -Ms. Miss -Mr Mister -Mrs Misses -Ms Miss -AC air conditioning -AC air conditioner -AC air conditioners -AC alternating current -&Co. and Co. -&Co. and Company -Mon Monday -Tu Tuesday -Wed Wednesday -Th Thursday -Thur Thursday -Thurs Thursday -Fri Friday -Sat Saturday -Sun Sunday -Mon Mon -Tu Tu -Wed Wed -Th Th -Thur Thur -Thurs Thurs -Fri Fri -Sat Sat -Sun Sun -= equals -# number -No. number -No number -NO number -NO. number -NO nitrogen monoxide -NO NO -NO. NO. -No. No. -No No -VOL Volume -VOL. Volume -TV Television diff --git a/nemo_text_processing/text_normalization/en/data/whitelist/alternatives_all_format.tsv b/nemo_text_processing/text_normalization/en/data/whitelist/alternatives_all_format.tsv deleted file mode 100644 index 449195c4f41f..000000000000 --- a/nemo_text_processing/text_normalization/en/data/whitelist/alternatives_all_format.tsv +++ /dev/null @@ -1,14 +0,0 @@ -st street -st saint -dr doctor -dr drive -mt mount -sr senior -prof professor -mt mountain -sr senior -jr junior -vol volume -rd road -ave avenue -approx approximately diff --git a/nemo_text_processing/text_normalization/en/data/whitelist/asr.tsv b/nemo_text_processing/text_normalization/en/data/whitelist/asr.tsv deleted file mode 100644 index c067e1796422..000000000000 --- a/nemo_text_processing/text_normalization/en/data/whitelist/asr.tsv +++ /dev/null @@ -1,14713 +0,0 @@ -Ph.D. p h d -Hon. honorable -& and -Mt. Mount -Maj. Major -Rev. Reverend -# hash -Gov. governor -vs. versus -vs versus -dept. department -vol volume -vol. volume -bldg. building -Bldg. Building -apt. apartment -Apt. Apartment -Σ sigma -η eta -κ kappa -ω omega -σ sigma -α alpha -ν nu -δ delta -ι iota -_ underscore -% percent sign -& ampersand -* asterisk -+ plus -/ slash -= equal sign -^ circumflex -{ left brace -| vertical bar -} right brace -~ tilde -ltd limited -int'l international -$ dollar -A. A. a a -A.A. a a -A.A.A. a a a -A&A a and a -AAAI a a a i -AAAM a a a m -AAAs a a a's -AAAS a a a s -AAAW a a a w -AABA a a b a -AaB a a b -A. A. B. a a b -AAB a a b -AABC a a b c -Aabo a a b o -AABP a a b p -AABW a a b w -aac a a c -AAC a a c -AACAR a a c a r -AACC a a c c -AACCUP a a c c u p -AACMI a a c m i -AACNo a a c n o -AACR a a c r -AACS a a c s -AACSB a a c s b -AACTA a a c t a -AACUPR a a c u p r -A.A.D. a a d -AADT a a d t -AADTs a a d t's -Aadu a a d u -AAEA a a e a -AAE a a e -AAERT a a e r t -AAF a a f -AAFCA a a f c a -AAFC a a f c -AAFC's a a f c's -AAFld a a f l d -AAFPOA a a f p o a -AAGPBL a a g p b l -AAHHE a a h h e -AAI a a i -AAIB a a i b -AAIP a a i p -AAK a a k -Aap a a p -AAP a a p -AAPB a a p b -AAPC a a p c -AAPEP a a p e p -AAPG a a p g -AAPM a a p m -AAPT a a p t -A.A.R.M. a a r m -A.B.A. a b a -A. B. a b -A.B. a b -A.B.A. J. a b a j -A.B.C. a b c -A. B. G. a b g -ABG a b g -ABH a b h -ABHD a b h d -Abhi a b h i -ABK a b k -abl a b l -Abl a b l -ABL a b l -ABLV a b l v -ABM a b m -ABMC a b m c -ABN a b n -ABN's a b n's -Abp a b p -ABP a b p -ABPD a b p d -ABPI a b p i -ABPN a b p n -ABPP a b p p -ABPRS a b p r s -ABPW a b p w -Abr a b r -ABR a b r -abv a b v -ABV a b v -ABW a b w -ABX a b x -Abz a b z -A.C.A. a c a -ACA a c a -A&C a and c -A. C. a c -A.C. a c -acac a c a c -Acad a c a d -ACAD a c a d -A.C.A.P. a c a p -ACAP a c a p -ACAS a c a s -ACAZ a c a z -ACBA a c b a -ACB a c b -ACBL a c b l -ACBS a c b s -ACBSP a c b s p -ACCA a c c a -ACC a c c -ACCC a c c c -ACCJ a c c j -ACC&S a c c and s -ACC's a c c's -ACCS a c c s -ACD a c d -ACDC a c d c -A.C. D.F.C. A.F.C. a c d f c a f c -A.C.E. a c e -ACF a c f -ACFE a c f e -A.C.G. a c g -ACG a c g -ACGIH's a c g i h's -A.C.H. a c h -ACH a c h -ACHP a c h p -ACHR a c h r -ACHRE a c h r e -Achs a c h's -ACHS a c h s -ACIA a c i a -ACI a c i -ACICS a c i c s -ACIGA a c i g a -ACIP a c i p -Acis a c i's -ACIS a c i s -ACLA a c l a -A.C.L. a c l -ACL a c l -A.C.L.N. a c l n -acls a c l s -ACL's a c l's -ACLS a c l s -ACLU a c l u -ACM a c m -ACMI a c m i -ACMs a c m's -ACN a c n -ACO's a c o's -AcpA a c p a -ACP a c p -AcpB a c p b -ACPI a c p i -ACPO a c p o -ACPP a c p p -ACPSEM a c p s e m -acq a c q -A&CR a and c r -ACR a c r -ACRPS a c r p s -ACSA a c s a -ACSAC a c s a c -ACSBR a c s b r -ACSCN a c s c n -ACSEL a c s e l -ACSIA a c s i a -ACSI a c s i -AcSOC a c s o c -ACSR a c s r -ACSS a c s s -ACSS's a c s s's -A.C.T. a c t -A.C.T.A.F.L. a c t a f l -acu a c u -ACU a c u -ACU's a c u's -ACUS a c u s -ACUV a c u v -ACV a c v -ACWA a c w a -AC&W a c and w -ACW a c w -ACWM a c w m -ACWS a c w s -Acy a c y -ADAA a d a a -A&D a and d -Adab a d a b -ADAC a d a c -A. D. a d -A.D. a d -A.D.A.M. adam -AdaSL a d a s l -ADATA a d a t a -ADBAC a d b a c -ADB a d b -ADBGR a d b g r -ADBICA a d b i c a -ADCA a d c a -AdC a d c -A.D.C. a d c -ADC a d c -ADCAF a d c a f -ADCC a d c c -ADC's a d c's -ADCs a d c's -ADCY a d c y -ADDA a d d a -AD&D a d and d -ADF a d f -ADF's a d f's -ADFS a d f s -ADG a d g -ADGB a d g b -ADH a d h -ADHD a d h d -ADHM a d h m -ADHS a d h s -ADIAC a d i a c -AdK a d k -ADK a d k -Adl a d l -ADL a d l -ADLL a d l l -ADLs a d l's -A.D.M.A.C. a d m a c -adm a d m -Adm a d m -ADM a d m -ADMK a d m k -admn a d m n -ADMN a d m n -ADNAC a d n a c -ADNEC a d n e c -ADNs a d n's -AdP a d p -A.D.P. a d p -ADP a d p -ADPCM a d p c m -ADPF a d p f -ADQ a d q -ADQ's a d q's -ADR a d r -ADRC a d r c -ADRP a d r p -ADRs a d r's -ADSB a d s b -ADSL a d s l -ADSR a d s r -ADSRs a d s r's -ADSs a d s's -ADT a d t -ADTG a d t g -Adwa a d w a -ADWC a d w c -ADX a d x -AEA a e a -A&E a and e -A. E. a e -A.E. a e -AEBN a e b n -A.E.C. a e c -AEC a e c -AECL a e c l -AECR a e c r -AEC's a e c's -Aed a e d -AED a e d -AEE a e e -AEF a e f -AEG a e g -A.E.G.I.S. a e g i s -aegte a e g t e -AEHL a e h l -AEIA a e i a -AEI a e i -AEIOU a e i o u -Aeka a e k a -AEK a e k -ael a e l -AEL a e l -Aema a e m a -AEM a e m -aen a e n -AEO a e o -AEPA a e p a -AEP a e p -aere a e r e -AEre a e r e -AErn a e r n -AERN a e r n -Aert a e r t -AESA a e s a -Aes a e's -AES a e s -aet a e t -AET a e t -AETC a e t c -AEU a e u -AEV a e v -AEW a e w -AEX a e x -Afa a f a -AFA a f a -af a f -Af a f -A. F. a f -A.F. a f -AF a f -Afaf a f a f -AFAP a f a p -AFB a f b -AFCA a f c a -afc a f c -A.F.C. a f c -AFC a f c -AFCEC a f c e c -AFCO a f c o -AFC's a f c's -AFCS a f c s -AFCSThe a f c s t h e -Afd a f d -AfD a f d -AfDB a f d b -AFDD a f d d -Afe a f e -AFE a f e -Aff a f f -AFF a f f -AFG a f g -AFGM a f g m -AFH a f h -AFIA a f i a -Afi a f i -AFI a f i -AFIP a f i p -Afiq a f i q -AFI's a f i's -AFJ a f j -A.F.L. a f l -AFL a f l -AFLEG a f l e g -AFLPA a f l p a -AFLP a f l p -AFL's a f l's -AFLUA a f l u a -A. F.M.A. a f m a -AFM a f m -AFMC a f m c -AFMs a f m's -AFN a f n -AFNET a f n e t -AFNOR a f n o r -AfNS a f n s -Afo a f o -AFO a f o -AFOP a f o p -AFP a f p -AFRA a f r a -Afr a f r -AFR a f r -AFRC a f r c -AFRL a f r l -AFROTC a f r o t c -AFRTS a f r t s -AFS a f s -AFSC a f s c -AFSPC a f s p c -A. F. T. a f t -AFTRA a f t r a -AFTRA's a f t r a's -AFTRS a f t r s -AFV a f v -AFW a f w -AFWS a f w s -AFX a f x -A. G. a g -A.G. a g -AGB a g b -AGC a g c -Agco a g c o -AGCO a g c o -Agda a g d a -AGD a g d -A.G.E. a g e -AGF a g f -AG&G a g and g -agg a g g -Agi a g i -AGI a g i -AGID a g i d -AGIR a g i r -Agis a g i's -A.G.J. a g j -A. G. K. a g k -AGL a g l -agli a g l i -AGM a g m -Agn a g n -AgN a g n -AGN a g n -AGNs a g n's -A.G.P. a g p -AGP a g p -AGPL a g p l -AGPS a g p s -AGPW a g p w -AGRs a g r's -AGS a g s -AGSM a g s m -Agsu a g s u -agt a g t -AGT a g t -Agte a g t e -Agu a g u -AGU a g u -AGV a g v -A.H.A. a h a -A. H. a h -A.H. a h -A. H. C. a h c -AHC a h c -AHD a h d -Ahdhra a h d h r a -AHDR a h d r -ahe a h e -Ahe a h e -AHEC a h e c -AHF a h f -Ahirs a h i r's -Ahk a h k -AHL's a h l's -AHP a h p -AHRC a h r c -AHRS a h r s -AHSAA a h s a a -Ahsa'i a h s a i -Ahta a h t a -aht a h t -AHT a h t -AHTD a h t d -Ahu a h u -Ahva a h v a -AHV a h v -AIAA a i a a -A.I.A. a i a -A&I a and i -A. I. a i -A.I. a i -AI a i -AIAP a i a p -Aias a i a's -AIAS a i a s -AIATSIS a i a t s i s -AIAW a i a w -AIAWU a i a w u -A.I.B.A. a i b a -AIB a i b -AIBO's a i b o's -AIBS a i b s -AIC a i c -AICAR a i c a r -AICC a i c c -AICCCR a i c c c r -aici a i c i -Aicme a i c m e -AICN a i c n -AICPA a i c p a -AICP a i c p -AICPS a i c p s -AICs a i c's -AICTE a i c t e -AICUF a i c u f -AIDA a i d a -AIDN a i d n -AIEC a i e c -AIEE a i e e -Aiel a i e l -AIEP a i e p -AIFA a i f a -AIF a i f -AIFB a i f b -Aife a i f e -AIFF a i f f -AIFM a i f m -AIFMD a i f m d -AiG a i g -AIG a i g -Aigis a i g i's -AIHA a i h a -AIH a i h -AIHL a i h l -AIIMS a i i m s -Aija a i j a -Aik a i k -AIK a i k -AIKR a i k r -A.I.M. a i m -AIP a i p -A.I.R. a i r -AIs a i's -AISC a i s c -AISD a i s d -AISRI a i s r i -Aist a i s t -AIST a i s t -AIT's a i t's -Aitu a i t u -Aiud a i u d -aius a i u s -AIVC a i v c -AIW a i w -AIX a i x -A. J. a j -A.J. a j -Ajba a j b a -A.J.B. a j b -AJC a j c -A.J.E. a j e -A.J.G.C. a j g c -A. J. J. a j j -A.J.K. a j k -AJK a j k -AJKF a j k f -AJL a j l -A. J. M. a j m -Ajmi a j m i -AJN a j n -AJO a j o -Ajok a j o k -A. J. P. a j p -A.J.P. a j p -AJPW a j p w -A.J.R. a j r -AJR a j r -Ajsa a j s a -A.J.S. a j s -AJS a j s -A. J. T. a j t -A.J.T. a j t -AJT a j t -Aju a j u -AJUFE a j u f e -AJUSCO a j u s c o -AJV a j v -AJW a j w -AJWRC a j w r c -Akaa a k a a -a.k.a. a k a -aka. a k a -aka a k a -Aka. a k a -Aka a k a -AKA a k a -A&K a and k -A. K. a k -A.K. a k -AK a k -AKAP a k a p -AKAPs a k a p's -akas a k a's -AKAs a k a's -AKB a k b -AKC a k c -AKCR a k c r -AKD a k d -AKG a k g -AKG's a k g's -akh a k h -AKH a k h -AKHS a k h s -AKM a k m -AKN a k n -Akpa a k p a -AKP a k p -AKPD a k p d -AKQA a k q a -AKR a k r -A.K.S. a k s -AKS a k s -Aku a k u -AKU a k u -AKW a k w -A&L a and l -A. L. a l -A.L. a l -ALDF a l d f -ALDH a l d h -ALDS a l d s -ALDT a l d t -ALFASID a l f a s i d -ALFIDI a l f i d i -ALFTP a l f t p -Alh a l h -ALH a l h -A. L. M. a l m -A.L.P. a l p -ALPG a l p g -ALR a l r -ALSC a l s c -ALSF a l s f -ALSP a l s p -ALUs a l u's -A.L.V. a l v -ALVF a l v f -ALW a l w -ALWS a l w s -AMAA a m a a -A'ma a m a -A&M a and m -a.m. a m -a.m a m -A. M. a m -A.M. a m -AMAP a m a p -AMARC a m a r c -AMAs a m a's -amb a m b -Amb a m b -AMB a m b -AmBX a m b x -A.M.C. a m c -AMC a m c -AMC's a m c's -AMCs a m c's -Amda a m d a -Amd a m d -AMD a m d -AMDH a m d h -AMDISA a m d i s a -AMD's a m d's -A.M.E. a m e -AMF a m f -AM&FM a m and f m -A. M. G. a m g -AMG a m g -AMGTV a m g t v -Amha a m h a -AMH a m h -A.M.H.S. a m h s -A. M. J. a m j -AMJ a m j -A. M. K. a m k -A.M.K. a m k -AMK a m k -aml a m l -AML a m l -Amli a m l i -AMLS a m l s -Amlwch a m l w c h -Amm a m m -A. M. M. a m m -AMM a m m -AMMAYI a m m a y i -AMMB a m m b -AMMK a m m k -AMN a m n -AMNH a m n h -AMNRL a m n r l -AM&O a m and o -AMPK a m p k -AMPL a m p l -AMP's a m p's -Ampt a m p t -AMPTP a m p t p -amr a m r -A. M. R. a m r -AMREF a m r e f -AMSA a m s a -A&M's a and m's -Ams' a m's -amsl a m s l -AMSO's a m s o's -AMSRS a m s r s -AMS's a m s's -AMSS a m s s -AMSU a m s u -Amta a m t a -amt a m t -Amt a m t -A.M.T. a m t -AMT a m t -AMTB a m t b -AMTPAT a m t p a t -Amts a m t's -AMTV a m t v -Amu a m u -AMU a m u -AMU's a m u's -A.M.V.M. a m v m -AMW a m w -AMX a m x -AMYF a m y f -A. N. a n -A.N. a n -ANB a n b -ANBO a n b o -ANC a n c -ANCC a n c c -ANC's a n c's -andFHL a n d f h l -ANDOC a n d o c -ANDP a n d p -ANDPOP a n d p o p -andPPP a n d p p p -Anej a n e j -ANELFA a n e l f a -ANF a n f -ANFIS a n f i s -ANFP's a n f p's -ANGB a n g b -ANGPTL a n g p t l -ANGRAU a n g r a u -Angti a n g t i -Angu a n g u -Anhe a n h e -ANI a n i -ANL a n l -ANM a n m -ANP a n p -ANPP a n p p -ANPR a n p r -Anr a n r -ANR a n r -ANRC a n r c -ANREF's a n r e f's -Anrep a n r e p -ANRS a n r s -ANRW a n r w -A.N.S. a n s -ANS a n s -ANSF a n s f -ANTB a n t b -ANTM a n t m -ANTV a n t v -ANUGA a n u g a -ANWB a n w b -ANX a n x -ANZ a n z -A&O a and o -AOA a o a -AOAC a o a c -A. O. a o -A.O. a o -AOB a o b -AOC a o c -AOCB a o c b -AOCCs a o c c's -AOCE a o c e -AOD a o d -Aodh a o d h -Aodla a o d l a -AODV a o d v -AOE a o e -AOGCC a o g c c -Aogo a o g o -AOH a o h -Aoke a o k e -Aoko a o k o -AOKP a o k p -Aola a o l a -Aol a o l -AOL a o l -AOL's a o l's -AOLTV a o l t v -AOM a o m -AOMC a o m c -AONB a o n b -Aone a o n e -Aoni a o n i -Aoos a o o's -AOPA a o p a -AOP a o p -aor a o r -AOR a o r -AORs a o r's -AORS a o r s -aos a o s -AoS a o s -AOS a o s -Aotus a o t u's -AOTW a o t w -aov a o v -aovf a o v f -AOWC a o w c -A&P a and p -A. P. a p -A.P. a p -APBA a p b a -APB a p b -APBL a p b l -APCA a p c a -Apc a p c -APC a p c -APCCR a p c c r -APCh a p c h -APCR a p c r -APCRDA a p c r d a -APC's a p c's -APCs a p c's -APDA a p d a -APD a p d -APDM a p d m -APEGBC a p e g b c -APFA a p f a -APF a p f -APFCT a p f c t -APFOL a p f o l -APFSDS a p f s d s -APFUTU a p f u t u -apg a p g -APG a p g -APGAW a p g a w -Aph a p h -A.P.H. a p h -APH a p h -APHEDA a p h e d a -APHL a p h l -Apiao a p i a o -Api a p i -APi a p i -API a p i -Apic a p i c -APICv a p i c v -API's a p i's -A.P.J. a p j -APJ a p j -Apl a p l -APL a p l -APLP a p l p -APLS a p l s -Aplu a p l u -APM a p m -APML a p m l -APMR a p m r -APMSO a p m s o -APN a p n -A.P.N.C. a p n c -Apphttp a p p h t t p -Appl a p p l -APPO's a p p o's -appr a p p r -Appts a p p t's -appu a p p u -Appu a p p u -APRA's a p r a's -APRC a p r c -APR's a p r's -APRS a p r s -APRST a p r s t -A&P's a and p's -Aps a p's -APs a p's -APS a p s -APSF a p s f -APSL a p s l -APTA a p t a -AP&T a p and t -APW a p w -APX a p x -APXS a p x s -Aqa a q a -AQA a q a -AQC a q c -AQI a q i -Aql a q l -AQP a q p -Aqr a q r -A&R a and r -ARA a r a -A. R. a r -A.R. a r -A.R.B. a r b -ARD a r d -ARDF a r d f -A.R.E. a r e -A.R.F. a r f -ARF a r f -A.R.G. a r g -ArgR a r g r -ARGs a r g's -ARGT a r g t -ArH a r h -ARJ a r j -ARL a r l -ARLFC a r l f c -A. R. M. a r m -ARMv a r m v -Arnd a r n d -ARNG a r n g -ARNT a r n t -A.R.P. a r p -ARQ a r q -ARTL a r t l -A.R.U. a r u -arv a r v -Arv a r v -ARV a r v -ARVD a r v d -ARVN a r v n -ARW a r w -Arwi a r w i -ARWU a r w u -A.S.A. a s a -ASA a s a -As'ad a s a d -Asai a s a i -Asao a s a o -A.s a's -A. S. a s -A.S. a s -ASAS a s a s -Asasp a s a s p -ASAU a s a u -A.S.B. a s b -ASB a s b -ASBDA a s b d a -asbl a s b l -ASBM a s b m -ASBMH a s b m h -ASBO a s b o -A.S.C. a s c -ASC a s c -ASCB a s c b -ASCE a s c e -asci a s c i -Asci a s c i -ASCP a s c p -ASCW a s c w -A.S.D. a s d -ASD a s d -ASDIC a s d i c -Asdis a s d i's -ASE a s e -ASEC a s e c -ASEE a s e e -ASEF a s e f -ASFA a s f a -ASF a s f -Asfi a s f i -ASGA a s g a -ASG a s g -ASGC a s g c -ASGE a s g e -ASGS a s g s -A. S. H. a s h -A.S.I. a s i -ASL a s l -A.S.M. a s m -ASM a s m -ASMD a s m d -ASME a s m e -ASMIK a s m i k -ASML a s m l -ASMPH a s m p h -ASMSU a s m s u -Asn a s n -ASN a s n -Aso a s o -ASO a s o -ASP a s p -ASPCA a s p c a -Aspe a s p e -ASPTS a s p t s -Asr a s r -ASR a s r -Assn a s s n -assoc a s s o c -Assoc a s s o c -ASSPs a s s p's -ASSR a s s r -Asst a s s t -ASSU a s s u -A. S. T. a s t -ASTAT a s t a t -ASTCL a s t c l -Aste a s t e -ASTE a s t e -ASTM a s t m -ASTR a s t r -Astt a s t t -A.S.U. a s u -ASU a s u -A.S.V. a s v -ASV a s v -Aswa a s w a -ASW a s w -ASX a s x -A&T a and t -atac a t a c -Atac a t a c -Atad a t a d -Ata's a t a's -Atas a t a's -ATA's a t a's -A. T. a t -A.T. a t -ATBF a t b f -ATBs a t b's -atc a t c -A.T.C. a t c -ATC a t c -atcc a t c c -ATCC a t c c -ATCDE a t c d e -ATCL a t c l -ATDC a t d c -ATF a t f -Atg a t g -ATG a t g -ATGM a t g m -ath a t h -Ath a t h -ATH a t h -ATHN a t h n -ATIA a t i a -Atia's a t i a's -Ati a t i -ATi a t i -ATI a t i -Atid a t i d -Atiiq a t i i q -Atil a t i l -ATINC a t i n c -ATIP a t i p -Atiq a t i q -Ativ a t i v -ATJ a t j -Atka a t k a -ATK a t k -ATKN a t k n -ATK's a t k's -Atl a t l -ATL a t l -Atli a t l i -ATLY a t l y -A.T.M.A. a t m a -atm a t m -Atm a t m -ATM a t m -ATMs a t m's -ATN a t n -ATO a t o -Atos a t o's -ATO's a t o's -ATOs a t o's -ATOS a t o s -ATP a t p -atpB a t p b -A.T.Q. a t q -ATR a t r -atri a t r i -Atri a t r i -ATRP a t r p -A&T's a and t's -A.T.s a t's -A.T.S. a t s -ATS a t s -ATSC a t s c -ATSDR a t s d r -AT&SF a t and s f -ATSF a t s f -ATSIC a t s i c -AT&T a t and t -attd a t t d -ATTESA a t t e s a -ATTF a t t f -AT&T's a t and t's -ATTS a t t s -ATTWI a t t w i -ATTWX's a t t w x's -ATU's a t u's -atv a t v -ATV a t v -ATV's a t v's -ATVs a t v's -ATW a t w -AtxA a t x a -ATX a t x -ATXN a t x n -A. U. a u -A.U. a u -Aub a u b -Aubl a u b l -AUC a u c -AUVs a u v's -Auw a u w -avab a v a b -AVAC a v a c -A. V. a v -A.V. a v -AVCA a v c a -AVC a v c -AVCHD a v c h d -AVCs a v c's -AVCS a v c s -AVD a v d -avg a v g -AVG a v g -AVK a v k -AVL a v l -A. V. M. a v m -AVM a v m -Avn a v n -AVN a v n -Avo a v o -AvP a v p -AVP a v p -AVR a v r -AVRs a v r's -AVSA a v s a -Avs' a v's -Avs a v's -AVS a v s -AVSM a v s m -AVU a v u -avvo a v v o -AVX a v x -Awa a w a -A. W. A. a w a -AWA a w a -A. W. A. M. a w a m -AWAs a w a's -A. W. a w -A.W. a w -A. W. B. a w b -A.W.B. a w b -AWB a w b -AWB's a w b's -A.W.C. a w c -AWC a w c -AWD a w d -A. W. F. a w f -AWG a w g -AWGIE a w g i e -AWGN a w g n -A. W. H. a w h -AWHL a w h l -AWI a w i -AWM a w m -Awo a w o -AWP a w p -AWP's a w p's -AWR a w r -AWSA a w s a -AWU a w u -Awwa a w w a -AXAF a x a f -A. X. a x -AXS a x s -AXV a x v -A. Y. a y -A.Y. a y -AYF a y f -AYK a y k -AYP a y p -A. Z. a z -A.Z. a z -AZE a z e -Azg a z g -Azi a z i -AZI a z i -AZL a z l -azm a z m -AZN a z n -AZS a z s -AZSTA's a z s t a's -AZT a z t -AzTV a z t v -Baad b a a d -BAAG b a a g -Ba'al b a a l -Baal b a a l -BAAL b a a l -Baam b a a m -Baap b a a p -ba'as b a a's -Baat b a a t -Ba'ath b a a t h -B. A. b a -B.A. b a -B&A b and a -Bac b a c -B.A.C. b a c -BAC b a c -bae b a e -Bae b a e -BAe b a e -BAE b a e -BAFA b a f a -BAF b a f -B.A.R. b a r -BARV b a r v -BASCA b a s c a -BASEC b a s e c -BASF b a s f -B.A.S.P. b a s p -BATVG b a t v g -Bauw b a u w -B'Av b a v -BAV b a v -BBA b b a -BBAG b b a g -B&B b and b -bb b b -B. B. b b -B.B. b b -BB b b -BBB b b b -bbc b b c -B.B.C. b b c -BBC b b c -BBCBBC b b c b b c -BB&CI b b and c i -BBC&PJR b b c and p j r -BBC's b b c's -B.B.D. b b d -BBDO b b d o -BBE b b e -BBF b b f -BBFC b b f c -BBFF b b f f -BBG b b g -BBH b b h -BBI b b i -BBK b b k -BBKL b b k l -BBLB b b l b -BBL b b l -BBM b b m -BBMP b b m p -BBNG b b n g -BBN's b b n's -BBO b b o -BBP b b p -BBQ'er b b q e r -B&Bs b and b's -BB&S b b and s -BBS b b s -BBSes b b s e's -BBS's b b s's -BB&T b b and t -BBT b b t -BBTV b b t v -BBU b b u -BBVA b b v a -BbvCI b b v c i -BBV's b b v's -BBWAA b b w a a -BBWA b b w a -BBWR's b b w r's -BBYA b b y a -BBYO b b y o -BCA b c a -BCAD b c a d -BCAM b c a m -BCATP b c a t p -BCBA b c b a -BCB b c b -bc b c -B. C. b c -B.C. b c -BC b c -BCCA b c c a -BCC b c c -BCCCA b c c c a -Bcci b c c i -BCCI b c c i -BCCP b c c p -BCC's b c c's -BCDA b c d a -BCD b c d -BCEA b c e a -BCE b c e -BCE's b c e's -BCF b c f -BCG b c g -BCG's b c g's -BCHL b c h l -BCHR's b c h r's -BCHS b c h s -BCI b c i -B. C. J. b c j -BCL b c l -BCLR b c l r -BCMA b c m a -BCM b c m -BCMG b c m g -BCMHS b c m h s -BCMS b c m s -BCN b c n -BCN's b c n's -BCP b c p -BCPM b c p m -BCR b c r -BCRF's b c r f's -B.C.'s b c's -BC's b c's -BCs b c's -BCS b c s -BCSC b c s c -BCSic b c s i c -BCSN b c s n -BCT b c t -BCTC b c t c -BCU b c u -BCYP b c y p -BdA b d a -BDA b d a -bd b d -B. D. b d -B.D. b d -BD b d -BDBL b d b l -BDC b d c -BDC's b d c's -BDD b d d -Bde b d e -B. D. E. b d e -BDE b d e -BDF b d f -BDI b d i -BDJ b d j -BDK b d k -BDK's b d k's -BDMI b d m i -BDN b d n -BDNF b d n f -BDO b d o -BDO's b d o's -BDOS b d o s -BDP b d p -BDR b d r -bds b d s -BDS b d s -BDSM b d s m -BdU b d u -BDU b d u -BDV b d v -B. D. W. b d w -B.E.A. b e a -B. E. b e -B.E. b e -Bedw b e d w -B.E.E. b e e -bef b e f -B.E.F. b e f -BEF b e f -BEF's b e f's -bei b e i -Bei b e i -Beih b e i h -BEIR b e i r -Beis b e i's -bej b e j -B.E.M. b e m -BEMs b e m's -BEP b e p -Ber b e r -BER b e r -Bes b e's -B.E.S. b e s -BES b e s -B.F.A. b f a -BFA b f a -bf b f -B. F. b f -B.F. b f -BF b f -BFCA b f c a -BFC b f c -BFC's b f c's -BFDG b f d g -BFES b f e s -BFF b f f -BFFs b f f's -B. F. G. b f g -BFG b f g -BFG's b f g's -BFI b f i -BFI's b f i's -BFJA b f j a -BFKL b f k l -BFL b f l -BFM b f m -BFO b f o -BFRA b f r a -BFR b f r -BFSA b f s a -BFS b f s -BFT b f t -BFU b f u -bfy b f y -BGAB b g a b -BGA b g a -BGB b g b -B. G. b g -B.G. b g -BGC b g c -BGCI b g c i -BGD b g d -BGEA b g e a -B. G. E. b g e -BGH b g h -B. G. J. S. b g j s -BGL b g l -BglII b g l i i -BGM b g m -BGP b g p -BGRA b g r a -BGR b g r -BGRC b g r c -BGRSO b g r s o -bgs b g s -B.G.S. b g s -BGS b g s -BGSU b g s u -BGT b g t -Bgy b g y -Bha b h a -B&H b and h -B. H. b h -B.H. b h -BHCC b h c c -BHDP b h d p -BH&E b h and e -Bhe b h e -BHL b h l -BHMA b h m a -BHMO b h m o -BHMs b h m's -BHMT b h m t -BHO b h o -B. H. P. b h p -BHP b h p -BHRT b h r t -BHS b h s -BHSN b h s n -BHSU b h s u -bhttp b h t t p -BHU b h u -BHUSD b h u s d -BHVS b h v s -B. I. b i -B.I. b i -Bie b i e -BIE b i e -biedt b i e d t -Biem b i e m -B.I.G. b i g -B.I.G.'s b i g's -BiH b i h -BIH b i h -B.I.O.L.A. b i o l a -B. J. b j -B.J. b j -BJCC b j c c -BJCP b j c p -BJD b j d -B.J.F. b j f -B. J. I. b j i -BJJ b j j -BJP b j p -BJP's b j p's -BJPs b j p's -BJPS b j p s -B.J.'s b j's -B.J.T. b j t -BJT b j t -BJU b j u -BJY b j y -B.K.A. b k a -B&K b and k -B. K. b k -B.K. b k -BKCa b k c a -BKC b k c -BKF b k f -BKI b k i -BKN's b k n's -BKO b k o -B.K.R. b k r -BKR b k r -BK's b k's -BKS b k s -BKT b k t -BKTV b k t v -BKV b k v -B. L. b l -B.L. b l -BL b l -BLC b l c -BLCC b l c c -BLCN b l c n -ble b l e -bleg b l e g -blev b l e v -BLG b l g -bli b l i -BLI b l i -Blla b l l a -BLL b l l -BLM b l m -BLMC b l m c -BLOU's b l o u's -BLPP b l p p -BLR b l r -BLRC b l r c -BLR&D b l r and d -B.L.S. b l s -BLS b l s -BLT b l t -BLTF b l t f -BLTs b l t's -BLV b l v -BMAA b m a a -B.M.A. b m a -BMA b m a -BMAL b m a l -BMARC b m a r c -BMA's b m a's -B&M b and m -BMB b m b -B. M. b m -B.M. b m -BMCA b m c a -B.M.C. b m c -BMC b m c -bmd b m d -BMD b m d -BME b m e -BMF b m f -BMG b m g -BMHS b m h s -bmi b m i -BMI b m i -BMIC b m i c -BMIR b m i r -BMI's b m i's -BMIT b m i t -BMJ b m j -BMK b m k -BMKG b m k g -BMMO b m m o -BMNA b m n a -BMNH b m n h -BMNP b m n p -BMO b m o -BMP b m p -BMPs b m p's -BMR b m r -BMRs b m r's -BMSA b m s a -B.M.S. b m s -BMS b m s -bmt b m t -B. M. T. b m t -B.M.T. b m t -BMT b m t -BMT's b m t's -BMU b m u -B.M.V. b m v -BMV b m v -B. M. W. b m w -BMW b m w -BMW M b m w -BMW's b m w's -BMX b m x -BNA b n a -B&N b and n -bnb b n b -BNB b n b -B. N. b n -B.N. b n -BNC b n c -BNCI b n c i -BNCM b n c m -BNCT b n c t -BND b n d -BNDY b n d y -BNET b n e t -BNF b n f -BNFL b n f l -BNI b n i -BNIC b n i c -BNL b n l -BNO b n o -BNP b n p -BNR b n r -BNRC b n r c -BNS b n s -BNSF b n s f -BNST b n s t -BNT b n t -BNU b n u -BNY b n y -BNZ b n z -Boac b o a c -BOAC b o a c -B&O b and o -B. O. b o -B.O. b o -Boc b o c -BoC b o c -BOC b o c -BOCs b o c's -BOCS b o c s -boj b o j -Boj b o j -BOJ b o j -B.O.M.B. b o m b -B&O's b and o's -B.O.S. b o s -BOYZZ b o y z z -BPA b p a -BPB b p b -bp b p -B. P. b p -B.P. b p -BP b p -BPC b p c -BPD b p d -BPER b p e r -B.P.H. b p h -BPI b p i -BPJ b p j -BPK b p k -bpl b p l -BPL b p l -B. P. M. b p m -BPM b p m -BPMN b p m n -B.P.N. b p n -BPN b p n -B.P.O. b p o -BPO b p o -BPPA b p p a -BPP b p p -BPR b p r -B.P.R.D. b p r d -BPRD b p r d -BPSA b p s a -BP's b p's -BPS b p s -BPSK b p s k -BPUP b p u p -BPV b p v -BPY b p y -B&Q b and q -BQB b q b -B. Q. b q -BRAC's b r a c's -BRAM b r a m -BRBDP b r b d p -Brbic b r b i c -B. R. b r -B.R. b r -BRBR b r b r -BRBs b r b's -BRCA b r c a -BRC b r c -BRCC b r c c -BRD b r d -BRF b r f -BRGF b r g f -BRGM b r g m -BRHS b r h s -B.R.I.C.K. b r i c k -BRK b r k -BRL b r l -BRM b r m -BRMS b r m s -BRN b r n -BRNC b r n c -Brne b r n e -Brno b r n o -BRNV b r n v -BRP b r p -BRPS b r p s -BRRA b r r a -BRS b r s -BRSCC's b r s c c's -BRT b r t -BRTs b r t's -BRTS b r t s -BRVM b r v m -BRW b r w -BRWK b r w k -Brza b r z a -BRZ b r z -brzu b r z u -brzy b r z y -Brzyk b r z y k -BSAA b s a a -B.S.A. b s a -BSA b s a -BSAC b s a c -BsaL b s a l -BSBA b s b a -BSB b s b -BSBI b s b i -B. s b's -B.'s b's -Bs b's -B. S. b s -B.S. b s -BS b s -B.S.C. b s c -BSC b s c -BSCL b s c l -bsd b s d -BSD b s d -BSDE b s d e -BSE b s e -BSET b s e t -BSFA b s f a -BSF b s f -BSG b s g -BSH b s h -BSIB b s i b -BSI b s i -BSK b s k -BSL b s l -BSM b s m -BSME b s m e -BSN b s n -BSNL b s n l -BSOG b s o g -BSPA b s p a -BSP b s p -BSPP b s p p -BSR b s r -BSRN b s r n -BSS b s s -BSU b s u -BSV b s v -BSX b s x -BTA b t a -BTAF b t a f -BTB b t b -BTBD b t b d -bt b t -B. T. b t -B.T. b t -BT b t -BTCA b t c a -BTC b t c -BTCC b t c c -BTD b t d -BTF b t f -BTG b t g -BTH b t h -BTK b t k -BTL b t l -BTM b t m -BTN b t n -BTNK b t n k -BTOB b t o b -BTO b t o -BTP b t p -BTRC b t r c -BTRDA b t r d a -Btry b t r y -btsan b t s a n -BT's b t's -BTS b t s -BTT b t t -BTU b t u -btus b t u s -BTUs b t u's -BTV b t v -B.U. b u -B.U.M. b u m -BUV b u v -BVA b v a -BVB b v b -BVB's b v b's -B. V. b v -B.V. b v -BVC b v c -BVFB b v f b -BVI b v i -BVM b v m -BVO b v o -BVR b v r -BVRC b v r c -BVT b v t -BVVL b v v l -Bwa b w a -BWAF b w a f -B&W b and w -BWB b w b -BWBR b w b r -B. W. b w -B.W. b w -B.W.C. b w c -BWC b w c -BWF b w f -BWFC b w f c -BWHBC b w h b c -B. W. I. b w i -BWI b w i -BWIR b w i r -BWO b w o -BWR b w r -BWS b w s -BWT b w t -BWTs b w t's -BWV b w v -BWW b w w -bwwtv b w w t v -Bxa b x a -BX b x -Bxe b x e -BXML b x m l -BYA's b y a's -BYB b y b -B. Y. b y -byc b y c -BYC b y c -BYD b y d -BYFC b y f c -BYG b y g -bygd b y g d -Byk b y k -byn b y n -BYOB b y o b -Byo b y o -BYO b y o -Byou b y o u -BYR b y r -Byrl b y r l -bySLC b y s l c -BYST b y s t -byt b y t -B.Y.U. b y u -BYU b y u -BYU's b y u's -BYX b y x -BYZ b y z -B.Z. b z -BZ b z -BZD b z d -bzhed b z h e d -Bzik b z i k -BZK b z k -BZP b z p -Bzyb b z y b -CAA c a a -CAAC c a a c -CAAC's c a a c's -CAAHEP c a a h e p -CAAM c a a m -CAAS c a a s -CAASE c a a s e -Caat c a a t -CAAT c a a t -C. A. c a -C.A. c a -CA c a -C&A c and a -CAC c a c -CA&CC c a and c c -CACC c a c c -CACM c a c m -Cadw c a d w -CADW c a d w -caeca c a e c a -Cae c a e -CAE c a e -C.A.F.B. c a f b -CAFTT c a f t t -C. A. G. c a g -CAG c a g -CAGM c a g m -C. A. I. c a i -C. A. J. c a j -CALUX c a l u x -CAMC c a m c -CAMLG c a m l g -C.A.M.'s c a m's -C. A. N. c a n -Capt. captain -C. A. R. c a r -cas c a s -Cas c a's -C.A.S. c a s -CAS c a s -Casc c a s c -CASC c a s c -CASD c a s d -CASF c a s f -CASG c a s g -CASQ c a s q -C. A. T. c a t -C.A.T. c a t -CATV c a t v -C.A.W. c a w -CBA c b a -CBBB c b b b -CBB c b b -CBBC c b b c -CBBS c b b s -CBCA c b c a -cb c b -C. B. c b -C.B. c b -CB c b -cbc c b c -CBC c b c -CB&CNS c b and c n s -CBC's c b c's -CBCS c b c s -CBDA c b d a -CBD c b d -CBDs c b d's -C.B.E. c b e -CBE c b e -CBEF c b e f -CBE's c b e's -CBF c b f -CBGB c b g b -CBGB's c b g b's -CBGBs c b g b's -CBG c b g -CBH c b h -CBHG c b h g -CBI c b i -CBI's c b i's -CbiXS c b i x s -CBKB c b k b -CBK c b k -CBKMT c b k m t -CBKRT c b k r t -CBL c b l -CBL's c b l's -CBM c b m -CBMS c b m s -CBN c b n -CBN's c b n's -CBNT c b n t -CBO c b o -CBOT c b o t -CBP c b p -CBPE c b p e -CB&Q c b and q -CBR c b r -CBRE c b r e -CBRN c b r n -CBRNE c b r n e -CBSA c b s a -CBs c b's -CBS c b s -C.B.S.E. c b s e -CBSE c b s e -CBS's c b s's -CBT c b t -CBTC c b t c -CBTU c b t u -CBU c b u -CBUT c b u t -CBV c b v -CBWT c b w t -CBX c b x -CBYT c b y t -CBZH c b z h -'c c -CcaA c c a a -CCAA c c a a -CCAAT c c a a t -cca c c a -Cca c c a -CCA c c a -CCAF c c a f -CCAP c c a p -CCAR c c a r -CCAS c c a s -CCB c c b -CCBCC c c b c c -CCBE c c b e -CCCA c c c a -C&C c and c -C. C. c c -C.C. c c -CCCE c c c e -CCCF c c c f -CCCP c c c p -CCC's c c c's -CCDB c c d b -CCDC c c d c -ccd c c d -CCD c c d -CCDev c c d e v -CCDI c c d i -CCDI's c c d i's -CCDM c c d m -CCDR c c d r -CCD's c c d's -CCDs c c d's -CCEd c c e d -C.C.E.D. c c e d -CCF c c f -CCFO c c f o -CCG c c g -C.C.G.S. c c g s -CCGS c c g s -CCHA c c h a -CCHC c c h c -C.C.H. c c h -CCH c c h -CCHD c c h d -CCHS c c h s -C.C.I.A.A. c c i a a -CCIAA c c i a a -CCi c c i -CCI c c i -CCID c c i d -CCIE c c i e -CCIH c c i h -CCITT c c i t t -CCJ c c j -CCK c c k -CCLC c c l c -CCL c c l -CCMA c c m a -ccm c c m -CCM c c m -CCMM c c m m -CCMP c c m p -CCMS c c m s -CCN c c n -CCNY c c n y -CCO c c o -CCOKC c c o k c -CCOO c c o o -CCOP c c o p -C.C.O.W.E. E.T.F. c c o w e e t f -CCP c c p -CCPD c c p d -CCPL c c p l -CCPN c c p n -CCP's c c p's -CCQ c c q -CCRC c c r c -CCR c c r -CCRCs c c r c's -CCRH c c r h -CCRS c c r s -CCSA c c s a -CCSC c c s c -CC's c c's -CCS c c s -CCSD c c s d -C.C.S.D.N.Y. c c s d n y -C.C.S.M. c c s m -CCSN c c s n -CCSR c c s r -CCS's c c s's -cct c c t -CCT c c t -CCTF c c t f -CCTRN c c t r n -CCTT c c t t -CCTV c c t v -CCUA c c u a -CCU c c u -ccus c c u s -CCV c c v -CCVG c c v g -C. C. W. c c w -CCWD c c w d -CDA c d a -CDAT c d a t -CDB c d b -CDBS c d b s -CDCA c d c a -C&D c and d -cdc c d c -CDC c d c -cd c d -C. D. c d -C.D. c d -CD c d -CDC's c d c's -CDDB c d d b -CD&DR c d and d r -CDE c d e -CDF c d f -CDFI c d f i -CDFW c d f w -CDH c d h -CDi c d i -CDI c d i -CDISC c d i s c -CDI's c d i's -CDK c d k -CDKN c d k n -CDL c d l -CDLI c d l i -CDLS c d l s -CDMA c d m a -CDM c d m -cDNA c d n a -cdnas c d n a s -cDNAs c d n a's -CDN c d n -CDN's c d n's -CDO c d o -CDOs c d o's -CDPC c d p c -CDP c d p -CDP's c d p's -CDPs c d p's -CD&R c d and r -CDR c d r -cds c d s -CD's c d's -CDs c d's -CDS c d s -CDSG c d s g -CDSP c d s p -CDSPCo c d s p c o -CdtA c d t a -CDT c d t -CdTe c d t e -CDTi c d t i -CDTI c d t i -CDT's c d t's -CDTV c d t v -CDTV's c d t v's -CDU c d u -CDU's c d u's -CD&V c d and v -CDV c d v -CDW c d w -CDWP c d w p -Cec c e c -CEC c e c -c'e c e -ce c e -C'e c e -Ce c e -C. E. c e -C.E. c e -CE c e -CECP c e c p -CEDA c e d a -CEDO c e d o -CEDR c e d r -CEDS c e d s -CEEBA c e e b a -CEEB c e e b -CEEH c e e h -Ceel c e e l -CEEOL c e e o l -CEESA c e e s a -CEFC c e f c -CEF c e f -Cefn c e f n -CEFP c e f p -CEFR c e f r -CEGB c e g b -C.E.G. c e g -C. E. H. c e h -CEI c e i -C&EI's c and e i's -C. E. M. c e m -C&EN c and e n -cen c e n -Cen c e n -CEN c e n -ceo c e o -C.E.O. c e o -CEO c e o -Ceol c e o l -CEOP c e o p -Ceorl c e o r l -Ceos c e o's -CEO's c e o's -CEOs' c e o's -CEOs c e o's -C.E.P. c e p -cer c e r -Cer c e r -CER c e r -CERP c e r p -CERPER c e r p e r -CERS c e r s -ces c e s -Ces c e's -C.E.S. c e s -CES c e s -CESL c e s l -CESO c e s o -CETB c e t b -C. E. T. c e t -CEV c e v -CEVCP c e v c p -CEVG c e v g -CEW c e w -Cex c e x -CeX c e x -CEX c e x -CEZ c e z -CEZMS c e z m s -CFAB c f a b -Cfa c f a -CFA c f a -CFAS c f a s -C. F. B. c f b -CFB c f b -CFBG c f b g -CFBISD c f b i s d -CFB's c f b's -C&F c and f -CFC c f c -cf c f -C. F. c f -C.F. c f -CF c f -CFC's c f c's -CFDA c f d a -CFD c f d -C.F.E. c f e -CFE c f e -CFFC c f f c -C. F. F. c f f -CFF c f f -C. F. H. c f h -CFH c f h -CFHS c f h s -CFI c f i -CFIT c f i t -CFJL c f j l -CFJR c f j r -CFL c f l -CFL's c f l's -CFM c f m -CFMEU c f m e u -CFMI c f m i -CFND c f n d -CFNY c f n y -C.F.O.A. c f o a -CFO c f o -CFOP c f o p -CFOs c f o's -CFPI c f p i -CFPL c f p l -CFQ c f q -CFRB c f r b -C.F.R. c f r -CFR c f r -CFRP c f r p -CFS c f s -CFSCI c f s c i -CFSP c f s p -CFTA c f t a -CFT c f t -CFTO c f t o -CFTR c f t r -CFTs c f t's -CFTXOP c f t x o p -CFU c f u -CFYN c f y n -CFZ c f z -CGA c g a -CGAP c g a p -CGB c g b -C&G c and g -CGCFAD c g c f a d -C. G. c g -C.G. c g -CGCRI c g c r i -CGCS c g c s -CGD c g d -CGDK c g d k -CGE c g e -CGF c g f -CGFP c g f p -CGG c g g -CGI c g i -CGIL c g i l -CGKD c g k d -cgl c g l -CGL c g l -CGP c g p -CGPM c g p m -CGPME c g p m e -CGPW c g p w -CGR c g r -CGRP c g r p -CGS c g s -CGSS c g s s -CGT c g t -CGTG c g t g -CGTIC c g t i c -CGU c g u -CGW c g w -CGW's c g w's -C.H.B. c h b -CHBM c h b m -CHBX c h b x -C&H c and h -C.H.C. c h c -C. H. c h -C.H. c h -CHCO c h c o -CHD c h d -CHDK c h d k -CHDS c h d s -CHF c h f -CHFI c h f i -C.H.G. c h g -CHGO c h g o -CHH c h h -CHHOTO c h h o t o -CHHS c h h s -Chhu c h h u -Chirs c h i r's -CHISMS c h i s m s -CHISZ c h i s z -CHK c h k -CHL c h l -CHLR c h l r -CHL's c h l's -C. H. M. c h m -CHM c h m -CHN c h n -CHP c h p -CHP'den c h p d e n -CHPs c h p's -CHPS c h p s -CHR c h r -CHRDI c h r d i -CHRGD c h r g d -C.H.S. c h s -CHS c h s -CHSE c h s e -CHT c h t -CHV c h v -CHX c h x -Chy c h y -CHYR c h y r -CIAC c i a c -Cia c i a -C.I.A. c i a -CIA c i a -CIC c i c -ci c i -Ci c i -C. I. c i -C.I. c i -CI c i -Cicic c i c i c -CICL c i c l -C.I.C.M. c i c m -CICO c i c o -CIC's c i c's -C.I.D. c i d -CIFF c i f f -CIFL c i f l -CIHE c i h e -CIHR c i h r -cii c i i -CII c i i -CIID c i i d -CIJ c i j -CIKM c i k m -C.I.L. c i l -CIL c i l -cim c i m -Cim c i m -CIM c i m -Cio c i o -C.I.O. c i o -CIO c i o -CIOs c i o's -CIOT c i o t -CIPCA c i p c a -Cip c i p -C.I.P. c i p -CIP c i p -CIPD c i p d -CIPFA c i p f a -CIPM c i p m -CISA c i s a -CISC c i s c -cis c i s -CIs c i's -CIS c i s -CITB c i t b -cit c i t -CIT c i t -CiTD c i t d -CITM c i t m -CITN c i t n -CITP c i t p -citS c i t s -CITV c i t v -CITWF c i t w f -CitX c i t x -Ciuc c i u c -Ciu c i u -CiU c i u -CIU c i u -CIUT c i u t -CIVD c i v d -CIW c i w -CIWS c i w s -CIX c i x -Cixi c i x i -CIY c i y -CIZN c i z n -CJA c j a -CJBHL c j b h l -CJCA c j c a -CJCB c j c b -CJC c j c -C. J. c j -C.J. c j -CJD c j d -C. J. E. c j e -CJFL c j f l -CJFT c j f t -CJFX c j f x -CJGC c j g c -CJGO c j g o -CJHL c j h l -CJK c j k -CJL c j l -CJLS c j l s -CJM c j m -CJNT c j n t -CJOC c j o c -CJP c j p -C. J. R. c j r -C.J.R. c j r -CJR c j r -CJSC c j s c -C. J. S. c j s -CJS c j s -CJT c j t -CJUF c j u f -CJWC c j w c -CKAC c k a c -CKC c k c -C. K. c k -C.K. c k -CK c k -CKD c k d -CKE c k e -CKLM c k l m -CKLQ c k l q -CKLW c k l w -CKS c k s -CKSO c k s o -CKVL c k v l -C. K. W. c k w -CKWK c k w k -ckx c k x -CKXX c k x x -CKY c k y -CLAC c l a c -CLA c l a -CLAS c l a s -CLBs c l b's -clc c l c -CLC c l c -cl c l -C. L. c l -C.L. c l -CL c l -CLCN c l c n -CLD c l d -Cle c l e -CLE c l e -CLF c l f -C.L.G. c l g -CLG c l g -CLHIA c l h i a -CLHS c l h s -CLI c l i -CLK c l k -cllr c l l r -CLM c l m -CLMD c l m d -CLMP c l m p -CL&N c l and n -Clo c l o -CL&P c l and p -CLP c l p -CLRC c l r c -C. L. R. c l r -C.L.R. c l r -CLR c l r -CLRK c l r k -CLSA c l s a -CLSC c l s c -CLs c l's -CLS c l s -CLT c l t -CLTD c l t d -CLTPA c l t p a -CLTs c l t's -Clu c l u -CLU c l u -CLUK c l u k -CLV c l v -Clwyd c l w y d -CLX c l x -C&MA c and m a -CMAC c m a c -C.M.A. c m a -CMA c m a -CMAL c m a l -CMAP's c m a p's -CMAS c m a s -CMAT c m a t -C.M.B. c m b -CMB c m b -CMCC c m c c -CMCCDI c m c c d i -C.M.C. c m c -CMC c m c -cm c m -C. M. c m -C.M. c m -CM c m -CMCs c m c's -CMCS c m c s -CMD c m d -Cmde c m d e -CMDISE c m d i s e -cmdr c m d r -CMEC c m e c -CME c m e -CMF c m f -CMFS c m f s -C. M. G. c m g -C.M.G. c m g -CMG c m g -CMHC c m h c -CMH c m h -CMHR c m h r -CMI c m i -CMJ c m j -CMLA c m l a -cml c m l -C.M.L. c m l -CML c m l -CMLDP c m l d p -CMLL c m l l -CMLL's c m l l's -CMM c m m -CMMI c m m i -CMMs c m m's -cmn c m n -CMO c m o -CMO's c m o's -CMPC c m p c -CMP c m p -CMPS c m p s -CMPSO c m p s o -CMPV c m p v -CMQ c m q -CMQT c m q t -C. M. R. c m r -CMR c m r -CMSAF c m s a f -C.M.S. c m s -CMS c m s -C.M.S.L. c m s l -CMT c m t -Cmte c m t e -CMT's c m t's -CMTU c m t u -CMUCL c m u c l -CMU c m u -CMU's c m u's -CMV c m v -CMVM c m v m -CMVSS c m v s s -CMWF c m w f -CMXXII c m x x i i -CMYK c m y k -CNA c n a -CNBC c n b c -CNB c n b -CNC c n c -cn c n -C. N. c n -C.N. c n -CN c n -CNC's c n c's -CND c n d -CNDL c n d l -CNEB c n e b -CNE c n e -CNF c n f -CNFE c n f e -CNFK c n f k -CNG c n g -CNI c n i -CNJ c n j -CNJF c n j f -CNK c n k -C. N. L. c n l -C.N.L. c n l -CNL c n l -CNMs c n m's -CNN c n n -CNO c n o -CNPC c n p c -CNP c n p -cnr c n r -CNR c n r -CNRMA c n r m a -CNRS c n r s -CNSA c n s a -CNs c n's -CNS c n s -CNSS c n s s -CNSW c n s w -CNTA c n t a -CNT c n t -CNTE c n t e -CNTI c n t i -CNTK c n t k -CNTN c n t n -CNTs c n t's -CNTS c n t s -CNTV c n t v -CNU c n u -Cnut's c n u t's -CNV c n v -CNVs c n v's -CNW c n w -CNWS c n w s -CNZ c n z -COA c o a -C&O c and o -C. O. c o -C.O. c o -Co. company -C.O.D. c o d -Col. colonel -C.O.M.L. c o m l -CPAC c p a c -CPA c p a -CPA's c p a's -CPB c p b -CPBL c p b l -C&P c and p -CPC c p c -cp c p -C. P. c p -C.P. c p -CP c p -CPC's c p c's -CPCs c p c's -CPCS c p c s -CPD c p d -CPDM c p d m -CPD's c p d's -CPEB c p e b -CPEC c p e c -C.P.E. c p e -CPE c p e -CPEO c p e o -CPF c p f -CPFO c p f o -CPG c p g -CPHC c p h c -CPH c p h -CPI c p i -CPIFL c p i f l -CPIM c p i m -CPI's c p i's -CPJ c p j -CPL c p l -CPLP c p l p -CPM c p m -CPN c p n -CPNI c p n i -cpo c p o -C.P.O. c p o -CPO c p o -CPOE c p o e -CPPA c p p a -CPPCC c p p c c -CPP c p p -CPPE c p p e -CPPIB c p p i b -CPPM c p p m -CPQ c p q -CPR c p r -CPRS c p r s -CPSA c p s a -CPSC c p s c -CPS c p s -CPSF c p s f -CPSK c p s k -CPSL c p s l -CPSU c p s u -CPTA c p t a -CPT c p t -CPTM c p t m -CPUC c p u c -CPU c p u -CPUID c p u i d -CPUSA c p u s a -CPUs c p u's -CPUT c p u t -CPVA c p v a -CPV c p v -CPW c p w -CQAC c q a c -CQBR c q b r -CQC c q c -cq c q -CQ c q -CQD c q d -CQDs c q d's -CQM c q m -CQO c q o -CQU c q u -C. R. A. c r a -CRA c r a -C.R.A.Z.Y. c r a z y -CRBC c r b c -CRC c r c -cr c r -C. R. c r -C.R. c r -CR c r -Cre c r e -CRE c r e -CRF c r f -CRG c r g -CRHK c r h k -CRH's c r h's -CRHS c r h s -CRJ c r j -crk c r k -Crkva c r k v a -crkve c r k v e -CRL c r l -CRLs c r l's -crm c r m -CRM c r m -CRNA c r n a -CRN c r n -Crne c r n e -Crni c r n i -Crno c r n o -CRP c r p -CRPF c r p f -CRRF c r r f -CRs c r's -CRS c r s -CRSP c r s p -CRST c r s t -CRTC c r t c -CRT c r t -CRTC's c r t c's -CRUA c r u a -CRWRC c r w r c -CRW's c r w's -CRX c r x -CSAC c s a c -csa c s a -Csa c s a -C.S.A. c s a -CSA c s a -CSAH c s a h -Csak c s a k -Csaky c s a k y -CSAR c s a r -CSAT c s a t -CSAV c s a v -CSB c s b -C&S c and s -C.S.C. c s c -CSC c s c -CSCD c s c d -CSCE c s c e -CSCR c s c r -cs c s -C.s c's -Cs c's -C. S. c s -C.S. c s -CS c s -CSC's c s c's -CSDA c s d a -CSDB c s d b -CSD c s d -CSDL c s d l -CSDN c s d n -CSDP c s d p -CSD's c s d's -CSDS c s d s -CSE c s e -C&SF c and s f -CSF c s f -CSFL c s f l -CSFSO c s f s o -CSGA c s g a -csg c s g -CSG c s g -CSG's c s g's -CSH c s h -CSHL c s h l -CSIC c s i c -CsI c s i -CSI c s i -CSIDC c s i d c -CSILUS c s i l u s -CSIO c s i o -CSIR c s i r -CSIRO c s i r o -CSis c s i's -CSIs c s i's -CSIS c s i s -CSIU c s i u -CSKA c s k a -CSKA's c s k a's -CSK c s k -CSK's c s k's -CSL c s l -CSLI c s l i -CSM c s m -CSNB c s n b -CSNBX c s n b x -CSN c s n -CSN's c s n's -CSOB c s o b -CSO c s o -CSO's c s o's -CSOs c s o's -cspA c s p a -csp c s p -CSP c s p -CSPH c s p h -CSPI c s p i -CSPRA c s p r a -CSPs c s p's -CSRA c s r a -CSR c s r -CSRS c s r s -CSRT c s r t -CSRTT c s r t t -CSSA c s s a -CSS c s s -CSSD c s s d -CSSE c s s e -CSSL c s s l -CSSNCs c s s n c's -CSSR c s s r -CSSS c s s s -CSST c s s t -CSTB c s t b -CST c s t -CSTF c s t f -CSTI c s t i -CSTS c s t s -CSUB c s u b -CSU c s u -CSU's c s u's -Csuz c s u z -CSV c s v -CSW c s w -CSWF c s w f -CSWIP c s w i p -CSWS c s w s -CSX c s x -CSX's c s x's -CSXT c s x t -C.T.A. c t a -CTA c t a -CTAF c t a f -CTARL c t a r l -CTA's c t a's -CTAs c t a's -CTBT c t b t -CTBTO c t b t o -C&T c and t -CTC c t c -ct c t -C. T. c t -C.T. c t -CT c t -CTD c t d -cte c t e -CTE c t e -CTEQ c t e q -CTF c t f -CTGF c t g f -CTI c t i -CTLA c t l a -CTL c t l -CTLLS c t l l s -CTLs c t l's -CTM c t m -CTN c t n -CTO c t o -CTP c t p -ctr c t r -CTRG c t r g -Ctrip c t r i p -CTRL c t r l -CTRP c t r p -CTR's c t r's -CTS c t s -CTSD c t s d -CTSF c t s f -CTSG c t s g -CTUC c t u c -CTU c t u -CTUG c t u g -CTU's c t u's -CTVA c t v a -ctv c t v -CTV c t v -CTV's c t v's -CTX c t x -CTZ c t z -cu c u -C. U. c u -CU c u -C.U.L. c u l -C. U. S. c u s -C. U. T. c u t -CUW c u w -Cuyp c u y p -CVA c v a -CVAK c v a k -CVARG c v a r g -CVB c v b -C&V c and v -CVC c v c -cvcp c v c p -'cv c v -cv c v -C. V. c v -C.V. c v -CV c v -CVD c v d -CVEs c v e's -CVG c v g -CVI c v i -CVJM c v j m -CVM c v m -C.V.N. c v n -CVN c v n -C.V.O. c v o -CVO c v o -CVODE c v o d e -CVOs c v o's -CVP c v p -CVR c v r -CVs c v's -CVS c v s -CVSNT c v s n t -CVT c v t -CVTs c v t's -CVV c v v -CVVHDF c v v h d f -CVW c v w -CVZ c v z -CWA c w a -CWBI c w b i -CWBL c w b l -CWC c w c -C. W. c w -C.W. c w -CWD c w d -CWDS c w d s -C. W. E. c w e -CWE c w e -Cwej c w e j -Cwele c w e l e -CWF c w f -CWGC c w g c -CWG c w g -CWI c w i -C. W. K. c w k -cwm c w m -CWM c w m -CWO c w o -CWPA c w p a -CWR c w r -cwrt c w r t -CWRU c w r u -CWSAC c w s a c -CW's c w's -CWS c w s -CWTS c w t s -CWUR c w u r -C. W. Y. c w y -CWYFL c w y f l -CXC c x c -CXCL c x c l -CXCR c x c r -C.X. c x -C. Y. c y -C.Y. c y -cyl c y l -Cyn c y n -CYOG c y o g -CyP c y p -CYP c y p -Cyrl c y r l -Cys c y's -CYS c y s -CYSK c y s k -CYSY c y s y -czci c z c i -cz c z -C.Z. c z -CZ c z -CZE c z e -CZM c z m -CZW c z w -czy c z y -DAAD d a a d -Daai d a a i -D. A. d a -D.A. d a -D&AD d and a d -D. A. H. d a h -D.A.M. d a m -D.A.N.C.E. dance -D. A. T. d a t -Dav d a v -D.A.V. d a v -DAV d a v -DAV's d a v's -dBa d b a -DBA d b a -DBC d b c -DBCS d b c s -db d b -D. B. d b -D.B. d b -DB d b -DBE d b e -DBI d b i -DBL d b l -DBLE d b l e -DBLP d b l p -DBM d b m -DBMS d b m s -DBR d b r -DB's d b's -D. B. S. d b s -DBS d b s -DBU d b u -DBYC d b y c -DBZ d b z -DCA d c a -DCAF d c a f -DCAM d c a m -DCB d c b -DCCC d c c c -DCCCD d c c c d -DCC d c c -DCCG d c c g -DCCs d c c's -D&C d and c -dc d c -D. C. d c -D.C. d c -DC d c -DCE d c e -DCFC d c f c -DCF d c f -DCG d c g -DCH d c h -DCI d c i -DCK d c k -D.C.L. d c l -DCL d c l -DCMA d c m a -DCM d c m -DCMF d c m f -DCMI d c m i -DCMS d c m s -DCO d c o -DCom d c o m -DCOM d c o m -DCP d c p -DCR d c r -D.C.'s d c's -DC's d c's -DCs d c's -DCS d c s -DCSD d c s d -DCSO d c s o -DCSPER d c s p e r -DCSS d c s s -DCTA d c t a -DCT d c t -DCU d c u -DCUO d c u o -DCUs d c u's -DDA d d a -DDB d d b -D.D.C. d d c -DDC d d c -D&D d and d -D. D. d d -D.D. d d -DDF d d f -DDG d d g -DDHH d d h h -DDi d d i -DDI d d i -DDK d d k -DDLJ d d l j -DDM d d m -DDMS d d m s -DDN d d n -DDNOS d d n o s -Ddoc d d o c -DDO d d o -DDP d d p -DDR d d r -DDRMAX d d r m a x -DDSB d d s b -D.D.S. d d s -DDS d d s -DDT d d t -DDTV d d t v -Ddu d d u -DDWFTTW d d w f t t w -DDX d d x -D.E.A.F. d e a f -Deah d e a h -DEA's d e a's -DECC d e c c -DEC d e c -DECS d e c s -DEDD d e d d -D. E. d e -D.E. d e -deg d e g -D. E. G. d e g -DEG d e g -DEH d e h -DEHP d e h p -dei d e i -Dei d e i -DeI d e i -DEI d e i -Deijm d e i j m -Deip d e i p -D. E. J. d e j -D.E.R. d e r -dez d e z -Dez d e z -Dfa d f a -DFA d f a -DFB d f b -D.F.C. d f c -DFC d f c -DFCs d f c's -D. F. d f -D.F. d f -DFD's d f d's -DfE d f e -D.F.E. d f e -DFE d f e -DfES d f e s -DFF d f f -DFG d f g -DFJ d f j -DFL d f l -DFM d f m -DFOA d f o a -DFO d f o -DFP d f p -DFPs d f p's -DFRC d f r c -DFR d f r -DFs d f's -DFS d f s -DFT d f t -DFW d f w -dfx d f x -DFX d f x -DGAC d g a c -DGA d g a -DGALS d g a l s -DGAP d g a p -DGB d g b -DGCA d g c a -DGC d g c -DGCR d g c r -D&G d and g -D. G. d g -D.G. d g -D. G. E. d g e -DGE d g e -DGERT d g e r t -DGF d g f -DGFI d g f i -DGH d g h -DGK d g k -D. G. L. R. d g l r -DGM d g m -DGMO d g m o -DGP d g p -DGPS d g p s -DGR d g r -Dha d h a -DHA d h a -DHB d h b -DHBs d h b's -DHCP d h c p -D&H d and h -dh d h -D. H. d h -D.H. d h -DH d h -DHEA d h e a -d'HEC d h e c -dhe d h e -DHE d h e -Dheku d h e k u -DHFS d h f s -DHHC d h h c -DHH d h h -DHL d h l -DHMO d h m o -DHNC d h n c -D.H.N. d h n -DHNS d h n s -Dhod d h o d -dhol d h o l -Dhol d h o l -Dhoo d h o o -Dhor d h o r -Dhou d h o u -DHP d h p -DHPH d h p h -DHQ d h q -dhr d h r -D.H.R. d h r -DHRS d h r s -DHS d h s -DHSs d h s's -DHSS d h s s -DHT d h t -DHV d h v -DHW d h w -DHX d h x -D.I. d i -DIFC d i f c -DISD d i s d -DIW d i w -DIX d i x -diy d i y -DiY d i y -D.I.Y. d i y -DIY d i y -Diyn d i y n -DIYs d i y's -D. J. A. d j a -Djam d j a m -dj d j -D. J. d j -D.J. d j -DJ d j -DJ'ed d j e d -Djem d j e m -D.J.H. d j h -Dji d j i -DJI d j i -D.J.N. d j n -Djo d j o -D'Jok d j o k -djr d j r -DJR d j r -D.J.s d j's -DJ's d j's -DJs d j's -D. J. S. d j s -D.J.T. d j t -DjVu d j v u -DJVU d j v u -DjVus d j v u's -D. J. Y. d j y -DKC d k c -D. K. d k -D.K. d k -DKI d k i -DKIM d k i m -DKK d k k -DKNY d k n y -DKPP d k p p -D. K. R. d k r -DKW d k w -dla d l a -DLA d l a -DLC d l c -DLD d l d -dl d l -D. L. d l -D.L. d l -DL d l -DLE d l e -D.L.F. d l f -DLF d l f -DLFN d l f n -DLG d l g -D. L. H. d l h -dlia d l i a -DLI d l i -DLJ d l j -DLJ's d l j's -DLK d l k -DLL d l l -DL&LR d l and l r -DLL's d l l's -DLLs d l l's -DLM d l m -DLMPST d l m p s t -dlo d l o -DLP d l p -DLPFC d l p f c -DLP's d l p's -DLR d l r -DLS d l s -DLSS d l s s -DLSU d l s u -DLSU's d l s u's -DLT d l t -DL&W d l and w -DLZ d l z -DMAA d m a a -DMA d m a -DMB d m b -DMBT d m b t -DMCA d m c a -DMCC d m c c -DMC d m c -DMCM d m c m -D.M.C.'s d m c's -D&M d and m -DMD d m d -DMDK d m d k -D. M. d m -D.M. d m -DM d m -DMDS d m d s -DMed d m e d -DM&E d m and e -DME d m e -DMF d m f -DMG d m g -DMGT d m g t -DMHS d m h s -DMI d m i -DMK d m k -DML d m l -DMN d m n -DMO d m o -DMOFD d m o f d -DMOZ d m o z -DMP d m p -DMRC d m r c -DMR d m r -DMRG d m r g -DMs d m's -DMS d m s -DMSII d m s i i -DMT d m t -DMTF d m t f -DMU d m u -DMUs d m u's -DMV d m v -DMX d m x -DMZ d m z -Dna d n a -DnA d n a -DNA d n a -DNADTC d n a d t c -DNAITV d n a i t v -DNAJA d n a j a -DNAJC d n a j c -DnaJ d n a j -DNAN d n a n -DNB d n b -DNBY d n b y -DNC d n c -DNCG d n c g -DND d n d -D. N. d n -D.N. d n -Dnepr d n e p r -dnes d n e s -Dnes d n e's -DNES d n e s -DNF d n f -DNF's d n f's -DNFs d n f's -DNG d n g -DNJO d n j o -DNKA d n k a -DNK d n k -DNM d n m -DNMT d n m t -DNP d n p -DNQ d n q -DNR d n r -DNSBLs d n s b l's -dns d n s -D.N.S. d n s -DNS d n s -DNSS d n s s -DNT d n t -DNU d n u -DNVP d n v p -D.O.A. d o a -D.O.A.'s d o a's -DOAS d o a s -D.O.B. d o b -D.O.C. d o c -DOCG d o c g -dod d o d -Dod d o d -DoD d o d -DOD d o d -D. O. d o -D.O. d o -DOD's d o d's -D.O.E. d o e -DOGNY d o g n y -DOHC d o h c -D'oh d o h -DOJ d o j -DOJ's d o j's -dok d o k -Dok d o k -domt d o m t -Domt d o m t -Dop d o p -DOP d o p -DOPR d o p r -DOSAAF d o s a a f -DOTC d o t c -dotCMS d o t c m s -DOTD d o t d -DOTP d o t p -DotSVN d o t s v n -Douw d o u w -DOW d o w -DOXO d o x o -dozd d o z d -d'OZ d o z -Dozhd d o z h d -DPAA's d p a a's -DPA d p a -DPB d p b -DPD d p d -dp d p -D. P. d p -D.P. d p -DP d p -DPG d p g -DPJ d p j -DPMA d p m a -DPM d p m -DPN d p n -DPNM d p n m -DPO d p o -dpon d p o n -DPP d p p -DPP's d p p's -DPPX d p p x -DPR d p r -DPRK d p r k -DPRK's d p r k's -DProf d p r o f -DPRP d p r p -DPRT d p r t -DPs d p's -DPS d p s -DPSS d p s s -DPSSL d p s s l -DPT d p t -DPW d p w -DPW's d p w's -DPX d p x -DPYS d p y s -D.Q. d q -DQOY d q o y -DQV d q v -DRB d r b -DRBG d r b g -DRB's d r b's -DRCA d r c a -drc d r c -DRC d r c -DRDA d r d a -DRDC d r d c -DRD d r d -Dr. doctor -D. R. d r -D.R. d r -DRG d r g -dri d r i -Dri d r i -DRI d r i -DRIs d r i's -DRITTES d r i t t e s -Driu d r i u -Driv d r i v -DRJTBC d r j t b c -DRK d r k -DRL d r l -DRLR d r l r -DRMC d r m c -D.R.M. d r m -DRM d r m -Drnis d r n i's -Dro d r o -DRO d r o -DRP d r p -Drs. doctors -DR's d r's -DRS d r s -Drska d r s k a -DRT d r t -druj d r u j -DSA d s a -DsbA d s b a -DS&BB d s and b b -DSB d s b -DSBL d s b l -D.S.C. d s c -DSC d s c -D.S.C.H. d s c h -DSCM d s c m -DSCR d s c r -DSCS d s c s -DSDB d s d b -DSD d s d -DSDM d s d m -dsDNA d s d n a -DSDP d s d p -ds d s -Ds d's -D. S. d s -D.S. d s -DS d s -DSDS d s d s -DSG d s g -DSGi d s g i -DSIC d s i c -dsi d s i -DSi d s i -DSI d s i -DSIF d s i f -DSIR d s i r -DSJ d s j -dsl d s l -D. S. L. d s l -DSL d s l -DSLR d s l r -DSMB d s m b -D.S.M. d s m -DSM d s m -DSMs d s m's -dsn d s n -D&SNGRR d and s n g r r -DSNP d s n p -D.S.O. d s o -DSO d s o -DSPA d s p a -DSP d s p -DSR d s r -DS's d s's -DSS d s s -DST d s t -dsu' d s u -DSU d s u -DSV d s v -DSW d s w -DSX d s x -DSZG d s z g -D.T.A. d t a -DTA d t a -DTC d t c -DTC's d t c's -DTCs d t c's -DTDC d t d c -DTD d t d -DTDs d t d's -D. T. d t -D.T. d t -DT&E d t and e -DTE d t e -DTEK d t e k -DTES d t e s -DTG d t g -DTH d t h -DTIC d t i c -DT&I d t and i -DTI d t i -DTLA d t l a -dtl d t l -DTL d t l -DTLLS d t l l s -DTLS d t l s -DTM d t m -DTO d t o -DTPA d t p a -DTPC d t p c -DTRA d t r a -DTR d t r -DTRS d t r s -DTs d t's -DTS d t s -DTT d t t -D.T.U. d t u -DTU d t u -DTV d t v -dty d t y -D.U. d u -dva d v a -Dva d v a -DVA d v a -DVB d v b -DVBE d v b e -DVBIC d v b i c -DVC d v c -DVCs d v c's -DVCS d v c s -dvd d v d -DVD d v d -DVD's d v d's -DVDs d v d's -dv d v -D. V. d v -D.V. d v -DV d v -dve d v e -dvfb d v f b -DVFS d v f s -DVGS d v g s -DVHS d v h s -dvi d v i -DVI d v i -dvije d v i j e -Dvin d v i n -Dvir d v i r -DVLA d v l a -DVLA's d v l a's -DVL d v l -DVM d v m -DVP d v p -DVR d v r -DVRJRBC d v r j r b c -DVR's d v r's -DVSC d v s c -DVS d v s -DVT d v t -DVV d v v -DVVT d v v t -DVWK d v w k -Dwa d w a -DWA d w a -D&W d and w -D. W. d w -D.W. d w -Dwedw d w e d w -DWET d w e t -DWF d w f -DWG d w g -D.W.H. d w h -Dwi d w i -DWI d w i -DWM d w m -DWN d w n -DWNU d w n u -DWP d w p -dwr d w r -DWR d w r -DWRFC d w r f c -DWS d w s -DWT d w t -DWTS d w t s -dwur d w u r -dxa d x a -DXA d x a -dx d x -D.X. d x -DX d x -DXED d x e d -DXers d x e r's -DXII d x i i -DXK d x k -DXR d x r -DXZ d x z -Dydd d y d d -dy d y -Dy d y -D. Y. d y -DY d y -DYFI d y f i -DYIS d y i s -DYNC d y n c -dypl d y p l -dyr d y r -DYSP d y s p -DZBB d z b b -DZB d z b -dz d z -DZEC d z e c -DZEM d z e m -dzis d z i s -DZNE d z n e -Dzog d z o g -Dzor d z o r -DZRH d z r h -DZRH's d z r h's -DZS d z s -Dzus d z u's -DZXL d z x l -DZZ d z z -EAAB e a a b -EAA e a a -EAAP e a a p -E. A. C. e a c -EAC e a c -EACEF e a c e f -EACS e a c s -EAD e a d -E. A. e a -E.A. e a -EAF e a f -EAGE e a g e -E.A.H. e a h -EAH e a h -EAI e a i -EAIE e a i e -E. A. J. e a j -EAMs e a m's -EAP e a p -E.A.R.L. e a r l -E.A.R.T.H. e a r t h -EASA e a s a -EASB e a s b -eas e a s -Eas e a's -EA's e a's -EAS e a s -Eav e a v -EAX e a x -E. B. e b -E.B. e b -EBL e b l -Ebn e b n -EBNF e b n f -EBOV e b o v -E&BR e and b r -EBR e b r -Ebru e b r u -EBS e b s -EBSL e b s l -EBT e b t -EBU e b u -EBV e b v -E.B.W. e b w -EBWs e b w's -EBZR e b z r -ECAC e c a c -ECA e c a -ECAR e c a r -ECB e c b -ECBJ e c b j -ECBL e c b l -ECCA e c c a -ECCC e c c c -ecc e c c -ECC e c c -ECCI e c c i -Eccl e c c l -ECCRFA e c c r f a -ECCW e c c w -ECD e c d -ec e c -Ec e c -E. C. e c -E.C. e c -EC e c -ece e c e -Ece e c e -ECE e c e -Ecem e c e m -ECFA e c f a -ECF e c f -ECFR e c f r -ECFU e c f u -ecg e c g -ECG e c g -ECH e c h -ECHL e c h l -ECHR e c h r -ECI e c i -ECJ e c j -ECLAC e c l a c -ECLA e c l a -ECLC e c l c -ECL e c l -ECLI e c l i -ECLSS e c l s s -ecma e c m a -ECMA e c m a -ECMC e c m c -ECMDB e c m d b -E.C.M. e c m -ECM e c m -ECMI e c m i -ECML e c m l -ECMLRUS e c m l r u s -ECMWF e c m w f -ECN e c n -ECNP e c n p -ECNs e c n's -ECNS e c n s -ECNZ e c n z -ECP&DA's e c p and d a's -ECP e c p -ECPR e c p r -ECQ e c q -ECR e c r -eCRM e c r m -ECRR e c r r -ECR's e c r's -ECSA e c s a -EC's e c's -E. C. S. e c s -E.C.S. e c s -ECS e c s -ect e c t -E. C. T. e c t -ECT e c t -ECTLO e c t l o -ECTN e c t n -ECTS e c t s -ECV e c v -ECVs e c v's -ECWA e c w a -ECW e c w -edb e d b -EDB e d b -EDC e d c -E. D. e d -E.D. e d -EDF e d f -Edh e d h -EDH e d h -edhe e d h e -EDHF e d h f -EDIB e d i b -EDI e d i -EDK e d k -EDL e d l -EDMA e d m a -EDM e d m -EDMs e d m's -EDMW e d m w -Edmx e d m x -edn e d n -Edn e d n -EDN e d n -EDP e d p -EDQH e d q h -E.D.R. e d r -EDRP e d r p -EDSA e d s a -EDSC e d s c -eds e d s -Eds e d's -E. D. S. e d s -EDS e d s -EDSP e d s p -EDTA e d t a -EDT e d t -EDVAC e d v a c -EDWDO e d w d o -Edw e d w -EDXRD e d x r d -EEA e e a -EEAF e e a f -EEAS e e a s -EEBC e e b c -Eeb e e b -EEC e e c -EECS e e c s -E. E. e e -E.E. e e -Eef e e f -EEF e e f -Eega e e g a -EEI e e i -EEI's e e i's -EEJF e e j f -EELC e e l c -eene e e n e -EEOC e e o c -EEPCO e e p c o -eep e e p -EEP e e p -EERC e e r c -EERI e e r i -Ees e e's -EES e e s -EEST e e s t -EEUU e e u u -eeuw e e u w -Eeuw e e u w -EEZs e e z's -EFA e f a -EFAS e f a s -EFCC e f c c -efc e f c -EFC e f c -EFCJ e f c j -EFD e f d -ef e f -Ef e f -E. F. e f -E.F. e f -EF e f -E. F. J. e f j -E. F. K. e f k -EFL e f l -EFM e f m -EFNB e f n b -EFPA e f p a -EFP e f p -EFQUEL e f q u e l -EFR e f r -EFRU e f r u -EFSA e f s a -eFS e f s -Efs e f's -EFSET e f s e t -EFTPOS e f t p o s -EFUA e f u a -Efu e f u -EFV e f v -EFVR e f v r -EFVS e f v s -E. F. W. e f w -EFX e f x -EFY e f y -E. G. A. e g a -EGA e g a -Egba e g b a -EGBA e g b a -EGBT e g b t -EGC e g c -EGD e g d -EGF e g f -egfl e g f l -EGFL e g f l -e.g. for example -e. g. for example -EGFR e g f r -EG&G e g and g -E. G. H. e g h -Egi e g i -EGL e g l -EGM e g m -E&GR e and g r -EGR e g r -EGREM e g r e m -EGSC e g s c -EGX e g x -egy e g y -EGY e g y -Egyl e g y l -EHAAT e h a a t -EHAs e h a's -EHC e h c -EHCI e h c i -EHD e h d -E. H. e h -E.H. e h -ehf e h f -EHF e h f -E. H. M. e h m -EHP e h p -EHRC e h r c -EHR e h r -EHS e h s -EHSH e h s h -Ehttp e h t t p -EIA e i a -EIC e i c -EiCs e i c's -EIC's e i c's -E. I. e i -E.I. e i -EIES e i e s -eIF e i f -EIF e i f -EIFF e i f f -EIF's e i f's -EIT e i t -EITI e i t i -Eitr e i t r -EITs e i t's -EIU e i u -E. J. A. e j a -EJB e j b -EJBs e j b's -EJC e j c -ej e j -E. J. e j -E.J. e j -EJ e j -E. J. H. e j h -E.J.H. e j h -EJH e j h -EJHL e j h l -EJMA e j m a -E. J. M. e j m -EJML e j m l -E. J. R. e j r -E.J.S. e j s -EJSM e j s m -EJV e j v -EKB e k b -EKBO e k b o -EKCO's e k c o's -EKD e k d -E. K. e k -E.K. e k -EKF e k f -EKG e k g -eki e k i -EKIZ e k i z -EKL e k l -EKLR e k l r -Ekma e k m a -EKNU e k n u -Ekow e k o w -eks e k s -Eks e k's -EKS e k s -E. K. U. e k u -EKU e k u -E. L. C. e l c -E. L. e l -E.L. e l -ELH e l h -ELTs e l t's -ELUs e l u's -ELV e l v -EMA e m a -EMAS e m a s -Embd e m b d -EMB e m b -eMC e m c -EMC e m c -EMD e m d -EMD's e m d's -EMDs e m d's -EMDS e m d s -Emea e m e a -EMEA e m e a -E&M e and m -E. M. e m -E.M. e m -EMF e m f -Emge e m g e -EMG e m g -EMLL e m l l -E.M.M. e m m -EMNLP e m n l p -EMP e m p -emra e m r a -EMR e m r -EMSC e m s c -E.M.S. e m s -EMTE e m t e -EMT e m t -EMV e m v -E&NA e and n a -Enas e n a's -ENAS e n a s -EnBW e n b w -ENC e n c -EncFS e n c f s -Encyc e n c y c -E.N.D. e n d -ENEC e n e c -E. N. e n -E.N. e n -Enes e n e's -engl e n g l -Engl e n g l -ENGM e n g m -Engr e n g r -Eni e n i -ENI e n i -enn e n n -Enn e n n -ENP e n p -ENR e n r -ens e n s -Ens e n's -ENS e n s -Enso e n s o -ENSO e n s o -ENTJs e n t j's -ENTP e n t p -ENTV e n t v -EOC e o c -EOC's e o c's -EoD e o d -EOD e o d -E. O. e o -E.O. e o -EOFFTV e o f f t v -EOG e o g -Eois e o i's -EOKA e o k a -eok e o k -Eole e o l e -Eol e o l -EOL e o l -Eolss e o l s's -Eom e o m -EOOW e o o w -eop e o p -eoptta e o p t t a -Eora e o r a -EOR e o r -Eorl e o r l -Eors e o r's -EOSHD e o s h d -EOTC e o t c -EOTDC e o t d c -EOU e o u -E.O.W. e o w -E&P e and p -Epe e p e -E. P. E. e p e -EPEG e p e g -ep e p -Ep e p -E. P. e p -E.P. e p -EP e p -EPF e p f -EPFL e p f l -EPG e p g -EPHAR e p h a r -EPHB e p h b -Eph e p h -EPH e p h -Ephs e p h's -EPIA e p i a -epi e p i -Epi e p i -EPI e p i -E.P.J. e p j -EPL e p l -EPLF e p l f -EPM e p m -EPOC e p o c -ePO e p o -EPO e p o -epos' e p o's -epos e p o s -Epos e p o's -EPRDF e p r d f -EPR e p r -EPRI e p r i -EPRO e p r o -EPSC e p s c -E.P.s e p's -EPT e p t -EPV e p v -EPZA e p z a -EPZ e p z -EQAO e q a o -eq e q -Eq e q -EQ e q -EQG e q g -EQN e q n -EQP e q p -EQS e q s -EQT e q t -E.R.A. e r a -ERAF e r a f -ERAU e r a u -ERCB e r c b -ERC e r c -ERCES e r c e s -ERCIM e r c i m -ERCS e r c s -E. R. D. e r d -E. R. e r -E.R. e r -ERF e r f -ERH e r h -ERJs e r j's -ERK e r k -ERP e r p -ERPM e r p m -ERPMRP e r p m r p -ERRC e r r c -E. R. R. e r r -E.R.R. e r r -ERSAR e r s a r -ERT e r t -Eru e r u -ERU e r u -ERUUF e r u u f -Eruv e r u v -Erv e r v -ERW e r w -ES3 e s three -Esa e s a -E.S.A. e s a -ESA e s a -Esam e s a m -Esa's e s a's -ESA's e s a's -Esat e s a t -ESB e s b -ESC e s c -ESCHL e s c h l -ESCs e s c's -Esd e s d -ESD e s d -ESDI e s d i -E.S.E. e s e -E. S. e s -E.S. e s -ESF e s f -ESFG e s f g -E.S.G. e s g -ESG e s g -ESGR e s g r -esi e s i -ESI e s i -ESL e s l -ESM e s m -ESMLC e s m l c -ESMT e s m t -ESNAGI e s n a g i -ESN e s n -ESPA e s p a -ESPC e s p c -ESPD e s p d -ESP e s p -espn e s p n -ESPN e s p n -ESPNhttp e s p n h t t p -ESPN's e s p n's -ESPNU e s p n u -ESPNW's e s p n w's -esq e s q -Esq e s q -Esq. esquire -Esraa e s r a a -Esra e s r a -ESRB e s r b -ESRC e s r c -Esref e s r e f -ESR e s r -ESRF e s r f -Esri e s r i -ESRI e s r i -ESSR e s s r -estd e s t d -Estd e s t d -EST e s t -ESTP e s t p -esu e s u -ESU e s u -ESU's e s u's -Esva e s v a -ESV e s v -ESWAT e s w a t -ESW e s w -Etad e t a d -ETA e t a -ETAF e t a f -ETA's e t a's -ETASU e t a s u -ETB e t b -ETBs e t b's -E.T.C. e t c -ETCO's e t c o's -ETCS e t c s -ETCSL e t c s l -ETD e t d -E. T. e t -E.T. e t -ETF e t f -ETFs e t f's -ETFS e t f s -Et'hem e t h e m -ETH e t h -Eti e t i -ETI e t i -ETIM e t i m -ETIM's e t i m's -etj e t j -ETJ e t j -ETL e t l -ETNZ e t n z -Eto e t o -ETO e t o -ETRM e t r m -ETRS e t r s -ETSEIB e t s e i b -ets e t s -Ets e t's -ET's e t's -ETs e t's -ETS e t s -ett e t t -Ett e t t -ETT e t t -Ettre e t t r e -Etts e t t's -ETU e t u -ETV e t v -Etz e t z -EUA e u a -EUB e u b -EUBs e u b's -EUBS e u b s -EUCAP e u c a p -EUCE e u c e -EUC e u c -EUCOM's e u c o m's -EUDEC e u d e c -EUD e u d -E. U. e u -E.U. e u -EU e u -eup e u p -EUP e u p -Eurwg e u r w g -EUSA e u s a -Euse e u s e -EU's e u's -EUS e u s -EUSEW e u s e w -Eusi e u s i -EUV e u v -EUX e u x -EVAAP e v a a p -EvaGT e v a g t -EVDC e v d c -EVD e v d -EVDO e v d o -E. V. e v -E.V. e v -EVEX e v e x -EVIT e v i t -evl e v l -EVL e v l -EVM e v m -Evna e v n a -EVN e v n -EVNS e v n s -evnt e v n t -E.V.O. e v o -E.V.O.L. e v o l -EVP e v p -EVR e v r -EVs e v's -EVS e v s -evv e v v -EVV e v v -Ewa's e w a's -EWD e w d -EWEB e w e b -E. W. e w -E.W. e w -EWF e w f -EWF's e w f's -Ewha e w h a -EWH e w h -E.W.K. e w k -EWLA e w l a -EWL e w l -EWO e w o -EWOs e w o's -EWP e w p -EW's e w's -EWS e w s -EWTN e w t n -Ewu e w u -EWU e w u -excl e x c l -exd e x d -E.X. e x -exhb e x h b -exh e x h -EXI e x i -EXIF e x i f -EXIN e x i n -EXL e x l -EXLM e x l m -E. X. M. e x m -Exptl e x p t l -EXR e x r -exsul e x s u l -EXT e x t -EYA e y a -EYCN e y c n -EYD e y d -E&Y e and y -E. Y. e y -EY e y -Eyk e y k -eyu e y u -Ezaa e z a a -ez e z -Ez e z -EZ e z -Ezh e z h -EZH e z h -EZO e z o -EZR e z r -EZTV e z t v -Ezu e z u -EZW e z w -Ezy e z y -Faaa f a a a -faa f a a -Faa f a a -FAA f a a -faama f a a m a -FAANP f a a n p -FabH f a b h -FABM f a b m -FabR f a b r -F.A.B.'s f a b's -F.A.C.E. f a c e -FACR f a c r -F.A.C.S. f a c s -FADD f a d d -Fadl f a d l -FADS f a d s -FAEM f a e m -fa f a -Fa f a -F. A. f a -F.A. f a -FA f a -Faf f a f -FAFG f a f g -Fafhrd f a f h r d -FAFL f a f l -F. A. G. f a g -F.A.H. f a h -fai f a i -Fai f a i -FAI f a i -FAIM f a i m -FAIP f a i p -FAIPs f a i p's -Faiq f a i q -fajn f a j n -FAK f a k -Fal f a l -FAL f a l -F.A.M.E. f a m e -F.A.M. f a m -FAQ f a q -FARC's f a r c's -FARDC f a r d c -F.A.R. f a r -farw f a r w -FASA f a s a -FASB f a s b -fasc f a s c -Fasc f a s c -fas f a s -Fas f a's -FAs f a's -F.A.S. f a s -FAS f a s -FASG f a s g -FasL f a s l -FASRE f a s r e -FATF f a t f -FAUP f a u p -FAVC f a v c -FBA f b a -FBD f b d -FBFA f b f a -F. B. f b -F.B. f b -FB f b -F.B.G.s f b g's -F.B.I. f b i -FBI f b i -FBI's f b i's -FBK f b k -FBL f b l -FBMW f b m w -FBN f b n -FBO f b o -FBOs f b o's -FBPA f b p a -FBR f b r -FBS f b s -FBSN f b s n -FBSU f b s u -FBT f b t -FCA f c a -fcb f c b -FCB f c b -FCBF f c b f -F.C.B.I. f c b i -FCBL f c b l -FCBs f c b's -F.C.C. f c c -FCC f c c -FCD f c d -F&C f and c -fc f c -F. C. f c -F.C. f c -FCF f c f -FCGR f c g r -F. C. H. f c h -F.C.H.L. f c h l -FCIAC f c i a c -FCIC f c i c -FCI f c i -FCIP f c i p -FCK f c k -FCL f c l -FCNM f c n m -FCNZ f c n z -FCO f c o -FCP f c p -F.C.P.R.E.M.I.X. f c p r e m i x -FCPS f c p s -FCRLA f c r l a -F.C.'s f c's -FC's f c's -FCS f c s -FCT f c t -FCU f c u -FCUL f c u l -FCV f c v -F. C. W. f c w -FCW f c w -FCW's f c w's -FDA f d a -FDCC f d c c -FD&C f d and c -FDCPA f d c p a -FDCPAs f d c p a's -FDD f d d -FDDI f d d i -FDE f d e -F. D. f d -F.D. f d -FDFL f d f l -FDGB f d g b -FDG f d g -FDICs f d i c's -FdI f d i -FDI f d i -FDIO f d i o -FDJ f d j -FDL f d l -FDLT f d l t -FDM f d m -FDMNES f d m n e s -FDNY f d n y -FDP f d p -FDP's f d p's -FDR f d r -FDU f d u -F. E. f e -F.E. f e -FEFK f e f k -FEFSI f e f s i -FEFY f e f y -FEG f e g -FEGs f e g's -FEP f e p -F.E.R.S. f e r s -Fes f e's -FES f e s -FFA f f a -FFAK f f a k -ffc f f c -FFC f f c -FFC's f f c's -FFDO f f d o -ff f f -F. F. f f -F.F. f f -FF f f -FFF f f f -FFG f f g -FFH f f h -FFI f f i -FFK f f k -FFL f f l -FFL's f f l's -FFME f f m e -ffm f f m -FFOM f f o m -FFP f f p -FFPIR f f p i r -FFR f f r -FFSA f f s a -FF's f f's -FFs f f's -FFS f f s -FFU f f u -FFV f f v -FFWD f f w d -FGCU f g c u -FGFA f g f a -FGF f g f -F. G. f g -F.G. f g -FGFR f g f r -FGID f g i d -fgk f g k -FGL f g l -FGM f g m -FGR f g r -FGs f g's -FGS f g s -FGU f g u -FGV f g v -F. H. A. f h a -FHA f h a -FHAR f h a r -F. H. C. f h c -FHFA f h f a -F. H. f h -F.H. f h -FHI's f h i's -FHL f h l -fhm f h m -FHM f h m -FHM's f h m's -FHQ f h q -FHSAA f h s a a -FHS f h s -FHWA f h w a -F. H. W. f h w -FIAC f i a c -fiadh f i a d h -fia f i a -FIA f i a -FIAPO f i a p o -FIA's f i a's -FIAS f i a s -FIBCO f i b c o -F. I. B. f i b -FiBL f i b l -FIBT f i b t -FIC f i c -Fi'd f i d -FIDH f i d h -F.I f -F&I f and i -F. I. f i -F.I. f i -FIFTA f i f t a -FIGC f i g c -Figl f i g l -FIH f i h -FIH's f i h's -fija f i j a -FIJL f i j l -FIK f i k -F.I.M. f i m -FIM f i m -FIMI f i m i -Fio f i o -FiO f i o -FIO f i o -F.I.R. f i r -F.I.T.T. f i t t -FIU f i u -FIVB f i v b -F. J. A. f j a -FJE f j e -F. J. f j -F.J. f j -F.J.G. f j g -FJK f j k -F. J. M. f j m -FKA f k a -FKBP f k b p -F. K. f k -F.K. f k -FKK f k k -FKL's f k l's -FKS f k s -FKY f k y -Fla f l a -FLB f l b -FLCL f l c l -FLCS f l c s -FLD f l d -FLDS f l d s -FLEC f l e c -FLE f l e -Fles f l e's -FLEST f l e s t -FLETC f l e t c -fl f l -F. L. f l -F.L. f l -FL f l -FLH f l h -fli f l i -FLI f l i -FLI's f l i's -FLL f l l -F.L.M. f l m -FLM f l m -FLNB f l n b -FLN f l n -FLP f l p -FLPP f l p p -FLQ f l q -FLRA f l r a -FLR f l r -FLRL f l r l -FLS f l s -FLSW f l s w -FLT f l t -FLTK f l t k -FLTR f l t r -FLTS f l t s -FLV f l v -FLW f l w -flyr f l y r -FMA f m a -FMARS f m a r s -FMC f m c -FMCG f m c g -FMCSA f m c s a -FMCS f m c s -FMF f m f -fm f m -F. M. f m -F.M. f m -FM f m -FMH f m h -FMHS f m h s -FMI f m i -FMJD f m j d -FMLN f m l n -FMM f m m -FMN f m n -FMNJ f m n j -FMNP f m n p -FMPA's f m p a's -FMP f m p -FMQB f m q b -FMR f m r -fMRI f m r i -FMRP f m r p -FMRU f m r u -FM's f m's -FMs f m's -FMS f m s -FMSF's f m s f's -FMSO f m s o -FMV f m v -FMW f m w -FMX f m x -FMY f m y -FNAC f n a c -F. N. A. f n a -FNA f n a -FNB f n b -FNC f n c -F. N. D. f n d -FNE f n e -F. N. f n -F.N. f n -FNIC f n i c -FNI f n i -FNLA f n l a -FNLC f n l c -FNLC's f n l c's -FNL f n l -FNO f n o -FNP f n p -FNPI f n p i -FNSEA f n s e a -FNU f n u d s a -FOF f o f -F. O. f o -FPA f p a -FPB f p b -FPC f p c -FPCP f p c p -F.P.D. f p d -FPDs f p d's -fp f p -F. P. f p -F.P. f p -FP f p -FPGA f p g a -FPGAs f p g a's -FPI f p i -FPJ f p j -FPK f p k -F. P. L. f p l -FPL f p l -FPMR f p m r -FPM&SA f p m and s a -FPO f p o -FPP f p p -FPPS f p p s -F.P.R. f p r -FPR f p r -FPSC f p s c -fps f p s -FPS f p s -FPSO f p s o -FPSs f p s's -FPTP f p t p -FPU f p u -FQAD f q a d -F.Q. f q -FRA f r a -FRA's f r a's -FRAXA f r a x a -FRB f r b -FRBNF f r b n f -FRCC f r c c -F. R. C. f r c -FRC f r c -FRCNA f r c n a -FRCP f r c p -FRCS f r c s -FRDF f r d f -F.R.D. f r d -fr f r -F. R. f r -F.R. f r -FR f r -FRFS f r f s -FRFT f r f t -FRG f r g -F.R.G.S. f r g s -F.R.H.S. f r h s -F.R.I.B.A. f r i b a -FRMN f r m n -FRMR f r m r -FRMS f r m s -F. R. P. f r p -FRP f r p -FRR f r r -F.R.S.A. f r s a -FRSA f r s a -FRSC f r s c -FRSE f r s e -frs f r s -F. R. S. f r s -F.R.S. f r s -FRS f r s -FRSL f r s l -Fru f r u -FRU f r u -FRV f r v -FRWT f r w t -Frwydr f r w y d r -FSA f s a -FSB f s b -FSC f s c -FSCS f s c s -FSE f s e -fsf f s f -FSF f s f -FSFH f s f h -FSFLA f s f l a -fs f s -F. s f's -Fs f's -F. S. f s -F.S. f s -FS f s -FSFS f s f s -FSG f s g -FSGS f s g s -FSH f s h -FSIC f s i c -FSI f s i -FSIL f s i l -FSK f s k -FSKN f s k n -FSLF f s l f -FSL f s l -FSLN f s l n -FSM f s m -FSM's f s m's -fsn f s n -FSN f s n -FSO f s o -FSP f s p -FSPS f s p s -FSR f s r -FSRU f s r u -FSSB f s s b -FSS f s s -FSSM f s s m -FSSRU f s s r u -FSSs f s s's -FST f s t -FSU f s u -FSU's f s u's -FSV f s v -FTA f t a -FTAs f t a's -F.T.B. f t b -FTC f t c -FTD f t d -FTDI f t d i -FTE f t e -FTF f t f -Ft. Fort -F. T. f t -F.T. f t -FTI f t i -FTII f t i i -FTJ f t j -FTK f t k -FTL f t l -FTM f t m -FTN f t n -FTO f t o -ftp f t p -FTP f t p -FTPI f t p i -FTPL f t p l -FTRA f t r a -FTR f t r -FtsA f t s a -FT's f t's -FTS f t s -FTT f t t -FTTH f t t h -FTUC f t u c -Ftuh f t u h -FTU's f t u's -FTV f t v -F. V. B. f v b -F. V. f v -F.V. f v -FVGM f v g m -FVHS f v h s -FVJC f v j c -FVL f v l -FVP f v p -FVW f v w -FVWM f v w m -FWAA f w a a -FWA f w a -FWCD f w c d -FWD f w d -FWE f w e -F&W f and w -F. W. f w -F.W. f w -FWH f w h -FWI f w i -FWMW f w m w -FWO f w o -fwr f w r -F.W.R. f w r -F. W. S. f w s -F.W.S. f w s -FWS f w s -FWX f w x -FXCM's f x c m's -FXE f x e -FXFL f x f l -fx f x -FX f x -FXI f x i -F.X.R. f x r -FX's f x's -FXS f x s -FXX f x x -FXX's f x x's -FXXT f x x t -Fyb f y b -FYCO f y c o -F.Y. f y -F.Y.I. f y i -FYI f y i -Fyn f y n -FYN f y n -FYP f y p -fyr f y r -F.Y.R. f y r -FYR f y r -FYT f y t -FYU f y u -Fyw f y w -FZCI f z c i -FZD f z d -F.Z. f z -FZG f z g -FZJD f z j d -FZKA f z k a -Gaac g a a c -GAAF g a a f -G.A.A. g a a -G. A. g a -G.A. g a -G.A.R. g a r -GASL g a s l -GATA g a t a -GATV g a t v -GATX g a t x -G. A. W. g a w -GBA g b a -GBAG g b a g -GBASE g b a s e -GBA's g b a's -GBB g b b -GBBI g b b i -GBCA g b c a -GBC g b c -GBCM g b c m -GBD g b d -Gbe g b e -GbE g b e -GBE g b e -G. B. g b -G.B. g b -GBGC g b g c -GBGM g b g m -GBH g b h -GBIB g b i b -GBIF g b i f -GBI g b i -GBK g b k -GBL g b l -GBMA g b m a -G. B. M. g b m -GB&NDR g b and n d r -GBP g b p -GBR g b r -GB's g b's -GBS g b s -GBV g b v -GBX g b x -GBYLA g b y l a -GCAC g c a c -GCAF g c a f -GCA g c a -G. C. B. g c b -GCB g c b -GCC g c c -GCCN g c c n -GCC's g c c's -GCCS g c c s -GCD g c d -G.C.E. g c e -GCEP g c e p -GCF g c f -G&C g and c -G. C. g c -G.C. g c -GCHQ g c h q -GCI g c i -GCIP g c i p -GCIT's g c i t's -GCIV g c i v -GCJ g c j -GCL g c l -GCMC g c m c -G.C.M. g c m -GCM g c m -GCMG g c m g -GCN g c n -GCNT g c n t -GCOEC g c o e c -GCOS g c o s -GCPD g c p d -GCPH g c p h -GCR g c r -GCRIO g c r i o -GCSE g c s e -GCSEs g c s e's -GC&SF g c and s f -GCS g c s -GCSI g c s i -GCU g c u -GCV g c v -GCVO g c v o -GCVS g c v s -GDA g d a -Gdal g d a l -GDAL g d a l -GDAP g d a p -GDB g d b -GDC g d c -GDD g d d -GDDM g d d m -GDDR g d d r -Gde g d e -G. D. F. g d f -GDF g d f -gd g d -G. D. g d -G.D. g d -GD g d -GDI g d i -G.D.L. g d l -GDN g d n -G.D.O.D. g d o d -Gdow g d o w -G.D.P. g d p -GDP g d p -GDPs g d p's -gdr g d r -GDR g d r -GDR's g d r's -GDS g d s -GDST g d s t -GDT g d t -GDV g d v -Gebr g e b r -Gebt g e b t -GEC g e c -Ged g e d -GED g e d -GEF g e f -GEGB g e g b -ge g e -G. E. g e -G.E. g e -GE g e -GEHA g e h a -GEHX g e h x -Gek g e k -G.E.M. g e m -Gen. general -G.E.O. g e o -G. E. P. g e p -geq g e q -Geu g e u -GEU g e u -Gev g e v -GeV g e v -GEV g e v -Gfa g f a -GFA g f a -GF&A g f and a -GFC g f c -GFD g f d -GFDL g f d l -GFE g f e -GFF g f f -G. F. g f -G.F. g f -GFM g f m -GFP g f p -G. F. R. g f r -GFR g f r -GFS g f s -GFSN g f s n -GFSR g f s r -GFT g f t -GFWC g f w c -GFW g f w -GFY g f y -G. G. g g -G.G. g g -G. G. J. g g j -GGL g g l -GGM g g m -GGMRC g g m r c -GGS g g s -G.H.A. g h a -GHB g h b -GHC g h c -GHD g h d -Ghe g h e -G. H. g h -G.H. g h -GHGs g h g's -Ghir g h i r -GHK g h k -GHMC g h m c -G.H.N. g h n -G.H.Q. g h q -GHQ g h q -ghra g h r a -GHR g h r -GH&SA g h and s a -GHSA g h s a -GHSFHA g h s f h a -GHS g h s -GHSR g h s r -GHSU g h s u -GHU g h u -G.H.W. g h w -GICL g i c l -Giei g i e i -GIFV g i f v -G. I. g i -G.I. g i -G.J.B. g j b -G. J. g j -G.J. g j -G. K. g k -G.K. g k -Gla g l a -GLA g l a -GLB g l b -GLBT g l b t -GLBTQ g l b t q -GLC g l c -G&L g and l -G. L. g l -G.L. g l -gli g l i -Gli g l i -GLI g l i -Glis g l i's -GLK g l k -GLL g l l -GLLI g l l i -GLM g l m -glnA g l n a -G.L.O.R.Y. g l o r y -Glos g l o's -GLRS g l r s -GLS g l s -GLT g l t -GLVC g l v c -GLX g l x -GMAC g m a c -gma g m a -GMA g m a -GMAM g m a m -GMA's g m a's -GMAT g m a t -GMB g m b -GmbH g m b h -GMBH g m b h -GMB's g m b's -GMCC g m c c -GMC g m c -GMCL g m c l -GMC's g m c's -GMDSS g m d s s -GME g m e -Gmel g m e l -GMES g m e s -GMFF g m f f -GMF g m f -GMG g m g -gm g m -G. M. g m -G.M. g m -GM g m -GMG's g m g's -gmin g m i n -GMM g m m -GMO g m o -GMOs g m o's -GMPC g m p c -GMPE g m p e -GMP g m p -GMPTE g m p t e -GMR g m r -GMRL g m r l -GM's g m's -GMs g m's -GMS g m s -gmt g m t -GMT g m t -GMTV g m t v -Gmul g m u l -GNAA g n a a -GNA g n a -gnb g n b -GNB g n b -GNCC g n c c -G. N. C. g n c -GNC g n c -G. N. g n -G.N. g n -GNL g n l -GNP&BR g n p and b r -GNPDA g n p d a -gnp g n p -GNP g n p -GNPs g n p's -GNRD g n r d -GNR g n r -GNR's g n r's -GNS g n s -GNSS g n s s -GNT g n t -G.O. g o -G.O.O.D. g o o d -GOPB g o p b -G.O.P. g o p -GOP g o p -GOPIO g o p i o -GOP's g o p's -GOQ g o q -GPA g p a -GPB g p b -G.P.C. g p c -GPC g p c -GPCR g p c r -GPCRs g p c r's -GPD g p d -GPdI g p d i -GPDSC g p d s c -GPF g p f -GPFS g p f s -G&P g and p -GPG g p g -gp g p -G. P. g p -G.P. g p -GP g p -GPGPU g p g p u -GPI g p i -GpIIb g p i i b -GPIO g p i o -GPL g p l -GPMGs g p m g's -G.P.O. g p o -GPO g p o -GPP g p p -GPRA g p r a -GPRF g p r f -GPR g p r -GPRs g p r's -GPRS g p r s -GPSC g p s c -GPS g p s -GPT g p t -GPU g p u -GPUs g p u's -GPX g p x -G. Q. g q -GQ g q -GQNYGK g q n y g k -GQ's g q's -GQT's g q t's -G.R.A.B. g r a b -grac g r a c -gra g r a -Gra g r a -GRA g r a -Grbac g r b a c -GRB g r b -GRBs g r b's -grc g r c -GRC g r c -GRCs g r c's -GRDA g r d a -GRDC g r d c -G.R.D. g r d -GRD g r d -gre g r e -Gre g r e -GRE g r e -GRF g r f -GRG g r g -gr g r -G. R. g r -G.R. g r -GR g r -GRK g r k -G.R.L. g r l -GRL g r l -GRMG g r m g -GRM g r m -GRO g r o -GRP g r p -grrl g r r l -G. R. S. g r s -GRS g r s -GRT g r t -GRU g r u -GRX g r x -Gryf g r y f -gry g r y -Gry g r y -GRY g r y -grz g r z -GSA g s a -GSAPP g s a p p -GSAS g s a s -GSB g s b -GSC g s c -GSDF g s d f -GSD g s d -G.S.E. g s e -GSE g s e -GSFC g s f c -GSF g s f -G&S g and s -GSG g s g -Gs g's -G. S. g s -G.S. g s -GS g s -GSHD g s h d -GSH g s h -GSI g s i -GSIs g s i's -GSIS g s i s -GSK g s k -GSL g s l -GSLV g s l v -GSMA g s m a -GSM g s m -GSNAS g s n a s -G. S. N. g s n -GSN g s n -GSNM g s n m -GSN's g s n's -GSO g s o -GSOM's g s o m's -GSOp g s o p -GSPC g s p c -G. S. P. g s p -GSP g s p -GSR g s r -GSSE g s s e -GSS g s s -GSSP g s s p -GSTA g s t a -G.S.T. g s t -GST g s t -GSU g s u -GSUSA g s u s a -GSVD g s v d -GSVS g s v s -GSX g s x -GTAA g t a a -GTA g t a -GTAs g t a's -GTB g t b -GTBK g t b k -GTBP g t b p -GTC g t c -GTCR g t c r -GTD g t d -GTEC g t e c -GTE g t e -GTF g t f -GTFT g t f t -GTFU g t f u -gt g t -G. T. g t -G.T. g t -GT g t -G. T. H. g t h -GTi g t i -GTI g t i -GTK g t k -GTL g t l -GTMO g t m o -G. T. O. g t o -GTO g t o -GTPBP g t p b p -GTP g t p -GTPR g t p r -GTRA g t r a -GTR g t r -GTRI g t r i -GTs g t's -GTS g t s -GTSV g t s v -GTT g t t -GTTP g t t p -GTU g t u -GTV g t v -GTW g t w -GTW's g t w's -GTX g t x -G. U. g u -G.U. g u -GUI's g u i's -GUIs g u i's -G.U.Y. g u y -GVA g v a -GVAV g v a v -GVBA g v b a -GVC g v c -G. V. g v -G.V. g v -GVHSS g v h s s -GVJZ g v j z -GVK g v k -GvpA g v p a -GVP g v p -GVRD g v r d -GVSU g v s u -GVT g v t -GVU g v u -GVVV g v v v -GVW g v w -GVWR g v w r -GWA g w a -GWB g w b -G. W. C. g w c -GWe g w e -Gwet g w e t -G.W.F. g w f -GWF g w f -gw g w -G. W. g w -G.W. g w -GW g w -G. W. H. g w h -GWHS g w h s -Gwi g w i -GWK g w k -G.W.L. g w l -GWO g w o -GWOT g w o t -gwr g w r -GWR g w r -GWR's g w r's -GW's g w's -G. W. S. g w s -GWS g w s -GWU g w u -GXG g x g -GX g x -GXP g x p -GYAN's g y a n's -GYC g y c -GYF g y f -Gy g y -G. Y. g y -GY g y -Gyn g y n -GYN g y n -Gyps g y p's -gyu g y u -Gyu g y u -GZA g z a -GZ g z -GZR g z r -HAFV h a f v -H. A. h a -H.A. h a -H&A h and a -HBA h b a -HBA's h b a's -HBBO h b b o -HBC h b c -HBCo h b c o -HBC's h b c's -HBCU h b c u -HBES h b e s -HBF h b f -HBGAs h b g a's -H. B. h b -H.B. h b -HB h b -HBI h b i -HBK h b k -HBLX h b l x -HBMP h b m p -HBO h b o -HBO's h b o's -HBPU h b p u -H&BR h and b r -HBR h b r -HBRN h b r n -HBRY h b r y -HB's h b's -HBS h b s -HBSS h b s s -HBU's h b u's -HBV h b v -HBW h b w -HCA h c a -HCAW h c a w -HCBE h c b e -HCB h c b -HCBS h c b s -HCCA h c c a -HCCB h c c b -HCCC h c c c -HCC h c c -HCDCH h c d c h -HCE h c e -HCEs h c e's -HCF h c f -H. C. G. h c g -HCGVN h c g v n -H. C. h c -H.C. h c -HC h c -HCH h c h -HCHS h c h s -HCI h c i -HCJB h c j b -hcl h c l -HCL h c l -HClO h c l o -HCMC's h c m c's -HCME h c m e -HCM h c m -HCN h c n -HCO h c o -HCoV h c o v -HCPA h c p a -H. C. P. h c p -HCP h c p -HCPs h c p's -HCR h c r -HCS h c s -HCT h c t -HCTV's h c t v's -HCUP h c u p -HCV h c v -Hcy h c y -HDAC h d a c -Hdad h d a d -hDAF h d a f -HDA h d a -HDB h d b -HDCAM h d c a m -HDC h d c -HDCP h d c p -HDD h d d -HDDs h d d's -HDFC h d f c -H.D.F. h d f -HDFM h d f m -H.D.G. h d g -HDG h d g -hd h d -H. D. h d -H.D. h d -HD h d -hdh h d h -HDi h d i -HDI h d i -HDLC h d l c -HDL h d l -HDLS h d l s -HDM h d m -HDMI h d m i -HDPE h d p e -HDP h d p -HDR h d r -HDRO h d r o -HDRR h d r r -H. E. h e -H.E. h e -hezb h e z b -Hezb h e z b -HFA h f a -HFB h f b -HFC h f c -HFCS h f c s -HFDF h f d f -HFF h f f -HFF's h f f's -H&F h and f -H. F. h f -H.F. h f -HFI h f i -H. F. J. h f j -HFNB h f n b -HFPA h f p a -HFP h f p -HFPPV h f p p v -HFRS h f r s -HFS h f s -HFTP h f t p -H.G.B. h g b -hgcA h g c a -HGC h g c -HGF h g f -hg h g -H. G. h g -H.G. h g -HG h g -HGH h g h -HGNC h g n c -HgO h g o -H. G. O. h g o -HGPIN h g p i n -HGPs h g p's -HGTV h g t v -HGTV's h g t v's -HgU h g u -HGVs h g v's -HGY h g y -HHA h h a -H. H. B. h h b -HHC h h c -HHD h h d -HHDP h h d p -HHG h h g -H&H h and h -HHHDB h h h d b -H. H. h h -H.H. h h -HHPC h h p c -HHSAA h h s a a -HHS h h s -H.H.T. h h t -H. I. h i -H.I. h i -H.I.V.E. h i v e -HIV h i v -HIY h i y -Hizb h i z b -hja h j a -HJC h j c -H. J. h j -H.J. h j -HJ h j -HJK h j k -H.J.M. h j m -HJN h j n -H. J. R. h j r -H.J.R. h j r -H. J. W. h j w -HJW h j w -HKAA h k a a -Hkam h k a m -HKB h k b -HKCEC h k c e c -HKCEE h k c e e -HKDF h k d f -HKD h k d -HKDSE h k d s e -HKFC h k f c -H&K h and k -H. K. h k -H.K. h k -HK h k -H. K. J. h k j -HKK h k k -HKMA h k m a -HKN h k n -H.K.P. h k p -HKP h k p -HKSAR h k s a r -HK's h k's -HKT h k t -HKTWU h k t w u -HKU h k u -Hla h l a -HLA h l a -HLBN h l b n -HLE h l e -HLH h l h -H. L. h l -H.L. h l -HLL h l l -HLLS h l l s -HLN h l n -HLN's h l n's -HLS h l s -HLT h l t -HLTV h l t v -Hluk h l u k -hlutr h l u t r -HLV h l v -Hly h l y -HMAC h m a c -HMA h m a -Hman h m a n -H.M.A.S. h m a s -HMAS h m a s -HMB h m b -HMC h m c -HMCS h m c s -HMDA h m d a -HMD h m d -HMDI h m d i -HMDP h m d p -HME h m e -HMEL h m e l -HMF h m f -HMGA h m g a -HMG h m g -HMGIC h m g i c -HMGN h m g n -H&M h and m -HMH h m h -hm h m -H. M. h m -H.M. h m -HM h m -HMI h m i -HMIS h m i s -HMMC h m m c -HMM h m m -HMMWV h m m w v -HMN h m n -HMNZS h m n z s -HMNZT h m n z t -HMO h m o -HMOs h m o's -HMPC h m p c -HMP h m p -HMRC h m r c -hMRE h m r e -H&M's h and m's -H. M. S. h m s -H.M.S. h m s -HMS h m s -HMSN h m s n -H.M.S.O. h m s o -HMSO h m s o -H. N. h n -H.N. h n -HNI h n i -HNK h n k -HNL h n l -HNLMS h n l m s -HNMT h n m t -HNN h n n -HNO h n o -HNS h n s -H. O. h o -H.O. h o -HPA h p a -H.P.B. h p b -hpc h p c -HPC h p c -HPCT h p c t -H. P. D. h p d -HPD h p d -HPE h p e -HPH h p h -hp h p -H. P. h p -H.P. h p -HP h p -HPI h p i -HPLC h p l c -HPL h p l -HPP h p p -HPRA h p r a -HPR h p r -HPRT h p r t -HP's h p's -HPS h p s -H. P. T. h p t -HPV h p v -HQAA h q a a -HQDA h q d a -H.Q. h q -HQ h q -HRG h r g -H&R h and r -H.R.H. h r h -HRH h r h -H. R. h r -H.R. h r -HR h r -HRK h r k -HRL h r l -HRM h r m -hroa h r o a -HRo h r o -HRO h r o -HRP h r p -HRSA h r s a -HRSA's h r s a's -HRSG h r s g -hrs h r s -HRs h r's -HRST h r s t -HRT h r t -HRV h r v -HRW h r w -HRX h r x -HSA h s a -HSAN h s a n -HSBC h s b c -HSB h s b -HSCA h s c a -HSC h s c -hscy h s c y -HSD h s d -HSDPA h s d p a -Hsee h s e e -HSE h s e -HSE's h s e's -HSF h s f -HSG h s g -HSHB h s h b -H.S.H. h s h -hs h s -Hs h's -H. S. h s -H.S. h s -HS h s -HSHS h s h s -hsi h s i -HSJ h s j -HSK h s k -HSL h s l -hSlo h s l o -H.S.M. h s m -HSM h s m -HSMR h s m r -HSN h s n -HSNY's h s n y's -HSPA h s p a -HSPA's h s p a's -HSPG h s p g -HSPH h s p h -HSP h s p -HSP's h s p's -HSPs h s p's -HSRC h s r c -HSR h s r -HSSCCT h s s c c t -HSSF h s s f -HSS h s s -HST h s t -HST's h s t's -HSTs h s t's -HSTS h s t s -hsv h s v -HSV h s v -HSX h s x -HSY h s y -HSYK's h s y k's -HSZ h s z -HTA h t a -hTAS h t a s -HTAs h t a's -htc h t c -HTC h t c -HTC's h t c's -HTGL h t g l -HTHC h t h c -ht h t -H. T. h t -H.T. h t -HT h t -HTIB h t i b -hti h t i -Htin h t i n -HTK h t k -HTLA h t l a -HTLV h t l v -HTML h t m l -HTM's h t m's -Htoo h t o o -HTPB h t p b -HTPC h t p c -HTP h t p -HtrA h t r a -HTR h t r -HTRs h t r's -HTS h t s -http h t t p -HTTP h t t p -https h t t p s -HTTPS h t t p s -HTV h t v -HTV's h t v's -Htwa h t w a -HTW h t w -HTX h t x -H. U. h u -HVC h v c -HVCs h v c's -hvcv h v c v -HVDC h v d c -hvem h v e m -Hvem h v e m -HVF h v f -HVHS h v h s -H. V. h v -H.V. h v -H.W.A. h w a -H. W. F. h w f -H. W. h w -H.W. h w -H.W.L. h w l -HWL h w l -HWM h w m -HWRT h w r t -HWT h w t -HWV h w v -hwy h w y -Hwy h w y -hxt h x t -H. Y. h y -H.Y. h y -H. Z. h z -HZ&PC h z and p c -iaaf i a a f -IAAF i a a f -IAAF's i a a f's -IAA i a a -IAAPA i a a p a -IAAP i a a p -IaaS i a a s -IAAS i a a s -IABSE i a b s e -Iaca i a c a -IACC i a c c -IACD i a c d -IAC i a c -Iacob i a c o b -IACR i a c r -IAC's i a c's -IACTs i a c t's -Iacub i a c u b -IACUC i a c u c -IACW i a c w -IAD i a d -IAD's i a d's -IAEA i a e a -IAEG i a e g -I.A.E. i a e -IAE i a e -IAF i a f -IAF's i a f's -IAG i a g -IAG's i a g's -iagt i a g t -IAHP i a h p -IAHRC i a h r c -I. A. i a -I.A. i a -I.A.M. i a m -IAO i a o -Iapa i a p a -IAPHC i a p h c -IAP i a p -iar i a r -Iar i a r -IAR i a r -IAST i a s t -IATA i a t a -I.A.T. i a t -IAT i a t -IATR i a t r -IAUC i a u c -IAUCs i a u c's -IAU i a u -IAV i a v -IAWTV i a w t v -Iax i a x -IAX i a x -Ibac i b a c -Ibaes i b a e's -IBA's i b a's -IBB i b b -IBC i b c -IBCs i b c's -IBCT i b c t -IBDA'A i b d a a -Ibda i b d a -ibdal i b d a l -IBDB i b d b -IBDE i b d e -IBD i b d -IBE i b e -IBF i b f -IBGE i b g e -IBH i b h -I. B. i b -I.B. i b -IBJJF i b j j f -IBL i b l -IBLT i b l t -IBMA's i b m a's -IBMC i b m c -Ibm i b m -I.B.M. i b m -IBM i b m -IBM's i b m's -IBMXF i b m x f -Ibne i b n e -ibn i b n -IBO i b o -IBPA i b p a -IBP i b p -Ibra i b r a -IBRA i b r a -IBRC i b r c -IBR i b r -IBRO i b r o -Ibsa i b s a -IBSA i b s a -IBSF i b s f -Ibs i b's -IBS i b s -IBSL i b s l -IBTC i b t c -iBT i b t -IBTS i b t s -Ibu i b u -IBU i b u -IBV i b v -IBWFF i b w f f -iby i b y -ICBA i c b a -ICBC i c b c -ICB i c b -ICBL i c b l -ICBM i c b m -ICBMs i c b m's -ICBP i c b p -ICBS i c b s -ICBTT i c b t t -ICCC i c c c -ICCC's i c c c's -ICCEC i c c e c -ICCF i c c f -Icche i c c h e -I.C.C. i c c -ICC i c c -ICCID i c c i d -ICCI i c c i -ICCR i c c r -ICC's i c c's -ICCS i c c s -ICCW i c c w -ICDC i c d c -ICD i c d -ICDS i c d s -I.C.E. i c e -ICFF i c f f -icf i c f -ICF i c f -ICFR i c f r -ICG i c g -ICHC i c h c -I&C i and c -ic i c -Ic i c -I. C. i c -I.C. i c -IC i c -Ici i c i -ICI i c i -ICJ i c j -ICL i c l -ICL's i c l's -ICMA i c m a -ICME i c m e -I.C.M. i c m -ICM i c m -iCN i c n -ICN i c n -Ico i c o -ICO i c o -ICOP i c o p -ICPC i c p c -ICPD i c p d -ICP i c p -ICP's i c p's -ICQ i c q -ICRC i c r c -ICRF i c r f -ICR i c r -IcRn i c r n -ICRP i c r p -ICRS i c r s -ICRW i c r w -ICSA i c s a -ICSB i c s b -ICSC i c s c -ICSE i c s e -ICSG i c s g -ics i c s -ICs i c's -ICS i c s -ICSS i c s s -ICSSR i c s s r -ICTA i c t a -ICT i c t -ICTM i c t m -ICTP i c t p -ICTSF i c t s f -ICTs i c t's -ICTS i c t s -ICTU i c t u -ICTUR i c t u r -ICTVdB i c t v d b -ICTV's i c t v's -ICTY i c t y -ICU i c u -ICUN i c u n -ICU's i c u's -ICv i c v -ICV i c v -ICVS i c v s -ICWA i c w a -ICWC i c w c -ICW i c w -ICW's i c w's -iCyt i c y t -ICZ i c z -ICZN i c z n -IDAF i d a f -IDBF i d b f -IDB i d b -IDBI i d b i -IDC i d c -Iddaa i d d a a -Idd i d d -ID'd i d d -IDEF i d e f -IDE i d e -IDFA i d f a -IDFG i d f g -IDF i d f -IDFL i d f l -IDF's i d f's -IDG i d g -I. D. H. i d h -IDH i d h -id i d -I. D. i d -I.D. i d -ID i d -Idi i d i -Idir i d i r -IDIS i d i s -IDLHs i d l h's -IDL i d l -Idm i d m -IDM i d m -IDMR i d m r -IDMS i d m s -IDNA i d n a -IDN i d n -IDNR i d n r -IDNR's i d n r's -IDPs i d p's -IDRC i d r c -IDRO i d r o -IDSA i d s a -IDSA's i d s a's -IDSF i d s f -ids i d s -ID's i d's -IDs i d's -IDV i d v -IDW i d w -IDWO i d w o -IDW's i d w's -IDx i d x -IDX i d x -IEA i e a -I.E.M. i e m -IEM i e m -I.E.S. i e s -IES i e s -i.e. that is -Ifa i f a -IFA i f a -IFBB i f b b -IFBC i f b c -I.F.B. i f b -IFCC i f c c -ifc i f c -IFC i f c -IFC's i f c's -IfF i f f -I. F. F. i f f -IFF i f f -IFFI i f f i -IFFK i f f k -Ifft i f f t -IFHA i f h a -IFHP i f h p -ifi i f i -Ifi i f i -IFI i f i -IFIP i f i p -IFIs i f i's -IFK i f k -IFLA i f l a -IFL i f l -IfM i f m -IFM i f m -IFN i f n -Ifni i f n i -IFNs i f n's -IFO i f o -Ifop i f o p -IFP i f p -IFPI i f p i -IFPRI i f p r i -IFRC i f r c -IFR i f r -IFRS i f r s -IFRSs i f r s's -IFSB i f s b -IFSC i f s c -IFSTA i f s t a -IFTA i f t a -IFTF i f t f -IFT i f t -Iga i g a -IgA i g a -IGA i g a -IGA's i g a's -IGBT i g b t -IGC i g c -IGCP i g c p -IGCSE i g c s e -IGD i g d -IGF i g f -IGFI i g f i -IGFs i g f's -IGIB i g i b -'ig i g -Ig i g -I.G. i g -IG i g -igi i g i -IGI i g i -IgM i g m -IGM i g m -IGMP i g m p -ign i g n -Ign i g n -IGN i g n -IHF i h f -IHGS i h g s -IHH i h h -Ih i h -I. H. i h -IH i h -ihi i h i -IHI i h i -IHL i h l -IHL's i h l's -IHMC i h m c -IHME i h m e -ihm i h m -ihn i h n -Ihn i h n -IHOM i h o m -Ihor i h o r -IHPK i h p k -IHRC i h r c -IHSAA i h s a a -IHSA i h s a -IHS i h s -Iht i h t -IIA i i a -IIb i i b -IIB i i b -IIc i i c -IIDC i i d c -IID i i d -IIDI i i d i -IIDS i i d s -IIDX i i d x -IIED i i e d -IIFA i i f a -IIF i i f -IIfx i i f x -Iiga i i g a -IIga i i g a -IIG i i g -IIgs i i g's -IIGs i i g's -IIGS i i g s -IIGS's i i g s's -IIHF i i h f -IIHR i i h r -IIHS i i h s -IIIA i i i a -IIIb i i i b -IIIB i i i b -IIIBy i i i b y -IIIc i i i c -IIId i i i d -IIIF i i i f -I. I. i i -I.I. i i -ija i j a -Ija i j a -IJA i j a -IJF i j f -IJGL i j g l -IJHCD i j h c d -IJHL i j h l -ij i j -I. J. i j -IJ i j -IJN i j n -Ijok i j o k -IJSB i j s b -IJSE i j s e -IJSEM i j s e m -IJS i j s -Ijui i j u i -IJVM i j v m -IKBN i k b n -IKCO i k c o -IKF i k f -Ikh i k h -I.K. i k -IKK i k k -IKL i k l -IKr i k r -Iksa i k s a -IKTA i k t a -Iku i k u -Ilbe i l b e -ILB i l b -ILC i l c -ILCS i l c s -ILD i l d -ILDIS i l d i s -ILFC i l f c -ILF i l f -IL&FS i l and f s -ili i l i -Ili i l i -ILI i l i -I. L. i l -I.L. i l -Ilm i l m -ILM i l m -ILM's i l m's -ILN i l n -ILP i l p -ILR i l r -ILRS i l r s -ilu i l u -Ilu i l u -ILU i l u -IMbd i m b d -IMBD i m b d -IMB i m b -IMCB i m c b -IMCI i m c i -imc i m c -IMC i m c -imdb i m d b -IMDb. i m d b -IMDb i m d b -IMDB i m d b -IMD i m d -IMD's i m d's -ime i m e -IME i m e -IMF i m f -IMG i m g -I&M i and m -IMI i m i -I. M. i m -I.M. i m -IMIS i m i s -IMK i m k -Iml i m l -IMMEX i m m e x -Imm i m m -Imms' i m m's -imoa i m o a -IMO's i m o's -IMPA i m p a -IMPDH i m p d h -impr i m p r -Impr i m p r -IMPSA i m p s a -IMR i m r -IMSLP i m s l p -iMSNs i m s n's -Imst i m s t -IMTA i m t a -IMT i m t -IMTV i m t v -IMUK i m u k -IMU's i m u's -IMWF i m w f -INAC i n a c -Inba i n b a -INBA i n b a -INBF i n b f -inb i n b -incl i n c l -INDC i n d c -ind i n d -Ind i n d -IND i n d -INFJ i n f j -I.N.F.O. i n f o -Infs i n f's -ING i n g -INGR i n g r -inHg i n h g -INH i n h -INI i n i -I. N. i n -I.N. i n -InlB i n l b -INL i n l -INM i n m -INPD i n p d -INPE i n p e -InP i n p -INP i n p -INPP i n p p -Inre i n r e -I.N.R.I. i n r i -INR i n r -INSB i n s b -INSDC i n s d c -INSD i n s d -Inspx i n s p x -INSS i n s s -INTF i n t f -Ints i n t's -INTs i n t's -Intu i n t u -INVA i n v a -inv i n v -INXS i n x s -INXS's i n x s's -Ioba i o b a -IOCG i o c g -IOC i o c -IOCP i o c p -IODP i o d p -IODs i o d's -IOE i o e -IOFB i o f b -IOF i o f -io. i o -io i o -.I.o. i o -Io. i o -Io i o -.IO i o -I/O i o -IO i o -Ioka i o k a -Iok i o k -IOL i o l -IoM i o m -IOM i o m -IOPI i o p i -IOP i o p -IOPs i o p's -IORG i o r g -IOR i o r -IORM i o r m -IOSA i o s a -IOSB i o s b -iOS i o s -Io's i o's -I/Os i o's -I.O.'s i o's -IOS i o s -IOSR i o s r -Iosu i o s u -IoT i o t -IOT i o t -I.O.U. i o u -IOU i o u -IoW i o w -IOW i o w -IPA i p a -IPA's i p a's -IPB i p b -IPCC i p c c -IPCC's i p c c's -IPC i p c -IPCRI i p c r i -IPCs i p c's -IPDA i p d a -IPDI i p d i -IPD i p d -IPE i p e -IPFA i p f a -IPFI i p f i -IPF i p f -IPFP i p f p -IPFW i p f w -IPG i p g -IPH i p h -ip i p -Ip i p -I.P. i p -IP i p -IPL i p l -IPMG i p m g -IPM i p m -IPNI i p n i -IPN i p n -Ipo i p o -I.P.O. i p o -IPO i p o -IPP i p p -IPPL i p p l -IPPs i p p's -IPPT i p p t -Ippu i p p u -iPPV i p p v -IPR i p r -iPSC i p s c -IPSC i p s c -iPSCs i p s c's -ipse i p s e -IPSF i p s f -iPS i p s -IPs i p's -I.P.S. i p s -IPS i p s -IPTA i p t a -IPTC i p t c -IPTG i p t g -IPT i p t -IPTL i p t l -IPTV i p t v -IPU i p u -IPUM i p u m -IPv i p v -IPWA i p w a -IPW i p w -IPX i p x -iQ i q -I.Q. i q -IQ i q -IRAC i r a c -I.R.A. i r a -IRB i r b -IRBM i r b m -IRCA i r c a -IRC i r c -IRCM i r c m -IRCTC i r c t c -IRCT i r c t -IRD i r d -IRDS i r d s -IREC i r e c -I.R.E. i r e -IRE i r e -IRFCA i r f c a -IRFC i r f c -IRF i r f -IRFU i r f u -IRGC i r g c -Irgm i r g m -IRHG i r h g -IRHT i r h t -IRIB i r i b -Irig i r i g -IRI i r i -Iril i r i l -IRIN i r i n -i'r i r -ir i r -Ir i r -I. R. i r -I.R. i r -IR i r -IRIX i r i x -Irla i r l a -IRL i r l -IRM i r m -Irmis i r m i's -IRM's i r m's -IRNE i r n e -IRN i r n -iro i r o -Iro i r o -IRO i r o -IRP i r p -IRP's i r p's -IRPS i r p s -IRQ i r q -Irra i r r a -IRRI i r r i -IRR i r r -IRs i r's -I.R.S. i r s -IRS i r s -IRSN i r s n -I.R.T. i r t -IRT i r t -iru i r u -Iru i r u -ISA i s a -ISAv i s a v -Isba i s b a -ISBA i s b a -ISBER i s b e r -ISBHF i s b h f -ISB i s b -isbn i s b n -ISBN i s b n -ISBNs i s b n's -ISBO i s b o -ISCB i s c b -ISCE i s c e -isCf i s c f -ISCI i s c i -iSC i s c -Isc i s c -ISC i s c -ISCM i s c m -ISCM's i s c m's -ISCP i s c p -ISDA i s d a -Isd i s d -ISD i s d -isdn i s d n -ISDN i s d n -ISDS i s d s -ISDT i s d t -ISFI i s f i -ISF i s f -Isgec i s g e c -ISG i s g -ISHC i s h c -ishq i s h q -Ishq i s h q -ISHS i s h s -I. S. i s -I.S. i s -ISKB i s k b -ISK i s k -Isl i s l -ISL i s l -ISMF i s m f -ISMIR i s m i r -ISMNs i s m n's -ISN i s n -ISNTUC i s n t u c -ISOGG i s o g g -ISO i s o -ISO's i s o's -I.S.P.C.K. i s p c k -ISPF i s p f -ISPI i s p i -ISP i s p -ISPL i s p l -ISPR i s p r -ISPs i s p's -Isra'il i s r a i l -Isra i s r a -ISRA i s r a -ISR i s r -ISRN i s r n -ISRO i s r o -ISRP i s r p -ISSCC i s s c c -ISSF i s s f -iss i s s -Iss i s's -ISS i s s -ISSN i s s n -ISSNs i s s n's -ISSSSP i s s s s p -ISSUU i s s u u -ISTAF i s t a f -ISTD i s t d -Isu i s u -ISU i s u -ISU's i s u's -ISWC i s w c -ISWI i s w i -ISW i s w -I.T.A. i t a -ITA i t a -ITB i t b -ITC i t c -Ite i t e -ITE i t e -ITF i t f -ITG i t g -ITHF i t h f -I.T.H.M. i t h m -ITHs i t h's -iti i t i -Iti i t i -ITI i t i -I.T. i t -itk i t k -Itk i t k -ITK i t k -ITM i t m -ITMO i t m o -ITN i t n -ITNNs i t n n's -ITN's i t n's -Itoi i t o i -ITPF i t p f -iTP i t p -ITP i t p -ITRD i t r d -Itse i t s e -ITSF i t s f -ITTF i t t f -itt i t t -ITT i t t -ITUC i t u c -Itu i t u -ITU i t u -ITV i t v -ITV's i t v's -ITWF i t w f -ITW i t w -Itz i t z -IUB i u b -I.U.C.N.B. i u c n b -Iucn i u c n -IUCN i u c n -IUCN's i u c n's -IUD i u d -IUDs i u d's -IU i u -IUT i u t -IUU i u u -Iuz i u z -IVAA i v a a -IVAW i v a w -IVB i v b -I. V. i v -I.V. i v -IVL i v l -IVLP i v l p -IVM's i v m's -IVP i v p -IVPP i v p p -IVPTC i v p t c -IVRCL i v r c l -IVs i v's -Ivu i v u -IVWA i v w a -IWA i w a -Iwas i w a's -Iwaz i w a z -IWBs i w b's -IWC i w c -IWD i w d -IWF i w f -IWFL i w f l -IWF's i w f's -IWFS i w f s -IWG i w g -IWGP i w g p -iwi i w i -iw i w -I. W. i w -IW i w -IWL i w l -IWMF i w m f -IWM i w m -IWPR i w p r -iwrg i w r g -IWRG i w r g -IWR i w r -IWRS i w r s -IWSA i w s a -iwspy i w s p y -Iwuh i w u h -IWU's i w u's -I.W.W. i w w -IWW i w w -IXA i x a -IXb i x b -IXBs i x b's -IXC i x c -IXCs i x c's -IXe i x e -IX i x -iXL i x l -IXO i x o -ixtle i x t l e -IXV i x v -Iya i y a -IYA i y a -Iyar i y a r -IYC i y c -iyem i y e m -IYHF i y h f -Iyi i y i -iy i y -I. Y. i y -I.Y. i y -IY i y -J.A.A. j a a -J. A. B. j a b -Jaf j a f -J.A.G. j a g -JAG j a g -JAIR j a i r -J. A. j a -J.A.K.E. j a k e -J. A. N. j a n -J.A.P. j a p -J.A.S. j a s -JBA j b a -JBBA j b b a -JBB j b b -JBC j b c -JBG j b g -J&B j and b -jb j b -J. B. j b -J.B. j b -JB j b -J.B.L.D. j b l d -JBL j b l -J.B.M. j b m -JBM j b m -JB's j b's -J. B. S. j b s -J.B.S. j b s -JBT j b t -JBZ j b z -JCA j c a -JCB j c b -JCC j c c -JCF j c f -JCFS j c f s -J.C.G. j c g -JCG j c g -JCGM j c g m -JCIB j c i b -JCI j c i -jcis j c i s -jc j c -J. C. j c -J.C. j c -JC j c -JCJC j c j c -J. C. L. j c l -JCPA j c p a -J. C. P. j c p -JCP j c p -JCPV j c p v -JCRB j c r b -JCR j c r -JCRs j c r's -J.C.S. j c s -JCS j c s -JCSR j c s r -JCVI j c v i -J. C. W. j c w -JCW j c w -J. D. A. j d a -JDA j d a -JDBC j d b c -JDC j d c -JdeBP j d e b p -J.D.E. j d e -JDE j d e -JDENET j d e n e t -J.D.F. j d f -JDF j d f -jdi j d i -JDI j d i -jd j d -J. D. j d -J.D. j d -JD j d -jdk j d k -JDK j d k -J.D.M. j d m -JDM j d m -JDO j d o -JDRF j d r f -J&D's j and d's -J.D.s j d's -JDT j d t -JDU j d u -JDV j d v -J. E. A. j e a -J.E.A.L. j e a l -J.E.B. j e b -J. E. C. j e c -J. E. E. j e e -J. E. j e -J.E. j e -J.F.G. j f g -JFH j f h -jf j f -J. F. j f -J.F. j f -JF j f -JFK j f k -JFN j f n -JFQA j f q a -JFS j f s -J. G. A. j g a -J.G.B. j g b -JGI j g i -J. G. j g -J.G. j g -J. G. L. j g l -JGLS j g l s -J. G. M. j g m -J.G.M. j g m -JGO j g o -JGP j g p -JGR j g r -JGR's j g r's -JGSDF j g s d f -JGTC j g t c -JGU j g u -J.H.B. j h b -JHB j h b -J. H. C. j h c -JHC j h c -JHED j h e d -J.H.F. j h f -J.H.H. j h h -jh j h -J. H. j h -J.H. j h -JH j h -JHL j h l -J. H. M. j h m -J.H.R. j h r -JHs j h's -JHS j h s -JHSVs j h s v's -JHud j h u d -JHU j h u -J. I. j i -J.I. j i -J.J.A. j j a -JJB j j b -J. J. C. j j c -J.J.G. j j g -J&J j and j -J. J. j j -J.J. j j -JJ j j -JJK j j k -J. J. L. j j l -J.J.M. j j m -J.J.N. j j n -JJN j j n -Jka j k a -JKA j k a -JKC j k c -jkd j k d -JKH j k h -J&K j and k -J.K. J. j k j -J. K. j k -J.K. j k -JK j k -JKNPP j k n p p -J. K. S. j k s -JKT j k t -jkx j k x -JKX j k x -JLA j l a -J.L.B. j l b -JLB j l b -JLC j l c -J. L. E. j l e -J.L.E. j l e -JLG j l g -JLI j l i -J. L. j l -J.L. j l -J.L.L. j l l -JLM j l m -JLO's j l o's -JLPGA j l p g a -JLP j l p -JLS j l s -JLT j l t -JMA j m a -JMBG j m b g -J.M.C. j m c -JMC j m c -J. M. D. j m d -JMD j m d -J. M. E. j m e -JMI j m i -J. M. J. j m j -J.M.J. j m j -J. M. j m -J.M. j m -JMKR j m k r -J. M. L. j m l -JML j m l -JMM j m m -JMP j m p -JMS j m s -JMU j m u -JMV j m v -J. M. W. j m w -J.M.W. j m w -JMX j m x -JNA j n a -jnb j n b -JNF j n f -JNG j n g -JNI j n i -J. N. j n -J.N. j n -JN j n -JNK j n k -J. N. L. j n l -Jno j n o -JNPT j n p t -jnr j n r -JNR j n r -JNS's j n s's -JOJ j o j -J. O. j o -J.O. j o -J.P.D.B. j p d b -J. P. F. j p f -JPF j p f -jpg j p g -JPG j p g -JPH j p h -J&P j and p -jp j p -J.P. j p -JP j p -J.P.R. j p r -J.Q. j q -JQ j q -J.R.A. j r a -JRA j r a -J.R.C. j r c -JRC j r c -J.R.E. j r e -JRG j r g -JRHU j r h u -J. R. I. j r i -J&R j and r -J. R. j r -J.R. j r -JR j r -JRJ's j r j's -Jr. junior -jr. junior -Jr junior -jr junior -JRPG j r p g -J. R. P. j r p -J. R. R. j r r -J.R.R. j r r -J.R.'s j r's -JRSP j r s p -J. R. T. j r t -JRU j r u -JRX j r x -JSA j s a -JSAP j s a p -JSA's j s a's -jsb j s b -JSCA j s c a -JSC j s c -J.S.D. j s d -JSD j s d -JSE j s e -JSF j s f -JSH j s h -Js j's -J. S. j s -J.S. j s -JS j s -J.S.K. j s k -J.S.L. j s l -JSL j s l -J. S. M. j s m -JSM j s m -JSNN j s n n -JSOC j s o c -JSO j s o -JSP j s p -JSPS j s p s -JSR j s r -JSS j s s -JSW j s w -JSW's j s w's -JSX j s x -JTA j t a -J.T.B. j t b -JTB j t b -JTC j t c -JTF j t f -JTG j t g -JTI j t i -J&T j and t -J. T. j t -J.T. j t -JT j t -JT&KW j t and k w -JTL j t l -JTO j t o -JTR j t r -JTRO j t r o -JTS j t s -JTTF j t t f -JTT j t t -JTV j t v -J. U. j u -J.U.L.I.A. j u l i a -J.V.B. j v b -JVB j v b -JVC j v c -J.V.E. j v e -JVG j v g -JVH j v h -J&V j and v -J. V. j v -J.V. j v -JV j v -JVM j v m -JVNW j v n w -JVP j v p -JVRA j v r a -JVS j v s -Jwa j w a -J. W. A. j w a -J. W. B. j w b -JWB j w b -J.W.C. U.S. j w c u s -JWHA j w h a -J.W.J. j w j -J. W. j w -J.W. j w -JWM j w m -J. W. P. j w p -JWP j w p -JWRC j w r c -J. W. S. j w s -J.W.S. j w s -JWS j w s -JWST j w s t -JWT j w t -jx j x -JX j x -J.X.W.P. j x w p -JYJ j y j -JYJ's j y j's -J. Y. j y -J.Y. j y -JY j y -Jym j y m -Jymn j y m n -JYP j y p -J. Y. S. j y s -J. Z. j z -J.Z. j z -JZ j z -K.A.A. k a a -K. A. k a -K.A. k a -K.A.N. k a n -K. A. R. k a r -K.A.S. k a s -KBBI k b b i -KBB k b b -KBCI k b c i -KBC k b c -KBCO k b c o -KBD k b d -KBE k b e -KBFC k b f c -KBFL k b f l -KBFR k b f r -KBGD k b g d -KBG k b g -KBIG k b i g -kbi k b i -K&B k and b -K. B. k b -K.B. k b -KBKR k b k r -KBKS k b k s -KBKW k b k w -KBL k b l -KBMT k b m t -KBND k b n d -KBNZ k b n z -KBO k b o -KBRC k b r c -KBR k b r -KBS k b s -KBSN k b s n -KCB k c b -KCBS k c b s -KCCC k c c c -KCCI k c c i -KCC k c c -KCCO k c c o -KCDC k c d c -K. C. D. k c d -KCD k c d -KCED k c e d -KCET k c e t -KCG k c g -KCHS k c h s -K.C.I.E. k c i e -K. C. k c -K.C. k c -KCK k c k -KCKM k c k m -KCLU k c l u -K.C.M.G. k c m g -KCMG k c m g -KCNA k c n a -KCNH k c n h -KCNJ k c n j -KCN k c n -KCNN k c n n -KCNQ k c n q -KCNV k c n v -KCPD k c p d -KCPI k c p i -KCPM k c p m -KCPQ k c p q -KCPQ's k c p q's -KCQL k c q l -KCRA k c r a -KCRC k c r c -KCRH k c r h -KCR k c r -KCRW k c r w -KCSD k c s d -KCSG k c s g -K.C.S.I. k c s i -KCSI k c s i -K.C.'s k c's -KCS k c s -KCSP k c s p -KCTD k c t d -KCT k c t -KCTL k c t l -KCTV k c t v -KCUB k c u b -KCU k c u -KCVO k c v o -KCYX k c y x -kDa k d a -KDA k d a -kdal k d a l -KDAL k d a l -KDB k d b -KDCE k d c e -KDCO k d c o -KDDG k d d g -KDDI k d d i -KDD k d d -KDE k d e -KDFW k d f w -KDFX k d f x -K. D. G. k d g -KDGS k d g s -KDHS k d h s -KDKA k d k a -K&D k and d -kd k d -K. D. k d -K.D. k d -KD k d -KDP k d p -KDPM k d p m -KDR k d r -K. D.'s k d's -K.D.'s k d's -KDS k d s -ke k e -Ke k e -K. E. k e -K.E. k e -KE k e -KFAC k f a c -KFAED k f a e d -KFAI k f a i -KFA k f a -KFBK k f b k -KFBT k f b t -KFCC k f c c -KFCD k f c d -kfc k f c -K.F.C. k f c -KFC k f c -KFC's k f c's -KFD k f d -KFDM k f d m -KFEM k f e m -KFFL k f f l -KFH k f h -KFI k f i -KFJB k f j b -K.F. k f -KFLX k f l x -KFMB's k f m b's -KFMJ k f m j -K. F. M. k f m -K.F.M. k f m -KFOR k f o r -KFQC k f q c -KFQ k f q -KFRC k f r c -KFRC's k f r c's -KFRD k f r d -KFRG k f r g -KFRH k f r h -KFRO k f r o -KFSD k f s d -KFSN k f s n -KFST k f s t -KFT k f t -KFTL k f t l -KFUM's k f u m's -KFVD k f v d -KFV k f v -KFWB k f w b -KFXO k f x o -KFYO's k f y o's -KGA k g a -K.G.B. k g b -KGB k g b -KGC k g c -KGPH k g p h -KGP k g p -KGRI k g r i -kgr k g r -KGRK k g r k -KGS k g s -kgt k g t -KGT k g t -KGTV k g t v -KgU k g u -KGU k g u -KGUN k g u n -kgv k g v -K.G.V. k g v -KGWB k g w b -KGW k g w -KHHZ k h h z -KHI k h i -Khizr k h i z r -KHJ k h j -K. H. k h -K.H. k h -KH k h -KHK k h k -KHKV k h k v -Khlav k h l a v -KHL k h l -KHL's k h l's -Khmu k h m u -KHMY k h m y -KHOJ k h o j -KHOW k h o w -KHQA k h q a -KHQ k h q -Khri k h r i -Khru k h r u -KHRW k h r w -KHSAA k h s a a -KHS k h s -KHTL k h t l -KHTZ k h t z -KHUI k h u i -KHUT k h u t -KHWI k h w i -K.I.D. k i d -K. I. k i -K.I. k i -K.I.M. k i m -KIP k i p -K.I.S.S. k i s s -KJCE k j c e -KJCT k j c t -KJEF k j e f -Kjer k j e r -KJHK k j h k -KJI k j i -KJIN k j i n -kj k j -K. J. k j -K.J. k j -KJ k j -KJKP k j k p -KJMM k j m m -KJNE k j n e -KJNW k j n w -Kjop k j o p -KJo's k j o's -KJQY k j q y -KJV k j v -KJZI k j z i -KKBL k k b l -KKBQ k k b q -KKCR k k c r -KKDA k k d a -K.K.E. k k e -KKE k k e -K. K. k k -K.K. k k -KKMC k k m c -KKMI k k m i -KKM k k m -KKMX k k m x -KKRG k k r g -KKR k k r -KKSF k k s f -KK's k k's -KKSY k k s y -KKT k k t -KLA k l a -KLBK k l b k -KLBM k l b m -KLCI k l c i -KLC k l c -KLDS k l d s -Kle k l e -KLE k l e -KLF k l f -KLGH k l g h -KLH k l h -KLK k l k -kl k l -K. L. k l -K.L. k l -KL k l -KLKS k l k s -KLLP k l l p -KLLV k l l v -KLM k l m -KLN's k l n's -KLQQ k l q q -KLQT k l q t -KLRY k l r y -KLSC k l s c -KLSD k l s d -KLS k l s -KLSX k l s x -KLT k l t -KLX k l x -KMB k m b -KMC k m c -KMD k m d -KMGO k m g o -KMGT k m g t -KMIH k m i h -KMIR k m i r -KMITL k m i t l -KMJ k m j -K&M k and m -KMK k m k -km k m -K. M. k m -K.M. k m -KM k m -KMLA k m l a -KML k m l -KMME k m m e -KMML k m m l -KMRL k m r l -KMS k m s -KMSP k m s p -KMT k m t -KMTP k m t p -KMTV k m t v -KMVA k m v a -KMXW k m x w -KMZ k m z -KNFM k n f m -KNHP k n h p -K&N k and n -kn k n -K. N. k n -K.N. k n -KN k n -KNKT k n k t -KNLA k n l a -KNMI k n m i -KNNV k n n v -KNPN k n p n -KNPR k n p r -KNPU k n p u -KNRE k n r e -KNRS k n r s -KNS k n s -KNSM k n s m -KNST k n s t -KNTH k n t h -K.N.T. k n t -KNTN k n t n -KNTU k n t u -KNVA's k n v a's -KNVB k n v b -KNVN k n v n -KNX k n x -KNXT k n x t -K.O.D. k o d -k'o k o -K. O. k o -K.O. k o -KOTC k o t c -KOTD k o t d -K.O.T. k o t -KOWL k o w l -K. P. A. C. k p a c -KPAC k p a c -KPA k p a -KPAN k p a n -KPCC k p c c -KPC k p c -KPD k p d -KPE k p e -KPIF k p i f -KPI k p i -KPIs k p i's -K&P k and p -K. P. k p -K.P. k p -KP k p -KPLC k p l c -KPL k p l -KPLZ k p l z -KPMG k p m g -KPM k p m -KPNA k p n a -KPNB k p n b -KPO k p o -Kppen k p p e n -KPPV k p p v -KPS k p s -kptm k p t m -KPTV k p t v -KPU k p u -KPVD k p v d -KPVX k p v x -KPWR k p w r -KPXE k p x e -KQA k q a -KQED k q e d -KQHN k q h n -KQKK k q k k -KQKS k q k s -KQLT k q l t -KQLZ k q l z -KQMO k q m o -KQV k q v -KQXR k q x r -KRBM k r b m -krc k r c -KRCR k r c r -KRCs k r c's -KRF k r f -KRH k r h -K.R.I.T. k r i t -K.R.I.T.'s k r i t's -Krka k r k a -KRKC k r k c -KRK k r k -kr k r -K. R. k r -K.R. k r -KR k r -KRMT k r m t -Krne k r n e -KRNS k r n s -KRNV k r n v -KROQ k r o q -kroz k r o z -Kroz k r o z -KRPS k r p s -KRRT k r r t -K. R. S. k r s -KRS k r s -KRT k r t -Kru k r u -KRU k r u -KRWC k r w c -KRX k r x -K.R.Y. k r y -KRZA k r z a -ksa k s a -KSA k s a -K. S. B. k s b -KSBW k s b w -KSBY k s b y -KSCB k s c b -KSC k s c -KSCO k s c o -KSC's k s c's -KSCS k s c s -KSDB k s d b -KSDO k s d o -KSEB k s e b -KSEG k s e g -KSE k s e -KSEQ k s e q -kset k s e t -KSET k s e t -Kseur k s e u r -Ksevt k s e v t -KSFO k s f o -KSG k s g -KSHB k s h b -KSI's k s i's -KSJR k s j r -KSKJ k s k j -KSKK k s k k -K.S.K. k s k -KSK k s k -ks k s -K. S. k s -K.S. k s -KS k s -KSLC k s l c -KSL k s l -KSLM k s l m -KSLU k s l u -KSMB k s m b -KSNR k s n r -KSNV k s n v -KSP k s p -KSPS k s p s -KSTE k s t e -KSTH k s t h -KST k s t -KSTP k s t p -KSU k s u -K.S.V. k s v -KSWD k s w d -KSWT k s w t -KSWW k s w w -KSYL k s y l -KSZR k s z r -Ktav k t a v -KTAV k t a v -KTBS k t b s -KTCC k t c c -KTC k t c -KTCZ k t c z -KTDA k t d a -KTDD k t d d -KTE k t e -KTH k t h -KT&K k t and k -kt k t -K. T. k t -K.T. k t -KT k t -KTKZ k t k z -KTLA k t l a -K. T. M. k t m -KTM k t m -KTN k t n -KTOK k t o k -KTO k t o -KTRE k t r e -KTRH k t r h -KTRK k t r k -KTR k t r -KT's k t's -KTSM k t s m -KTT k t t -KTTN k t t n -KTTV k t t v -KTU k t u -KTUM k t u m -KTVA k t v a -KTVK k t v k -KTVS k t v s -KTVT k t v t -KTVU k t v u -KTVZ k t v z -KTWD's k t w d's -KTWO k t w o -KTX k t x -KTXL k t x l -Kuaa k u a a -Kud k u d -KUFM k u f m -KUGB k u g b -kuih k u i h -K. U. k u -K.U. k u -Kutb k u t b -KUTV k u t v -KUUU k u u u -KUWL k u w l -KUYI k u y i -KVAB k v a b -KVBC k v b c -KVB k v b -K.V.G.K. k v g k -KVHV k v h v -Kvik k v i k -KVI k v i -Kvit k v i t -KVIT k v i t -KVK k v k -K. V. k v -K.V. k v -KVLO k v l o -KVLY k v l y -KVM k v m -KVMRT k v m r t -KVMX k v m x -KVNG k v n g -KVN k v n -KVP k v p -KVTV k v t v -KVVS's k v v s's -KVVV k v v v -Kvyat k v y a t -KVZ k v z -kwa k w a -KWBM k w b m -KWC k w c -KWD k w d -KWEI k w e i -kwe k w e -KWG k w g -KWJJ k w j j -KWKH k w k h -KWKW k w k w -KWMR k w m r -KWMT k w m t -KWNA k w n a -KWNK k w n k -KWOA k w o a -KWP k w p -KWPT k w p t -KWQC k w q c -KWRE k w r e -KWRU k w r u -KWSB k w s b -KWSX k w s x -KWU k w u -KWWL's k w w l's -KXAN k x a n -KXAS k x a s -KXI k x i -KXJB k x j b -KXKS k x k s -KX k x -KXLX k x l x -KXLY k x l y -KXMC k x m c -KXNA k x n a -KXOL's k x o l's -KXTA k x t a -KXTN k x t n -KXTX k x t x -KYAY k y a y -KYBE k y b e -KYEZ k y e z -KYNM k y n m -Kyse k y s e -KYSL k y s l -KYTC k y t c -kyt k y t -Kyt k y t -KYW k y w -kz k z -K. Z. k z -KZ k z -KZLZ k z l z -KZMP k z m p -KZMU k z m u -KZQX k z q x -KZZQ k z z q -L. A. G. l a g -L. A. l a -L.A.'s l a's -Lay's l a y's -L.B.A. l b a -LBA l b a -LBCC l b c c -LBC l b c -LBC's l b c's -LBE l b e -LBF l b f -LBi l b i -LBJ l b j -LBJ's l b j's -L. B. l b -L.B. l b -LB l b -LBL l b l -LBM l b m -LBN l b n -LBNL l b n l -LBP l b p -LBPs l b p's -LBR l b r -LBS l b s -LBV l b v -LBW l b w -LCAC l c a c -LCA l c a -LCAs l c a's -LCAS l c a s -L.C.B. l c b -LCBL l c b l -LCBM l c b m -lcc l c c -LCC l c c -LCCN l c c n -LCCs l c c's -LCCS l c c s -LCDB l c d b -LCD l c d -LCDP l c d p -LC&DR l c and d r -LCDR l c d r -LCDs l c d's -L.C.E. l c e -LCFC l c f c -LCF l c f -LCH l c h -LCI l c i -LCIs l c i's -L. C. l c -L.C. l c -LC l c -LCL l c l -LCMC l c m c -LCM l c m -LCMR l c m r -LCMV l c m v -lcn l c n -LCO l c o -LCP l c p -LCQ l c q -LCR l c r -LCSB l c s b -LCSC l c s c -LC's l c's -LCS l c s -LCSs l c s's -LCST l c s t -LCT l c t -LCTs l c t's -LCU l c u -LCV l c v -LCVP l c v p -LCVPs l c v p's -LCZ l c z -Lda l d a -LDA l d a -LDAP l d a p -LDB l d b -LDBV l d b v -L.D.C. l d c -LDC l d c -LDCM l d c m -LDCs l d c's -LDD l d d -LDDP l d d p -LDF l d f -LDH l d h -L.D.I. l d i -LDK l d k -L. D. l d -L.D. l d -LDL l d l -LDLR l d l r -LDM l d m -LDMR l d m r -LDN l d n -LDP l d p -LDS l d s -LDU l d u -LDV l d v -LEB l e b -L. E. l e -L.E. l e -LFA l f a -L.F.C. l f c -LFC l f c -L.F.D. l f d -LFE l f e -LFFCs l f f c's -LFF l f f -LFG l f g -LFHC l f h c -LFH l f h -L. F. l f -L.F. l f -LFL l f l -LFM l f m -LFOC l f o c -LFO l f o -LFOM l f o m -LFPB l f p b -LFP l f p -LFQD l f q d -LFRJ l f r j -LFR l f r -LFs l f's -LFS l f s -LFSRs l f s r's -LFTR l f t r -LFXA l f x a -LGA l g a -LGB l g b -LGBTI l g b t i -LGBT l g b t -LGBTQ l g b t q -LGC l g c -LGD l g d -LGI l g i -L. G. l g -L.G. l g -LG l g -LGMB l g m b -LGMs l g m's -LGN l g n -LGPL l g p l -LGPZ l g p z -LG&RDD l g and r d d -LGR l g r -LGs l g's -LGS l g s -LGTB l g t b -LGT's l g t's -LGU l g u -LGUs l g u's -LGV l g v -LGVs l g v's -Lha l h a -LHB l h b -L. H. C. l h c -LHC l h c -LHD l h d -LHFP l h f p -LHICE l h i c e -LHI l h i -L. H. l h -L.H. l h -LHMC l h m c -L.H.M. l h m -LHO l h o -L. H. P. l h p -LHP l h p -LHS l h s -LHW l h w -LHX l h x -LHY l h y -L. I. l i -L.I. l i -LJBL l j b l -Lje l j e -LJJ l j j -L. J. K. l j k -L. J. l j -L.J. l j -L.J.V. l j v -LKB l k b -LKG l k g -LKK l k k -L. K. l k -LK l k -LKL l k l -LKML l k m l -LKPR l k p r -LKRN l k r n -lks l k s -LKS l k s -LKTI l k t i -Llapi l l a p i -LLAW l l a w -L.L.B. l l b -LLB l l b -LLCC l l c c -L.L.C. l l c -LLC l l c -LLD l l d -LL.D l l d -lle l l e -LLE l l e -LLHS l l h s -lli l l i -LLI l l i -L&L l and l -LLLE l l l e -L. L. l l -L.L. l l -LL l l -LLL l l l -LLLP l l l p -LLM l l m -LLMNR l l m n r -LLPFX l l p f x -llp l l p -LLP l l p -LLPX l l p x -LLRW l l r w -LLT l l t -LLTV l l t v -llu l l u -LLVM l l v m -LLWS l l w s -LMA l m a -LMC l m c -LMCT l m c t -LMDB l m d b -LMDC l m d c -LMD l m d -LME l m e -LMFAO's l m f a o's -LMFF l m f f -LMG l m g -LMGs l m g's -LMGTE l m g t e -LMH l m h -LMHS l m h s -lm l m -L. M. l m -L.M. l m -LM l m -LMM l m m -LMP l m p -LMPs l m p's -LMQ l m q -LMQs l m q's -L.M.S. l m s -LMS l m s -LMT l m t -LMTP l m t p -LMU l m u -LMWH l m w h -LMX l m x -LnAIB l n a i b -LNA l n a -LNAV l n a v -LNBF l n b f -LNB l n b -L.N.C. l n c -LNCS l n c s -L.N.E.R. l n e r -LNER l n e r -LNFS l n f s -LNG l n g -LNH l n h -L&N l and n -L. N. l n -L.N. l n -L.O.C.'s l o c's -Lokk l o k k -l'OL l o l -L.O.L. l o l -L. O. l o -L.O. l o -L.O.V.E. l o v e -L.O.V. l o v -LOXL l o x l -LPA l p a -L.P.A.M. l p a m -LPAM l p a m -LPARs l p a r's -LPAVS l p a v s -LPBG l p b g -L.P.B. l p b -LPB l p b -LPC l p c -LPCM l p c m -LPDA l p d a -LPDR l p d r -LPE l p e -LPF l p f -LPFM l p f m -LPGA l p g a -LPG l p g -L.P.H. l p h -LPH l p h -LPI l p i -LPLA l p l a -L&P l and p -LPL l p l -lp l p -L. P. l p -L.P. l p -LP l p -L&PM l and p m -LPMN l p m n -LPMud l p m u d -LPMUD l p m u d -LPN l p n -LPO l p o -LPRP l p r p -LPSC l p s c -LP's l p's -LPs l p's -LPS l p s -LPSN l p s n -LPTB l p t b -LPThe l p t h e -LPTV l p t v -LQ l q -LRAD l r a d -LRA l r a -LRCD l r c d -LRC l r c -L.R.C.P. l r c p -LRCP l r c p -LRDG l r d g -LRG l r g -lr l r -L. R. l r -L.R. l r -LR l r -LRO l r o -L.R.P.C. l r p c -LRP l r p -LRPPRC l r p p r c -LRRI l r r i -LRRP l r r p -LRS l r s -LRTA l r t a -LRT l r t -LRTR l r t r -LRTs l r t's -LRVs l r v's -LRY l r y -LSAC l s a c -LSA l s a -LSBC l s b c -LSB l s b -LSC l s c -LSCS l s c s -LSDHH l s d h h -L.S.D. l s d -LSD l s d -LSDP l s d p -LSDs l s d's -LSE l s e -LSF l s f -LSG l s g -LSH l s h -LSi l s i -LSI l s i -LSJ l s j -LSK l s k -L.S.L. l s l -ls l s -Ls l's -L. S. l s -L.S. l s -LSM l s m -LSO l s o -LSP l s p -LSPN l s p n -LSQC l s q c -LSRI l s r i -L. S. R. l s r -LSS l s s -LSSP l s s p -LSSR l s s r -LST l s t -LSTM l s t m -LSTs l s t's -LSUA l s u a -LSU l s u -LSU's l s u's -LSVCCs l s v c c's -LSV l s v -LSWR l s w r -LTAF l t a f -L. T. B. l t b -LTB l t b -LTCF l t c f -LTCI l t c i -L. T. C. l t c -L.T.C. l t c -LTC l t c -Ltda l t d a -LTDA l t d a -Ltd. limited -LTE l t e -LTFA l t f a -LTHS l t h s -LTI l t i -LTK l t k -L&T l and t -Lt. lieutenant -LTL l t l -lt l t -L. T. l t -L.T. l t -L.T.M. l t m -LTM l t m -LTMPS l t m p s -LTN l t n -ltoh l t o h -LTP l t p -LTRPC l t r p c -LTTE l t t e -LTTR l t t r -LTU l t u -LTV l t v -L. U. l u -lv l v -L. V. l v -L.V. l v -LV l v -LVMH l v m h -LWDB l w d b -LWD l w d -LWDS l w d s -LWE l w e -LWF l w f -LWH l w h -L. W. l w -L.W. l w -L.Y. l y -L&YR l and y r -M.A.D.E. m a d e -M.A.D. m a d -M.A.J. m a j -M.A.K. m a k -M. A. m a -M.A. m a -M&A m and a -M. A. O. m a o -M.A.O. m a o -M.A.R. m a r -M.A.S.K. m a s k -M.A.S. m a s -M.A.S.S. m a s s -M.B.A. m b a -M.B.B.S. m b b s -MBBS m b b s -MBC m b c -MBC's m b c's -MBDA m b d a -MBD m b d -M.B. D.P.M. m b d p m -M.B.E. m b e -MBE m b e -Mbewu m b e w u -MBF m b f -MBFW m b f w -MBGN m b g n -MBH m b h -mbi m b i -MBI m b i -M. B. J. m b j -MBJ m b j -MBK m b k -MBL m b l -M. B. m b -M.B. m b -MBM m b m -MBNA m b n a -mBo m b o -MBO m b o -MBPJ m b p j -MBP m b p -Mbre m b r e -mbr m b r -MBR m b r -MBSE m b s e -MBSI m b s i -MBS m b s -MBTI m b t i -MBT m b t -MBU's m b u's -MBX m b x -MCAF m c a f -MCAL m c a l -M.C.A. m c a -MCA m c a -MCA's m c a's -MCAS m c a s -MCB m c b -M. C. C. m c c -M.C.C. m c c -MCC m c c -MCC's m c c's -MCCs m c c's -MCCU m c c u -MCDA m c d a -MCDC m c d c -MCD m c d -MCDM m c d m -MCE m c e -MCFA m c f a -M. C. F. m c f -MCF m c f -MCFM m c f m -MCGJCW m c g j c w -MCG m c g -MCI m c i -MCJ m c j -MCLA m c l a -MCL m c l -M. C. m c -M.C. m c -MCMC m c m c -M. C. M. m c m -MCM m c m -MCN m c n -MCO m c o -MCOT's m c o t's -MCPC m c p c -MCPI m c p i -MCP m c p -MCPON m c p o n -MCPP m c p p -MCQ m c q -MCRD m c r d -MCR m c r -MCRP m c r p -MCSBA m c s b a -MCs m c's -M.C. S. m c s -MCS m c s -MCST m c s t -MCTFS m c t f s -MCT m c t -MCTU m c t u -MCTV m c t v -MCU m c u -MCV m c v -MCVTS m c v t s -MCW m c w -mcyG m c y g -MCYO m c y o -MCZ m c z -MDAA m d a a -MDAH m d a h -MDA m d a -mdb m d b -MDB m d b -MDC m d c -MDC's m d c's -MDCs m d c's -MDCT m d c t -mdDA m d d a -MD&DI m d and d i -Mde m d e -MDE m d e -MDF m d f -MDG m d g -MDGs m d g's -MDH m d h -MDHUs m d h u's -mDia m d i a -MDIB m d i b -MDic m d i c -MDI m d i -MDJT's m d j t's -MDK m d k -mdla m d l a -MDL m d l -MDMA m d m a -md m d -M. D. m d -M.D. m d -MD m d -MDM m d m -MDNA m d n a -M.D.N. m d n -MDPD's m d p d's -MDP m d p -MDPPP m d p p p -MDPS m d p s -MDPV m d p v -MDQ m d q -MDRC m d r c -mdr m d r -MDR m d r -MDT m d t -MDV m d v -MDX m d x -M. E. m e -M.E. m e -M.E.N. m e n -M. E. P. m e p -M.F.A. m f a -MFA m f a -MFB m f b -MFCC m f c c -MFC m f c -MFCS m f c s -MFD m f d -MFDs m f d's -MFe m f e -MFF m f f -MFG m f g -MFI m f i -MFJ m f j -MFK m f k -M. F. m f -M.F. m f -MFMF m f m f -MFM m f m -MFN m f n -MFNW m f n w -MFP m f p -MFR m f r -MFS m f s -MFS's m f s's -MFTBC m f t b c -MFT m f t -MFTs m f t's -Mgadla m g a d l a -Mgal m g a l -mga m g a -Mga m g a -MGA m g a -MGB m g b -Mgbo m g b o -MGCCC m g c c c -MGC m g c -MGE m g e -MGen m g e n -MGG m g g -MGH m g h -MGIMO m g i m o -MGIT m g i t -MGJH m g j h -MGK m g k -M.G.L. m g l -M&G m and g -M. G. m g -M.G. m g -M. G. M. m g m -MGM m g m -MGM's m g m's -MGMT m g m t -M&GN m and g n -MGN m g n -MGO m g o -MGP m g p -M&GR's m and g r's -Mha m h a -M.H.A. m h a -MHA m h a -MHB's m h b's -MHC m h c -MHCs m h c's -MHD m h d -MHI m h i -MHK m h k -MH&L m h and l -MHL m h l -M&H m and h -M. H. m h -M.H. m h -Mhor m h o r -Mhow m h o w -MHPL m h p l -M.H.P. m h p -MHP m h p -MHP's m h p's -MHRA m h r a -M. H. R. m h r -MHSA m h s a -MHSC m h s c -MHS m h s -MHT m h t -M.I.A. m i a -M.I.A.'s m i a's -M.I.C.M. m i c m -M.I.H. m i h -M&I m and i -M. I. m i -M.I. m i -M.I.N. m i n -M.I.R.V. m i r v -M.I.S. m i s -M.I.T. m i t -MJAHL's m j a h l's -M.J.A. m j a -MJA m j a -MJBHA m j b h a -MJB m j b -MJC m j c -M.J.F. m j f -MJF m j f -MJG m j g -MJHL m j h l -MJHL's m j h l's -M&J m and j -M. J. m j -M.J. m j -M. J. Y. m j y -MKBHD m k b h d -MKB m k b -MKDE m k d e -MKE m k e -MKFM m k f m -MKG m k g -MKK m k k -MKMF m k m f -M. K. m k -M.K. m k -MKNG m k n g -MKP m k p -MKRN m k r n -MKs m k's -MKS m k s -MKT m k t -MKTO m k t o -MKTV m k t v -MKZ's m k z's -M.L.A. m l a -MLA m l a -MLANA m l a n a -MLAs m l a's -MLB m l b -MLBPA m l b p a -MLCA m l c a -mlc m l c -M.L.C. m l c -MLC m l c -MLCs m l c's -MLD m l d -MLE m l e -MLF m l f -mlg m l g -MLG m l g -MLH m l h -MLIA m l i a -mli m l i -MLI m l i -MLIM m l i m -MLK m l k -MLK's m l k's -Mlle m l l e -MLL m l l -MLLT m l l t -M&L m and l -M. L. m l -M.L. m l -MLM m l m -MLND m l n d -M.L.N. m l n -MLN m l n -MLPH m l p h -MLP m l p -MLPs m l p's -M. L. R. m l r -MLR m l r -MLSE m l s e -M. L. S. m l s -M.L.S. m l s -MLS m l s -MLT m l t -MLW m l w -MLWS m l w s -MM&A m m and a -M. M. J. m m j -MMK m m k -MML m m l -M&M m and m -M. M. m m -M.M. m m -MMSA m m s a -M&M's m and m's -M. M. S. m m s -MMS m m s -MMST m m s t -MMTB's m m t b's -MMTS m m t s -MMTV m m t v -MMWR m m w r -MMX m m x -MNA m n a -MNBA m n b a -MNC m n c -MNCPPC m n c p p c -MNCs m n c's -MNDM m n d m -MNDNR m n d n r -MNDO m n d o -MNDR m n d r -M&NF m and n f -MNF m n f -MNG m n g -MNI m n i -MNLA m n l a -MNLA's m n l a's -MNLF m n l f -MNM m n m -M. N. m n -M.N. m n -MNNA m n n a -MNN m n n -MNP m n p -MNPP m n p p -MNRG m n r g -MNSD m n s d -MNS m n s -MNTC m n t c -MNT m n t -MNZM m n z m -MNZ m n z -MOBKL m o b k l -MOBK m o b k -M.O.B. m o b -M.O.D. m o d -M.O.D.O.K. m o d o k -M.O.G.U.E.R.A.'s m o g u e r a's -M. O. H. m o h -M. O. m o -M.O. m o -M.O.P. m o p -M.O.R. m o r -M.O.T. m o t -MPAA m p a a -MpA m p a -MPA m p a -MPAs m p a's -MPB m p b -MPBN m p b n -MPBu m p b u -MPCA m p c a -MPCI m p c i -MPC m p c -MPD m p d -MPE m p e -MPF m p f -MPG m p g -M.P.H. m p h -MPH m p h -MP&I m p and i -MPi m p i -MPI m p i -MPIO m p i o -MPKAB m p k a b -MPLAD m p l a d -MPLA m p l a -MPL m p l -MPLMs m p l m's -MPLR m p l r -MPLS m p l s -M&P m and p -MPM m p m -M. P. m p -M.P. m p -MPO m p o -MPPJ m p p j -M.P.P. m p p -MPP m p p -MPPSC m p p s c -MPPs m p p's -MPQC m p q c -MPR m p r -MPRO m p r o -MPRP m p r p -MPRS m p r s -MPSE m p s e -MPSF m p s f -M.P.'s m p's -MP's m p's -MPs m p's -MPS m p s -MPThe m p t h e -MPT m p t -Mpu m p u -MPU m p u -MPV m p v -MPW m p w -MPZ m p z -MQM m q m -M. Q. m q -Mra m r a -MRA m r a -M. R. B. m r b -MRB m r b -MRBs m r b's -MRCB m r c b -MRCK m r c k -MRCM m r c m -MRC m r c -M.R.C.S. m r c s -MRCS m r c s -MRDC m r d c -MRD m r d -mre m r e -MRF m r f -MR&LE m r and l e -MRL m r l -Mr. mister -MRM m r m -M. R. m r -M.R. m r -mRNA m r n a -mRNAs m r n a's -MRPGM m r p g m -MRP m r p -MRPS m r p s -MRR m r r -MRSA m r s a -M.R.S.C. m r s c -Mrs. misses -MRSM m r s m -MRT m r t -MRTs m r t's -MRTS m r t s -MRTT m r t t -MRU m r u -MSAA m s a a -MSAC m s a c -MSAD m s a d -MSA m s a -M.S.A.S. m s a s -MSAW m s a w -MSB m s b -MSBO m s b o -MSBSD m s b s d -MSCC m s c c -M.S.C. m s c -MSC m s c -msd m s d -MSD m s d -MSDN m s d n -MSDS m s d s -Mse m s e -MSFC m s f c -M.S.F. m s f -MSF m s f -MSFT m s f t -MSG m s g -MSI m s i -MSJ m s j -MSK m s k -MSL m s l -M&S m and s -Ms. miss -MSML m s m l -M.S.M. m s m -MSM m s m -M. S. m s -M.S. m s -MSNBC m s n b c -MSNBC's m s n b c's -MSN m s n -MSOB m s o b -MSOM m s o m -M.S.P. m s p -MSP m s p -MSPs m s p's -MSR m s r -MSRP m s r p -MSRTC m s r t c -MSSH m s s h -MSSK m s s k -MSSMLP m s s m l p -MSSM m s s m -MSS m s s -MSTA m s t a -mst m s t -MST m s t -MSTS m s t s -MSU's m s u's -MSVCC m s v c c -MSV m s v -MSVU m s v u -MSX m s x -MSY m s y -MSZP m s z p -MTAC m t a c -Mta m t a -M.T.A. m t a -MTA m t a -MTA's m t a's -MTAs m t a's -MTBE m t b e -MTBI m t b i -MTB m t b -MTC m t c -MTDB m t d b -M.T.D. m t d -MTD m t d -MTD's m t d's -MTG m t g -MTHFR m t h f r -MTHL m t h l -MTIC m t i c -MTI m t i -MTJ m t j -MTK m t k -MTKO m t k o -M&T m and t -MTM m t m -MTMR m t m r -M. T. m t -M.T. m t -Mtor m t o r -MTOSI m t o s i -M.T.O.W. m t o w -MTPA m t p a -MTPC m t p c -MTP m t p -MTQ m t q -MTRCB m t r c b -MTRC m t r c -MTRJ m t r j -MTR m t r -M. T. S. m t s -MTS m t s -MTSU m t s u -MTTM m t t m -MTT m t t -MTU m t u -MTV m t v -MTVR m t v r -MTV's m t v's -mtvU m t v u -MTVu m t v u -MTVU m t v u -MTY m t y -MTZ m t z -MUKW m u k w -M. U. m u -MUSL m u s l -M.U.s m u's -M. U.S. m u s -MVA m v a -M. V. C. m v c -M.V.C. m v c -MVC m v c -M. V. D. m v d -MVD m v d -MVH m v h -MVK m v k -M.V.M. m v m -MVM m v m -MVMs m v m's -M. V. m v -M.V. m v -M.V.O. m v o -MVO m v o -MVP m v p -MVPs m v p's -MVPS m v p s -M.V. P.T. m v p t -MVs m v's -MVS m v s -MVSN m v s n -MVSR m v s r -MVT m v t -MVV m v v -M.W.A.M. m w a m -MWA m w a -MWC m w c -MWe m w e -M. W. E. m w e -MWF m w f -MWHL m w h l -MWIFF m w i f f -MWI m w i -MWJCHL m w j c h l -MWJHL m w j h l -M. W. J. m w j -M. W. m w -M.W. m w -M. X. m x -myb m y b -myc m y c -Myc m y c -MYC m y c -MYCN m y c n -MyDD m y d d -M. Y. m y -M.Y. m y -MySQL m y s q l -MYSQL m y s q l -MZC m z c -MZH m z h -MZM m z m -M. Z. m z -MZP m z p -MZT m z t -NAACCR n a a c c r -NAACL n a a c l -NAAC n a a c -N.A.A.C.P. n a a c p -NAACP n a a c p -NAACP's n a a c p's -N.A.C.L. n a c l -NAFBL n a f b l -NAFC n a f c -NAFC's n a f c's -NAFH n a f h -NAFI n a f i -N. A. n a -N.A. n a -NBADL n b a d l -N.B.A. n b a -NBA n b a -N.B.A.'s n b a's -NBA's n b a's -NBAs n b a's -NBCC n b c c -NBC n b c -NBC's n b c's -NBCSN n b c s n -NBDL n b d l -NBD n b d -NBFA's n b f a's -NBF n b f -NBG n b g -NBI n b i -NBK n b k -NBL n b l -N. B. n b -N.B. n b -N.B.N. n b n -NBN n b n -NBP n b p -NBQ n b q -NBR n b r -NBR's n b r's -NBSK n b s k -nbs n b s -NBS n b s -NBSP n b s p -NBTE n b t e -NBT n b t -NBTwo n b t w o -NBTY n b t y -NBW n b w -NCAAs n c a a's -NCAM n c a m -NCA n c a -NCAP n c a p -NCBC n c b c -NCBI n c b i -NCCC n c c c -NCCF n c c f -NCCMH n c c m h -NCC n c c -NCCP n c c p -NCCs n c c's -NCCS n c c s -NCCU n c c u -NCDC n c d c -NCDD's n c d d's -NCD n c d -NCES n c e s -NCFA n c f a -N.C.F. n c f -NCF n c f -NCG n c g -NCGS n c g s -NCHC n c h c -NCH n c h -NCHU n c h u -NCID n c i d -NCI n c i -NCIS n c i s -NCIS's n c i s's -NCKU n c k u -NCLB n c l b -NCLC n c l c -NCL n c l -NCLR n c l r -NCLT n c l t -NCMA n c m a -NCMC n c m c -NCMM n c m m -NCM n c m -NCMP n c m p -NCNB n c n b -N. C. n c -N.C. n c -N.C.O. n c o -NCO n c o -NCO's n c o's -NCOs n c o's -NCPA n c p a -NCPC n c p c -NCP n c p -NCPO n c p o -NCRC n c r c -NCR n c r -NCSU n c s u -NCSY n c s y -NCTA n c t a -NCTC n c t c -NCTE n c t e -NCT n c t -NCUA n c u a -NCVO n c v o -NCVS n c v s -NCVT n c v t -NCWM n c w m -NCW n c w -NDA n d a -NDE n d e -NDEP n d e p -NDFB n d f b -NDFT n d f t -N.D.G. n d g -N. D. n d -N.D. n d -N.D.N.Y. n d n y -NDPH n d p h -NDPK n d p k -N.D.P. n d p -NDP n d p -NDP's n d p's -NDRC n d r c -NDRE n d r e -NDRF n d r f -N.E.A.R. n e a r -NECW n e c w -N. E. n e -N.E. n e -N.E.R.D. n e r d -NERFU n e r f u -ner n e r -N.E.R. n e r -NER n e r -N.E.W.S.T. n e w s t -NFA n f a -NFATc n f a t c -NFAT n f a t -NFB n f b -NFB's n f b's -NFC n f c -NFC's n f c's -NFD n f d -NFEA n f e a -nfed n f e d -NFFC n f f c -NFF n f f -NFH n f h -NFHS n f h s -NFIB n f i b -NFI n f i -NFISD n f i s d -N.F.L. n f l -NFL n f l -NFLPA n f l p a -NFL's n f l's -N. F. n f -N.F. n f -NFO n f o -NFPA n f p a -NFPF n f p f -NFP n f p -NFPW n f p w -NFRS n f r s -NFS n f s -NGC n g c -Nge n g e -NGF n g f -N.G.L. n g l -N. G. n g -N.G. n g -NGO n g o -NGO's n g o's -NGOs n g o's -NGRC n g r c -N.G.R. n g r -NGR n g r -NGRR n g r r -NGS n g s -NGSS n g s s -NGST n g s t -NGTC n g t c -Nha n h a -NHA n h a -NHCEs n h c e's -NHC n h c -NHCP n h c p -NHCs n h c's -NHD n h d -NHHD n h h d -NHHI n h h i -nhi n h i -NHI n h i -NHK n h k -NHK's n h k's -N.H.L. n h l -NHM&W n h m and w -N. H. n h -N.H. n h -NHP n h p -NHPs n h p's -NHPS n h p s -NHRA n h r a -NHRC n h r c -NHSCT n h s c t -NHS n h s -NHST n h s t -NHW n h w -N.I.B.B.L.E. n i b b l e -NIBC n i b c -NIBR n i b r -N. I. n i -N.I. n i -N'I n i -N.J.A.C. n j a c -N. J. A. n j a -NJA n j a -N. J. C. n j c -NJC n j c -NJIT n j i t -N. J. M. n j m -NJM n j m -N. J. n j -N.J. n j -NJN n j n -Njoo n j o o -NJPS n j p s -NJPW n j p w -N.J.S.A. n j s a -N.J.'s n j's -NKL n k l -N. K. n k -N.K. n k -N. K. N. n k n -NKP n k p -NKP's n k p's -NKR n k r -NKT n k t -NKU n k u -nkvd n k v d -NKVD n k v d -NKVM n k v m -nkv n k v -NKX n k x -NLAES n l a e s -NLAI n l a i -NLA n l a -NLB n l b -NLCF n l c f -NLC n l c -NLCS n l c s -NLDC n l d c -NLD n l d -NLD's n l d's -NLDS n l d s -N. L. n l -N.L. n l -NLP n l p -NLRB n l r b -NLRP n l r p -NLRs n l r's -NLW n l w -NLX n l x -NMAA n m a a -NMA n m a -NMBS n m b s -NMBU n m b u -NMCA n m c a -NMCB n m c b -NMC n m c -N. M. n m -N.M. n m -NMP n m p -NMR n m r -NMRW n m r w -NMT n m t -NMU n m u -NMV n m v -NMW n m w -NNCL n n c l -NNFL n n f l -NNF n n f -N. N. n n -N.N. n n -NNSA n n s a -NNSS n n s s -NNSU n n s u -NNSW n n s w -N.O.R. n o r -NPA n p a -NPBD n p b d -NPBL n p b l -NPB n p b -NPC n p c -NPCs n p c's -NPDC n p d c -NPD n p d -NPF n p f -NPF's n p f's -NPGL n p g l -NPG n p g -NPGS n p g s -NPH n p h -NPHS n p h s -NPIM n p i m -NPI n p i -NPJ n p j -NPK n p k -NPL n p l -NPMA n p m a -NPM n p m -NPMs n p m's -N. P. n p -N.P. n p -NP n p -NPO n p o -NPP n p p -NPP's n p p's -NPRL n p r l -NPRN n p r n -NPR n p r -NPRR n p r r -NPR's n p r's -NPRSO n p r s o -NPSC n p s c -NPSG n p s g -NPSL n p s l -NP's n p's -NPs n p's -NPS n p s -NPTI n p t i -NPT n p t -NPV n p v -NPWE n p w e -NPWS n p w s -NPY n p y -NQAI n q a i -NQEA n q e a -NQHS n q h s -N. Q. n q -nri n r i -Nri n r i -NRI n r i -NRIs n r i's -NRJ n r j -NRJs n r j's -NRK n r k -NRLA n r l a -NRL n r l -NRL's n r l's -NRMA n r m a -NRM n r m -NRN n r n -nr n r -N. R. n r -N.R. n r -N.R.A. n r a -NSA n s a -NSAP n s a p -NSA's n s a's -NSB n s b -NSC n s c -NSD n s d -NSE n s e -NSFE n s f e -NSF n s f -NSFW n s f w -NSG n s g -NSI n s i -NSL n s l -NSM n s m -NSN n s n -N. S. n s -N.S. n s -NSO n s o -NSOs n s o's -NSPCC n s p c c -NSPC n s p c -NSP n s p -NSRI n s r i -NSRL n s r l -NSR n s r -NSSCD n s s c d -NSSDC n s s d c -NSS n s s -NSTA n s t a -NST n s t -NSU n s u -NSV n s v -NSVT n s v t -NSWBLF n s w b l f -NSWC n s w c -NSWCV n s w c v -NSWEC n s w e c -NSWGR n s w g r -N.S.W. n s w -NSW n s w -Nta n t a -NTA n t a -NTA's n t a's -ntb n t b -NTB n t b -NTC n t c -NTD n t d -NTFL n t f l -NTF n t f -NTI n t i -NTKF n t k f -NTL n t l -N. T. n t -N.T. n t -NTR n t r -NTR's n t r's -NTSB n t s b -NTSB's n t s b's -NTSC n t s c -NTS n t s -NTTF n t t f -NTT n t t -NTTR n t t r -NTUC n t u c -NTU n t u -NTVB n t v b -NTV n t v -ntw n t w -N.U. n u -NVA n v a -NVAO n v a o -NVC n v c -NVDA n v d a -NvDA's n v d a's -NVFC n v f c -NVGOP n v g o p -NVHJ's n v h j's -NVH n v h -NVI n v i -NVL n v l -N. V. n v -N.V. n v -NWAFU n w a f u -nwa n w a -N.W.A. n w a -NWA n w a -NWA's n w a's -N.W.F.P. n w f p -NWFP n w f p -NWMP n w m p -N&W n and w -NWN n w n -N. W. n w -N.W. n w -NWPD n w p d -N.W.P. n w p -NWP n w p -NWS n w s -N.W.T. n w t -NWT n w t -nyc n y c -N.Y.C. n y c -NYC n y c -NYC's n y c's -N.Y. G.O.P. n y g o p -N.Y.L. n y l -N. Y. n y -N.Y. n y -N.Y.P.D. n y p d -NYPD n y p d -NYPD's n y p d's -NYPL n y p l -N.Y.P. n y p -NY's n y's -N.Y.S. n y s -N.Y.S.V. n y s v -N.Y.U. n y u -NYU n y u -NYU's n y u's -NYYC n y y c -NZAID n z a i d -NZAOD n z a o d -NZCA n z c a -NZDT n z d t -NZEF n z e f -NZETC n z e t c -NZFC n z f c -NZF n z f -N'Zif n z i f -NZiK n z i k -N'Zi n z i -NZIV n z i v -NZL n z l -NZLP n z l p -N.Z. n z -NZPA n z p a -NZSAS n z s a s -NZ's n z's -NZS n z s -NZTA n z t a -N.Z.W.P.W. n z w p w -O.A.C. o a c -O. A. o a -O.A. o a -O.A.R. o a r -O.A.S. o a s -O.B.E. o b e -obl o b l -Oblt o b l t -O.B. o b -O. C. o c -O.C. o c -Octl o c t l -O.C.T. o c t -O. D. o d -O.E. o e -O.F.M. o f m -O.F. o f -ofr o f r -O.F.R. o f r -OFR o f r -O.F.T.B. o f t b -OGL o g l -O&G o and g -O. G. o g -O.G. o g -O.G.S. o g s -OGS o g s -O.H.A. o h a -O. H. o h -O.H. o h -OHV o h v -O. I. o i -O.I. o i -O. J. o j -O.J. o j -OJ o j -OJSC o j s c -OKBM o k b m -OKB o k b -OKC o k c -OK'd o k d -OKd o k d -OKD o k d -oke o k e -Oke o k e -OKH o k h -O.K.I. o k i -OKK o k k -OKM o k m -O&K o and k -O. K. o k -O.K. o k -O.L.F.A.L. o l f a l -O. L. K. o l k -O. L. o l -O.L. o l -Olo o l o -Olov o l o v -OLPC o l p c -OLPH o l p h -ols o l s -OLs o l's -OLS o l s -OLSR o l s r -OLTL o l t l -OLVT o l v t -olvwm o l v w m -olwm o l w m -OMB o m b -OMC o m c -OMCS o m c s -OMD o m d -O&MFL o and m f l -OMF o m f -omg o m g -OMG o m g -OMGs o m g's -O. M. o m -O.M. o m -OmOm o m o m -Om's o m's -OMTP o m t p -OMVG o m v g -OMW o m w -OMX o m x -ONCHR o n c h r -ONC o n c -oncu o n c u -ONDH o n d h -ond o n d -onf o n f -ONF o n f -ONGC o n g c -ONIR o n i r -ONJSC o n j s c -ONM o n m -ONMR o n m r -O. N. o n -O.N. o n -O&O o and o -OOOA o o o a -O. O. o o -O.O. o o -OO o o -OOO's o o o's -Oop o o p -OOP o o p -OOPSLA o o p s l a -oor o o r -oose o o s e -Oo's o o's -OOTP o o t p -Ootw o o t w -OOUR o o u r -OPAC o p a c -OPAG o p a g -O.P.A. o p a -OPBF o p b f -OPB o p b -OPC o p c -OPCS o p c s -OPCW o p c w -OPD o p d -O.P.I. o p i -OPIRG o p i r g -OPJHL o p j h l -OPLC o p l c -OPL o p l -OPMB o p m b -OPM o p m -OPMs o p m's -OPN o p n -O. P. o p -O.P. o p -opr o p r -Opr o p r -OPR o p r -O.R.C. o r c -ORECA o r e c a -ORFs o r f's -ORMO o r m o -ORNL o r n l -OR&N o r and n -O. R. o r -osaa o s a a -OSAA o s a a -osa o s a -Osa o s a -O.S.A. o s a -OSA o s a -O.S.B. o s b -OSB o s b -OSBs o s b's -O.S.N. o s n -OSN o s n -O. S. o s -O.S. o s -OSP o s p -OSR o s r -OSTM o s t m -O.S.T. o s t -OSTP o s t p -OSTs o s t's -osv o s v -OSV o s v -OTB o t b -OTC o t c -OTJ o t j -OTL o t l -OTMH o t m h -OTO o t o -O.T. o t -OTP o t p -OTR o t r -OTs o t's -OTS o t s -Otu o t u -OTU o t u -otv o t v -O.U. o u -OU o u -OUP o u p -OUSA o u s a -OVA's o v a's -ovca o v c a -Ovca o v c a -OVC o v c -OVC's o v c's -Ovda o v d a -OVF o v f -OVM o v m -Ovo o v o -OVO o v o -ov o v -Ov o v -O. V. o v -O.V. o v -OV o v -OVP o v p -OVS o v s -OVT o v t -OVW o v w -O.W.A. o w a -OWBT o w b t -OWCs o w c's -OWGR o w g r -OWHA's o w h a's -OWIU o w i u -OWM o w m -O. W. o w -O.W. o w -P. A. C. p a c -P. A. J. p a j -P.A.J. p a j -P. A. M. p a m -P.A.M. p a m -pa p a -P. A. p a -P.A. p a -PA p a -PAQ p a q -P.A.R. p a r -P. A. S. p a s -PBAA p b a a -PBA p b a -PBA's p b a's -PBB p b b -PBCC p b c c -pBCE p b c e -pbc p b c -P.B.C. p b c -PBF p b f -PBG p b g -PBIL p b i l -PBI p b i -PBK p b k -PBL p b l -PBM p b m -PBMR p b m r -PBO p b o -pb p b -P. B. p b -P.B. p b -PB p b -PBP p b p -PBR p b r -PBS p b s -PBT p b t -PBX p b x -PBY p b y -P. C. A. p c a -PCA p c a -PCB p c b -PCB's p c b's -PCBs p c b's -PCBS p c b s -PCC p c c -PCCS p c c s -PCCW p c c w -PCCW's p c c w's -PCDDs p c d d's -PCDHB p c d h b -PCDH p c d h -PCD p c d -PCE p c e -PCeU p c e u -PCFCL p c f c l -PCFL p c f l -PCF p c f -PCFS p c f s -PCG p c g -PCGS p c g s -PCHA p c h a -PCHA's p c h a's -P.C.H. p c h -PCH p c h -PCHR p c h r -PCIe p c i e -pci p c i -PCI p c i -PCJHL p c j h l -P. C. J. p c j -PCJ p c j -PCK p c k -PCLM p c l m -pcl p c l -PCL p c l -PCMCIA p c m c i a -PCMNO p c m n o -PCM p c m -PCMS p c m s -PCNA p c n a -PCN p c n -PcoA p c o a -PCOE p c o e -PCO p c o -pc p c -P. C. p c -P.C. p c -PC p c -PCPFL p c p f l -PCP p c p -PCPV p c p v -PCRC p c r c -PCRE p c r e -PCRev p c r e v -PCRM p c r m -PCR p c r -PCSK p c s k -PCSOM p c s o m -pcs p c s -P.C.s p c's -PC's p c's -PCTL p c t l -PCT p c t -PCTV p c t v -PCU p c u -PCU's p c u's -PCW p c w -PCX p c x -pDAB p d a b -PDAB p d a b -PDA p d a -PDAs p d a's -PDBML p d b m l -PDB p d b -pdbp p d b p -PDBsum p d b s u m -PDCI p d c i -PDC p d c -PDCPD p d c p d -PDC's p d c's -PDCs p d c's -PD&D p d and d -PDD p d d -PDE p d e -PDEs p d e's -PDES p d e s -pdf p d f -PDF p d f -PDFs p d f's -PDFT p d f t -PDGF p d g f -PDG p d g -PDHJ p d h j -PDH p d h -PDI p d i -PDJ p d j -PDK p d k -PDL p d l -PDN p d n -PDO p d o -P&D p and d -P. D. p d -P.D. p d -PD p d -PDP p d p -PDP's p d p's -PDPs p d p's -P. D. Q. p d q -PDR p d r -PDSA p d s a -PDSI p d s i -PD's p d's -PDs p d's -P.D.S. p d s -PDS p d s -PDSP p d s p -PDT p d t -P.E.I. p e i -P.E.I.'s p e i's -P.E.N. p e n -P. E. p e -P.E. p e -P.F.L. p f l -PF's p f's -PFs p f's -PFS p f s -PFU p f u -PFV p f v -PFW&C p f w and c -P.G.A. p g a -PGA p g a -PGM p g m -PGMs p g m's -PG&N p g and n -P&G p and g -P. G. p g -P.G. p g -P.G.T. p g t -PHD p h d -P. H. G. p h g -PHH p h h -PHHS p h h s -PHLF p h l f -Phlo p h l o -PHL p h l -PHN p h n -P&H p and h -P. H. p h -P.H. p h -php p h p -PHP p h p -PHPs p h p's -P.I.D.E. p i d e -P&I p and i -P. I. p i -P.I. p i -P. I. W. p i w -Piz p i z -PJB p j b -PJC p j c -PJD p j d -P. J. F. p j f -P.J.K. p j k -pj p j -P. J. p j -P.J. p j -PJ p j -PJ's p j's -PJs p j's -P.J.T. p j t -P. K. p k -P.K. p k -PK p k -PLCC p l c c -plc p l c -PLC p l c -PLC's p l c's -PLCs p l c's -P.L.O. p l o -P. L. p l -P.L. p l -PLP p l p -PLX p l x -P. M. A. p m a -PMC p m c -PMG p m g -PML p m l -PMLP p m l p -PMMA p m m a -PMMoV p m m o v -PMM p m m -PMMS p m m s -PMMT p m m t -PMOI p m o i -PMO p m o -PMPC p m p c -PMPF p m p f -p.m. p m -p.m p m -P. M. p m -P.M. p m -PM p m -PMP p m p -PMP's p m p's -PMRN p m r n -PMRO p m r o -PMR p m r -PMRR p m r r -PMSE p m s e -PMSL p m s l -PMSM p m s m -PM's p m's -PMs p m's -P. M. S. p m s -PMS p m s -PMSSY p m s s y -PMT p m t -PMVY p m v y -PMWA p m w a -PNaCl p n a c l -pna p n a -PNA p n a -Pnau p n a u -pnb p n b -PNB p n b -PNC p n c -PNE p n e -PNE's p n e's -PNEs p n e's -PNETs p n e t's -pneus p n e u s -PNFA p n f a -PNF p n f -PNGIA p n g i a -png p n g -PNG p n g -PNH p n h -PNI p n i -PNL p n l -PNL's p n l's -PNMT p n m t -PNNL p n n l -PNNs p n n's -PNoy p n o y -pn p n -P. N. p n -P.N. p n -PN p n -PNP p n p -P.N.R.A. p n r a -P.O.D. p o d -P.O.D.'s p o d's -P&O p and o -P. O. p o -P.O. p o -P.O.S. p o s -P.O.V. p o v -P.O.W. p o w -P.O.W.'s p o w's -PPACA p p a c a -PPA p p a -PPBS p p b s -PPC p p c -PPD p p d -PPE p p e -PPF p p f -PPG p p g -P&PH p and p h -PPH p p h -PPi p p i -P.P.I. p p i -PPI p p i -PPJ p p j -PPKM p p k m -PPK p p k -PPL p p l -PPM p p m -PPNB p p n b -PPN p p n -PPO p p o -P. P. p p -P.P. p p -PPRP p p r p -PPR p p r -PP's p p's -PPS p p s -PPT p p t -PPTV p p t v -PPV p p v -PPVs p p v's -PPy p p y -PQDT p q d t -PQ p q -PQQ p q q -PQ's p q's -PQS p q s -PRB p r b -PRCA p r c a -PRC p r c -PRCS p r c s -PRDM p r d m -PRD p r d -PRD's p r d's -P.R.I.M.E. p r i m e -pr p r -P. R. p r -P.R. p r -PR p r -P.S.A. p s a -P.S.C. p s c -P.S.K. p s k -PSP p s p -P. s p's -P.'s p's -Ps p's -P. S. p s -P.S. p s -PSQL p s q l -PSR p s r -PSRU p s r u -PSSA p s s a -PSSA's p s s a's -PSSAs p s s a's -PSSI p s s i -PSS p s s -PSS's p s s's -PST p s t -PSU p s u -PSV p s v -PSW p s w -pTA p t a -PTA p t a -PTA's p t a's -PTAs p t a's -PTB p t b -PTBT p t b t -PTCH p t c h -PTC p t c -Pte p t e -PTEs p t e's -PTFE p t f e -ptf p t f -PTF p t f -PTH p t h -Ptie p t i e -PTI p t i -PTI's p t i's -PTK p t k -PTLLS p t l l s -PTL p t l -PTLs p t l's -PTNNT p t n n t -Ptol p t o l -PTO p t o -PTPN p t p n -PTP p t p -pt p t -P. T. p t -P.T. p t -PT p t -PTSD p t s d -PTSE p t s e -PTS p t s -PTTGC p t t g c -PTTG p t t g -PTTOW p t t o w -PTT p t t -PTTs p t t's -PTUN p t u n -PTV p t v -PTV's p t v's -P'Twa p t w a -PTY p t y -P.U.F. p u f -Puiu p u i u -PUKKE p u k k e -PUK p u k -Pul p u l -P. U. M. p u m -PVA p v a -PVAs p v a's -PVB p v b -P.V.C. p v c -PVC p v c -PvdA p v d a -PVEM p v e m -pve p v e -PVH p v h -P.V.L. p v l -PVL p v l -PVN p v n -PVO p v o -PVP p v p -P. V. p v -P.V. p v -PV p v -pvr p v r -PVR p v r -PVRS p v r s -PVs p v's -PVS p v s -PVU p v u -PWA p w a -PWB p w b -PWC p w c -PWD p w d -PWF p w f -P. W. G. p w g -P.W.G. p w g -PWG p w g -PWI p w i -pwll p w l l -PWM p w m -P.W.O. p w o -P&W p and w -P. W. p w -P.W. p w -PW p w -Pyi p y i -PYI p y i -Pyk p y k -PYK p y k -Pyl p y l -PyL p y l -PYP p y p -P. Y. p y -Pyu p y u -Pyw p y w -Pyx p y x -PZB p z b -PZL p z l -PZP p z p -P. Z. p z -Q. A. P. q a p -QAP q a p -Q. A. q a -QA q a -Q&A q and a -Q&A's q and a's -Q&As q and a's -QbA q b a -QBE q b e -QBH q b h -QBL q b l -Q.B. q b -QB q b -Q.C.B. q c b -QCD q c d -Q.C. q c -QD q d -QDS q d s -QEA q e a -QEC q e c -Q.E.D. q e d -QED q e d -Q.E.H. q e h -Q. E. q e -Q.H.C. q h c -QHP q h p -Q.H. q h -Q.I. q i -Q. J. q j -QJ q j -Q. J. R. q j r -Q. N. q n -QPF q p f -QPFS q p f s -QPM q p m -QPO q p o -QPOs q p o's -QPP q p p -QP q p -Q.P.R. q p r -QPR q p r -QQ q q -QRL q r l -QRL's q r l's -QRNA q r n a -QROPS q r o p s -QRP q r p -QR q r -QRS q r s -QRT q r t -QRV q r v -QSAR q s a r -QSES q s e s -QSI q s i -QSL q s l -QSM q s m -QSO q s o -QST q s t -QSY q s y -QVC q v c -QVD q v d -QVM q v m -Q.V. q v -R.A.B. r a b -R. A. E. r a e -R.A.F. r a f -R.A.J. r a j -R. A. r a -R.A. r a -R&A r and a -RAV r a v -R&AW r and a w -RAZR r a z r -RBAC r b a c -RbAg r b a g -RBA r b a -RBBP r b b p -RBC r b c -RBCs r b c's -RBD r b d -RBE r b e -RBGE r b g e -RBG r b g -RBI r b i -RBIs r b i's -R. B. J. r b j -R.B.J. r b j -RBK r b k -R.B.L. r b l -RBL r b l -RBMG r b m g -RBMG's r b m g's -RBM r b m -RBMY r b m y -RBO r b o -RBP r b p -R&B r and b -R. B. r b -R.B. r b -RBR r b r -RBS r b s -RBTCO's r b t c o's -RBTH r b t h -RBWH r b w h -RBW r b w -RBX r b x -RBZ r b z -RCAC r c a c -RCAF r c a f -rca r c a -R.C.A. r c a -RCA r c a -RCA's r c a's -RCBC r c b c -RCB r c b -RCCA r c c a -RCCD r c c d -RCCL r c c l -RCCM r c c m -RCC r c c -RCI r c i -R. C. K. r c k -RCL r c l -R.C.M. r c m -RCM r c m -RCN r c n -RCPO r c p o -RCP r c p -RCPT r c p t -R&C r and c -rc r c -R. C. r c -R.C. r c -RCRD r c r d -RCR r c r -RCSB r c s b -RCS r c s -RCT r c t -RCTs r c t's -RCTS r c t s -RCTV r c t v -RCV r c v -RCW r c w -RDB r d b -RDC r d c -RDD r d d -RdE r d e -RDFC r d f c -RDF r d f -R.D.G. r d g -R&D r and d -R. D. r d -R.D. r d -R.E.A. r e a -R. E. B. r e b -R.E.D. r e d -R.E.L. r e l -R.E.M. r e m -R.E.M.'s r e m's -R.E.O. r e o -REPL r e p l -REPLs r e p l's -R.E.P. r e p -R. E. r e -R.E. r e -R.E.R. r e r -R.F.A. r f a -R.F.C. r f c -R. F. r f -R.F. r f -R. F.S. r f s -R.F.W. r f w -RFW r f w -RFX r f x -RGB r g b -RGD r g d -RGF r g f -RGG r g g -RGI r g i -RGK r g k -RGMA r g m a -RGNL r g n l -RGPH r g p h -R&G r and g -R. G. r g -R.G. r g -RGR r g r -RGS r g s -RGS's r g s's -RGTP r g t p -RGU r g u -RGV r g v -RGX r g x -R. H. C. r h c -RHC r h c -RHD r h d -RHHF r h h f -RHH r h h -RHHS r h h s -RHIBs r h i b's -RHIC r h i c -RHI r h i -Rhiw r h i w -RHK r h k -rhl r h l -RHM r h m -RhoG r h o g -Rho's r h o's -RHP r h p -R. H. r h -R.H. r h -R.H.S.J. r h s j -R. I. C. r i c -R.I.C. r i c -R. I. P. r i p -R.I.P. r i p -R&I r and i -R. I. r i -R.I. r i -riu r i u -Riu r i u -rivs r i v s -Rivu r i v u -RIXS r i x s -Rixt r i x t -RJB r j b -RJD r j d -RJE r j e -rjf r j f -RJHS r j h s -R. J. J. r j j -RJJ r j j -RJL r j l -RJN r j n -R. J. r j -R.J. r j -RJ r j -RJR r j r -RJ's r j's -RJTD r j t d -R. K. B. r k b -RKD r k d -RKI r k i -RKKA r k k a -RKL r k l -R.K.O. r k o -RKO r k o -RKO's r k o's -R. K. r k -R.K. r k -RLM r l m -RLM's r l m's -R. L. r l -R.L. r l -RL r l -RLV r l v -rly r l y -RMAF r m a f -RMAG r m a g -RMA r m a -RMB r m b -RMCH r m c h -RMCL r m c l -R.M.C. r m c -RMC r m c -RMDs r m d's -R.M.E.S. r m e s -RMFL r m f l -R.M.F. r m f -RMF r m f -RMG r m g -R. M. H. r m h -RMI r m i -RMIT r m i t -RMIT's r m i t's -RMJM r m j m -R.M.K. r m k -RMK r m k -R. M. L. r m l -RML r m l -RMLs r m l's -RMM r m m -RMP r m p -rm r m -R. M. r m -R.M. r m -RM r m -RMR r m r -RMRS r m r s -RMSD r m s d -RMSDs r m s d's -RMSE r m s e -RMs r m's -RMS r m s -R. M. W. r m w -R.M.W. r m w -RMW r m w -RMX r m x -RNAO r n a o -rna r n a -RNA r n a -RNA's r n a's -RNAs r n a's -RNAS r n a s -RNC r n c -RND r n d -RNE r n e -Rnet r n e t -RNF r n f -RNG r n g -Rnic r n i c -RNJD r n j d -RNK r n k -RNLAF r n l a f -RNLI r n l i -RNLI's r n l i's -RNNs r n n's -RNOH r n o h -RNP r n p -RNPs r n p's -RNPS r n p s -R. N. r n -R.N. r n -RN r n -R.N.R. r n r -RNZ r n z -R.O.C.K. r o c k -R.O.C. r o c -ROKMC r o k m c -ROKN r o k n -rOmpB r o m p b -Rooi r o o i -ROP r o p -R. O. r o -R.O. r o -ROVs r o v's -Roxb r o x b -Roxx r o x x -RPA r p a -RPAYC r p a y c -RPB r p b -RP&C r p and c -rpc r p c -RPC r p c -Rpe r p e -RPE r p e -RPF r p f -RPGA r p g a -rpg r p g -RPG r p g -RPG's r p g's -RPGs r p g's -RPI r p i -RPI's r p i's -RPK r p k -RPL r p l -rpm r p m -RPM r p m -RPMS r p m s -RPO r p o -RPP r p p -R. P. r p -R.P. r p -RP r p -RPs r p's -R.P.S. r p s -RPS r p s -RPT r p t -RPVE r p v e -RPV r p v -R. Q. r q -R.Q. r q -RQW r q w -rra r r a -RRA r r a -RRC r r c -RRDE r r d e -rrd r r d -Rreli r r e l i -rre r r e -RRE r r e -RRG r r g -R. R. H. r r h -RRH r r h -RRKM r r k m -R.R.K. r r k -rRNA r r n a -rRNAs r r n a's -RRN r r n -RRP r r p -R&R r and r -rr r r -R. R. r r -R.R. r r -RR r r -RRR r r r -RRSR r r s r -RRS r r s -RSAF r s a f -R. S. A. r s a -RSA r s a -RSCG r s c g -RSCJ r s c j -R.S.C. r s c -RSC r s c -RSCS r s c s -RSD r s d -RSE r s e -RSF r s f -RSFSR r s f s r -RSG r s g -RSHA r s h a -rsh r s h -RSHS r s h s -RSICC r s i c c -RSID r s i d -RSI r s i -Rsis r s i's -RSI's r s i's -RSIS r s i s -RSK r s k -RSL r s l -rsly r s l y -RsmA r s m a -RSMC r s m c -RSMI r s m i -RSML r s m l -RSM r s m -RSM's r s m's -RSNO r s n o -RSN r s n -RSNZ r s n z -R. S. O. r s o -RSO r s o -RSPB r s p b -RSPCA r s p c a -RSpec r s p e c -RSPK r s p k -RSPO r s p o -R. S. P. r s p -RSP r s p -RSR r s r -rs r s -Rs r's -R. S. r s -R.S. r s -RS r s -RSSI r s s i -R. S. S. r s s -RSS r s s -RSSSF r s s s f -RSTC r s t c -RST r s t -RSu r s u -R.S.U. r s u -RSU r s u -RSU's r s u's -RSVP r s v p -RSV r s v -RSX r s x -RTAFB r t a f b -RTA r t a -RTBF r t b f -rtb r t b -RTB r t b -RTCG's r t c g's -RTC r t c -RTCs r t c's -RTD r t d -RTEC r t e c -RTEjr r t e j r -RTeOR r t e o r -RTE r t e -RTE's r t e's -RTFB r t f b -RTFM r t f m -RTF r t f -RTF's r t f's -RTFS r t f s -RTHK r t h k -RTHK's r t h k's -RTHL r t h l -RTIP r t i p -RTI r t i -RTKL r t k l -RTK r t k -RTLM r t l m -RTL r t l -RTL's r t l's -RTML r t m l -RTMP r t m p -RTM r t m -RTN r t n -RTO r t o -RTOs r t o's -RTOS r t o s -rtPA r t p a -RTP r t p -RTR r t r -rt r t -R. T. r t -R.T. r t -RT r t -rts r t s -RTS r t s -RTS's r t s's -RTTOV r t t o v -RTT r t t -RTTY r t t y -RTUK r t u k -RTU r t u -RTVC r t v c -RTVE r t v e -RTVFBiH r t v f b i h -RTV r t v -RTVV r t v v -RTW r t w -RTXC r t x c -RTX r t x -RTZ r t z -Ruao r u a o -RUC r u c -RUC's r u c's -RUFC r u f c -Rukn r u k n -Ruk r u k -RUMC r u m c -RUNX r u n x -Rupf r u p f -Rupr r u p r -rup r u p -Rup r u p -RUP r u p -R.U.R. r u r -RUR r u r -R. U. r u -R.U. r u -RU r u -RUSD r u s d -RutB r u t b -Ruu r u u -RUV r u v -Ruwa r u w a -Ruy r u y -Ruyt r u y t -RVAR r v a r -Rvat r v a t -RVC r v c -RVCT r v c t -RVD r v d -R. V. E. r v e -RVE r v e -RVGK r v g k -R.V.G. r v g -R. V. J. r v j -R.V.J. r v j -RVM r v m -RVNG r v n g -Rvo r v o -RVO r v o -RVR r v r -R. V. r v -R.V. r v -RV r v -rvs r v s -RVTD's r v t d's -RVU r v u -rwa r w a -Rwa r w a -RWA r w a -rwb r w b -RWB r w b -RWC r w c -RWD r w d -RWDSU r w d s u -RWE r w e -RWEs r w e's -RWFC r w f c -RWIS r w i s -rwjf r w j f -R.W.R.J. r w r j -R. W. r w -R.W. r w -RW r w -RWSL r w s l -R. W. S. r w s -RWS r w s -Rxa r x a -RXL r x l -RX r x -RZR r z r -rz r z -R. Z. r z -RZ r z -RZS r z s -S.A.B. s a b -S.A.D. s a d -SAIC s a i c -Sa'id s a i d -S.A.I. s a i -SAIT's s a i t's -SAKEC s a k e c -S. A. L. s a l -S.A.P.A. s a p a -S.A.P.I. s a p i -S. A. R. s a r -S.A.R. s a r -S. A. s a -S.A. s a -SA s a -S.A.S. s a s -SAS s a s -S.A.V.A. s a v a -Saxl s a x l -Sa'yo s a y o -SAZU s a z u -SBAC's s b a c's -Sbai s b a i -S.B.A. s b a -SBA s b a -SBBK s b b k -SBB s b b -SBCM s b c m -SBCMT s b c m t -sbc s b c -SBC s b c -SBCs s b c's -SBDE s b d e -SBD s b d -SBE s b e -SBF s b f -SBI s b i -SBKP s b k p -sbk s b k -SBK s b k -SBL s b l -SBML s b m l -SBMNH s b m n h -SBM s b m -SBN s b n -SBOE s b o e -SBOL s b o l -SBOP s b o p -Sborz s b o r z -SBP s b p -SBRJ s b r j -sb s b -S. B. s b -S.B. s b -SB s b -SBSE s b s e -SBS s b s -SBS's s b s's -SBTDC s b t d c -SBT s b t -SBTU s b t u -Sbu s b u -SBU s b u -SBY s b y -SBZ s b z -S.C.A. s c a -SCA s c a -SCBA s c b a -SCB s c b -SCBWI s c b w i -SCCA s c c a -SCCA's s c c a's -SCCC s c c c -SCCI s c c i -SCCP s c c p -S.C.C. s c c -SCC s c c -scr s c r -SCR s c r -SCRs s c r's -SCRS s c r s -SCRTC s c r t c -scry s c r y -sc s c -S. C. s c -S.C. s c -SC s c -S.C.S.C. s c s c -SC's s c's -SCS s c s -SCTC s c t c -SCTE s c t e -SCTO s c t o -SCTP s c t p -SCT s c t -SCTS s c t s -SCTV s c t v -SCUAA s c u a a -Scuf s c u f -SCU's s c u's -SCW s c w -Scymn s c y m n -SD&AE s d and a e -SDAP s d a p -SDAPS s d a p s -SDA s d a -SDAS s d a s -SDASS s d a s s -SDAT s d a t -SDAX s d a x -SDB s d b -SDCC s d c c -SDCG s d c g -SDC s d c -SDDI s d d i -SDDOT s d d o t -SDD s d d -SDDS s d d s -Sdei s d e i -sde s d e -Sde s d e -SDF s d f -SDG&E s d g and e -SDHA s d h a -SDHC s d h c -SDHD s d h d -SDHHD s d h h d -SDH s d h -SDI s d i -SDJ s d j -sdk s d k -SDK s d k -SDK's s d k's -SDLC s d l c -SDLP s d l p -SDLP's s d l p's -S. D. L. s d l -S.D.N.Y. s d n y -S. D. s d -S.D. s d -SDSM&T's s d s m and t's -S. D. S. s d s -S.E.C. s e c -Sejms s e j m's -sejr s e j r -Sekl s e k l -Sek s e k -SEK s e k -S. E. s e -S.E. s e -SESL s e s l -ses s e s -Ses s e's -SEs s e's -S.E.S. s e s -SES s e s -S.E.S.'s s e s's -S.E.X. s e x -Seyh s e y h -sfadb s f a d b -SFA s f a -SFA's s f a's -Sfax s f a x -S.F.B.J. s f b j -SFB s f b -SFCC s f c c -SFCH s f c h -SFC s f c -SFDR s f d r -SFD s f d -SFE s f e -SFES s f e s -SFFAS s f f a s -SFFCo s f f c o -SFFH s f f h -SFF s f f -SFG s f g -SFI s f i -S. F. L. s f l -SFL s f l -sfn s f n -SFN s f n -SFOR s f o r -SFO s f o -SFPA s f p a -SFPD s f p d -SFP s f p -SFRA s f r a -SFRJ s f r j -SFR s f r -'sf s f -sf s f -S. F. s f -S.F. s f -SF s f -SFSG s f s g -SFSR s f s r -sfs s f s -S.F.'s s f's -SFS s f s -SFSS s f s s -SFSU s f s u -SFU s f u -SFWA s f w a -SFX s f x -SFX's s f x's -SGAE s g a e -SGAP s g a p -SGA s g a -SG&A s g and a -SGA's s g a's -sgb s g b -SGB s g b -S.G.C. s g c -SGC s g c -SGD s g d -SGE s g e -S. G. F. s g f -SGF s g f -SGH s g h -Sgip s g i p -SGI s g i -SGL s g l -sgml s g m l -SGML s g m l -SGP s g p -SGRAM s g r a m -sgra s g r a -SGR s g r -S. G. s g -S.G. s g -SGSN s g s n -SGSNs s g s n's -SGS s g s -SGSY s g s y -Sgt. sergeant -sgt s g t -SGT s g t -SGU s g u -SGV s g v -SGX s g x -SHBG s h b g -SHB s h b -SHBT s h b t -SHCA s h c a -SHC s h c -SHG s h g -SHH s h h -S.H.I.E.L.D.'s s h i e l d's -S.H.I.E.L. s h i e l -SHL s h l -Shma s h m a -SHMD s h m d -SHM s h m -SHN s h n -S. H. s h -S.H. s h -S.I.D. s i d -SIF s i f -S.I.R. s i r -S. I. s i -S.I. s i -S.I.T. s i t -SJAM s j a m -SJCH s j c h -SJC s j c -SJDA s j d a -S.J.D. s j d -sjef s j e f -SJEM s j e m -S. J. J. F. s j j f -SJK s j k -S.J.L. s j l -SJL s j l -SJM s j m -Sjon s j o n -SJPCD s j p c d -SJP s j p -SJPT s j p t -SJR s j r -SJR's s j r's -S. J. s j -S.J. s j -SJ s j -SKB s k b -SKC s k c -SKD s k d -S. K. F. s k f -SKF s k f -SKG s k g -SKH s k h -SKJ s k j -SKPC s k p c -SKP s k p -SKR s k r -SKSD s k s d -S. K. s k -S.K. s k -SK s k -S. K. S. s k s -S.L.A.A.'s s l a a's -SLAF s l a f -sla s l a -SLA s l a -SLAs s l a's -SLCO s l c o -SLC s l c -SLDL s l d l -SLD s l d -S.L.E. s l e -SLF s l f -SLG s l g -SLHS s l h s -S.L.I.F.E.R. s l i f e r -SLL s l l -SLMC s l m c -S. L. M. s l m -SLM s l m -SLN s l n -SLPIM s l p i m -SLP s l p -SLRC s l r c -SLR s l r -S&L s and l -SLS&E s l s and e -SLSF s l s f -SLSK s l s k -S. L. s l -S.L. s l -SL s l -SLS s l s -SLVR s l v r -SLV s l v -S.M.A.R.T. s m a r t -S.M.A.S.H. s m a s h -SMA s m a -SMB s m b -SMe s m e -SME s m e -SME's s m e's -SMEs s m e's -SMF s m f -SMG s m g -SMHI s m h i -SMH s m h -SmI s m i -SMI s m i -SMJR s m j r -SMK s m k -SMLS s m l s -SMMT s m m t -SMN s m n -SMNS s m n s -SMP s m p -SMP's s m p's -SMPSs s m p s's -SMPTE s m p t e -SMRJ s m r j -smr s m r -SMR s m r -S&M s and m -sm s m -S. M. s m -S.M. s m -SM s m -S.M.'s s m's -SMs s m s -S.M.S. s m s -SMS s m s -SMS's s m s's -SMSU s m s u -SMTP s m t p -S.M.T. s m t -SMT s m t -SMTV s m t v -SMU s m u -SMW s m w -SMX s m x -Smyl s m y l -SMYS s m y s -SNA s n a -SNBA s n b a -SNB s n b -SNCB s n c b -SNCC s n c c -SNCF s n c f -SNC s n c -SNDC s n d c -SND s n d -SNEP s n e p -SNESjr s n e s j r -SNES s n e s -SNET s n e t -SNF s n f -SNFU s n f u -sngle s n g l e -SNG s n g -SNK s n k -SNLA s n l a -SNL s n l -SNLS s n l s -SNMCMG s n m c m g -SNMC s n m c -SNMMA s n m m a -SNMP s n m p -SNNPR s n n p r -SNNR s n n r -SNPJ s n p j -SNP s n p -SNPs s n p's -SNRI s n r i -SNR s n r -SNSD's s n s d's -SnSe s n s e -S. N. s n -S.N. s n -SN s n -SNS s n s -S.O.E. s o e -S. O. s o -S.O. s o -Sos s o's -S.O.S. s o s -SOS s o s -Sovn s o v n -Sov s o v -SOX s o x -S. O. Y. s o y -S&P 500 s and p five hundred -S.P.A.L. s p a l -SPB s p b -SPCAs s p c a's -SPCG s p c g -SPCK s p c k -S.P.C. s p c -SPC s p c -SPDI s p d i -SPD s p d -Spe s p e -SPE s p e -SPES s p e s -SPFH s p f h -SPFL s p f l -SPF s p f -SPG s p g -SPHL s p h l -SPH s p h -SPINE's s p i n e's -SPIN's s p i n's -SPIR s p i r -SPI s p i -S.P.I.T. s p i t -SPJA s p j a -SPK s p k -SPLA s p l a -SPLC s p l c -SPL s p l -SPME s p m e -SPML s p m l -SPMRL s p m r l -SPM s p m -SPNJ s p n j -SPNM s p n m -SPNN s p n n -SPN s p n -Spoa s p o a -SPOC s p o c -SPOF s p o f -SPO s p o -SPOU s p o u -SpPIn s p p i n -SPP s p p -SPP's s p p's -SPQA s p q a -SPRL s p r l -SPRM s p r m -SPR s p r -SPRU s p r u -S&P s and p -SPSA s p s a -SPSL s p s l -S. P. s p -S.P. s p -SP s p -SP's s p's -SPs s p's -SPS s p s -SPSS s p s s -SPTA s p t a -SPT s p t -SPUC s p u c -SPUP s p u p -SPU s p u -SPUs s p u's -SPV s p v -SQA s q a -SQBB s q b b -SQI s q i -sql s q l -SQL s q l -SQM s q m -sq s q -S. Q. s q -SQ s q -Sra s r a -SRA s r a -SRAs s r a's -Srba s r b a -SRBIJA s r b i j a -Srbi s r b i -SRBOC s r b o c -SRBP s r b p -SRB s r b -SRBs s r b's -SRBY s r b y -SRCC s r c c -SRCL s r c l -SRC s r c -SRC's s r c's -SRCS s r c s -S.R.E. s r e -SRE s r e -SRFC s r f c -S.R.F. s r f -SRF s r f -S.R.G. s r g -SRG s r g -SRGs s r g's -Srhir s r h i r -SRH s r h -S. R. J. s r j -SRK s r k -SRK's s r k's -SRLGs s r l g's -S.R.L. s r l -SRL s r l -SRMC s r m c -S.R.M. s r m -SRM s r m -SRMs s r m's -Srni s r n i -SRN s r n -srp s r p -SRPT s r p t -S. R. R. s r r -SRR s r r -sr s r -S. R. s r -S.R. s r -SR s r -SRS s r s -SRT s r t -SRT's s r t's -Srul s r u l -SRU s r u -SRU's s r u's -SRV s r v -srx s r x -SRY s r y -SSAA s s a a -SSAB s s a b -SSAC s s a c -SSAR s s a r -SSA s s a -SSAs s s a's -SSBSE s s b s e -S.S.B. s s b -SSB s s b -SSCCC s s c c c -S.S.C. s s c -SSC s s c -SSCV s s c v -SSDB s s d b -SSDL s s d l -SSDP s s d p -S.S.D. s s d -SSD s s d -SSDs s s d's -SSE s s e -SSFL s s f l -SSF s s f -SSGRC s s g r c -SSG s s g -SSHSA's s s h s a's -SSH s s h -SSI s s i -SSIS s s i s -SSJA s s j a -SSKI's s s k i's -SSK s s k -SSLAM s s l a m -ssl s s l -SSL s s l -SSLT s s l t -SSME s s m e -SSM s s m -SSN s s n -SSOD s s o d -SSOF s s o f -SSO s s o -SSoSV s s o s v -SSPA s s p a -SSPH s s p h -SSPR s s p r -SSP s s p -SSPX s s p x -SSQ s s q -SSRAA s s r a a -SSRF s s r f -SSRI s s r i -SSRIs s s r i's -SSRN s s r n -SSRP s s r p -SSR s s r -S&S s and s -SSSC s s s c -SSSI s s s i -SSSR s s s r -ss s s -S. s s's -Ss s's -S. S. s s -S.S. s s -SSS s s s -SSTH s s t h -SSTL s s t l -SSTR s s t r -SST s s t -Ssu s s u -SSU s s u -SSVC s s v c -SSV s s v -SSWAHS s s w a h s -SSWC s s w c -ssw s s w -SSW s s w -SSX s s x -Ssy s s y -ST&AJ s t and a j -S.T.A.L.K.E.R. s t a l k e r -S.T.A.T.U.S. s t a t u s -STB s t b -STCC s t c c -S.T.C. s t c -STC s t c -STCW s t c w -STDP s t d p -STDs s t d's -STD s t d -STF s t f -stfv s t f v -STGs s t g's -STG s t g -STIs s t i's -STIS s t i s -STi s t i -STI s t i -STK s t k -stl s t l -S.T.L. s t l -STL s t l -STMB s t m b -STMIK s t m i k -STM's s t m's -STM s t m -STNS s t n s -STN s t n -STPI s t p i -STPNS s t p n s -STPRI s t p r i -STP s t p -S.T.R.I.D.E. s t r i d e -STRV s t r v -Stryj s t r y j -STRZ s t r z -S&T s and t -STScI s t s c i -ST's s t's -STS s t s -S. T. s t -S.T. s t -STT s t t -STVL s t v l -STVS s t v s -STV s t v -STXBP s t x b p -STX s t x -S.U.C. s u c -SUDV s u d v -suo s u o -Suo s u o -S. U. s u -SUSV s u s v -Susz s u s z -SUTs s u t's -SUT s u t -Suu s u u -SUVF s u v f -SUVs s u v's -SUV s u v -SUWN s u w n -SUW s u w -Suy s u y -Svac s v a c -svar s v a r -SVA s v a -SVB s v b -SVC s v c -SVD s v d -sve s v e -Sve s v e -SVGA s v g a -SVG s v g -SVGT s v g t -SVIA s v i a -SVM's s v m's -SVMs s v m's -SVM s v m -svn s v n -svom s v o m -SVOPC s v o p c -SVO s v o -SVP s v p -SVPW s v p w -SVSCEP s v s c e p -SVSC s v s c -SVS s v s -sv s v -S. V. s v -S.V. s v -SV s v -SVTs s v t's -SVT s v t -S.V.U. s v u -SVU s v u -SVV s v v -SVW s v w -SVYASA s v y a s a -SVZ s v z -SWABC s w a b c -SWAC s w a c -S.W.A. s w a -SWC s w c -SWD s w d -S.W.E.A.T. s w e a t -Swe s w e -SWE s w e -SWF s w f -SWG s w g -SWHL s w h l -SWHS s w h s -SWH s w h -SWJN s w j n -S. W. K. s w k -SWMRS s w m r s -SWM s w m -SWNH s w n h -SWOC s w o c -S.W.O.R.D. s w o r d -SWPA s w p a -SWPL s w p l -SWP s w p -SwRI s w r i -SWR s w r -S&W s and w -SWS s w s -sw s w -S. W. s w -S.W. s w -SW s w -SWTPC s w t p c -SWT s w t -SWWTP s w w t p -SXCT s x c t -SXN s x n -SXSW s x s w -sx s x -S.X. s x -SX s x -SXY s x y -SXZ s x z -SysML s y s m l -SysRq s y s r q -Sys s y's -SYS s y s -SysV s y s v -S. Y. s y -S.Y. s y -SyT s y t -syv s y v -Syxx s y x x -SZA s z a -Szasz s z a s z -SZDSZ s z d s z -Szer s z e r -szkic s z k i c -Szklo s z k l o -Szlak s z l a k -SzMME s z m m e -Szpir s z p i r -SZSE s z s e -sz s z -S. Z. s z -SZ s z -Szu s z u -Szyk s z y k -SZZ s z z -taf t a f -Taf t a f -TAF t a f -T.A.P. t a p -T.A.S. t a s -T. A. t a -T.A. t a -T&A t and a -TAZ t a z -TBA t b a -TBCA t b c a -TBCs t b c's -TBCS t b c s -TBD t b d -TBE t b e -TBF t b f -TBHS t b h s -TBI t b i -TBK t b k -TBL t b l -TBMA t b m a -TBMM t b m m -TBMs t b m's -T.B.M. t b m -TBM t b m -TBNJ t b n j -TBN t b n -TBP t b p -TBSC t b s c -TBS t b s -T. B. t b -T.B. t b -TB t b -TBTG t b t g -TBT t b t -TBV t b v -TBWA t b w a -TBX t b x -TCAR t c a r -TCA t c a -TCC t c c -TCDB t c d b -TCDC t c d c -TCDD t c d d -TCDOG t c d o g -TCDSB t c d s b -TCEB t c e b -T.C.E. t c e -TCE t c e -TCFAQ t c f a q -TCF t c f -TCGA t c g a -TCG t c g -TCHC t c h c -TCHRD t c h r d -TCH t c h -TCiAP t c i a p -TCI t c i -TCKs t c k's -TCLP t c l p -TCL t c l -TCM t c m -TCO t c o -TCPL t c p l -TCPMP t c p m p -TCP's t c p's -TCP t c p -TCR t c r -T. C. S. t c s -TCS t c s -tc t c -T. C. t c -T.C. t c -TC t c -TCT t c t -TCU's t c u's -TCUs t c u's -tcu t c u -TCU t c u -TCV t c v -TCWC t c w c -TCWP t c w p -TCW t c w -TDA t d a -tdb t d b -TDCC t d c c -TDCi t d c i -TDCJ t d c j -TDC's t d c's -TDC t d c -TDD t d d -TDE t d e -TDF t d f -TDH t d h -TdIF t d i f -TDI t d i -TDK t d k -TDLAS t d l a s -TDMA t d m a -TDME t d m e -TDM t d m -TDO t d o -TDP t d p -TDRS t d r s -TDR t d r -TDSB's t d s b's -TD's t d's -TDs t d's -T. D. t d -T.D. t d -TDT t d t -Teatr t e a t r -TEBD t e b d -T. E. t e -T.E. t e -TEVT t e v t -TFAP t f a p -TFAS t f a s -TFA t f a -TFC t f c -TFCU t f c u -TFF t f f -TFG t f g -T.F.H. t f h -TFH t f h -TFIH t f i h -TFIID t f i i d -TFK t f k -TFL t f l -TFM t f m -TFN t f n -TFO t f o -TFP t f p -TFR t f r -TFSA t f s a -TFSI t f s i -TFSP t f s p -TFS t f s -T&F t and f -TFTC t f t c -tf t f -T. F. t f -T.F. t f -TF t f -TFTR t f t r -TFT t f t -TFVC t f v c -TFWA t f w a -TFW t f w -TGA t g a -T. G. C. t g c -TGC t g c -tge t g e -T.G.E. t g e -TGE t g e -tgf t g f -TGF t g f -TGG t g g -TGIF t g i f -TGL t g l -TGM's t g m's -TGM t g m -TGP t g p -TGS t g s -tg t g -T. G. t g -T.G. t g -TG t g -TGVs t g v's -tgv t g v -TGV t g v -THB t h b -THCF t h c f -THC t h c -T.H.E.M. t h e m -T.H.F. t h f -THF t h f -THG t h g -THHK t h h k -T.H.I.N.K. B.I.G. t h i n k b i g -THLL t h l l -THL t h l -THMP t h m p -THM t h m -THNN t h n n -Thok t h o k -T.H.P. t h p -THQ's t h q's -THQ t h q -THR's t h r's -THR t h r -ThSe t h s e -THSR t h s r -T. H. S. t h s -T. H. t h -T.H. t h -T.H.U.N.D.E.R. t h u n d e r -THW t h w -THX t h x -TIAA t i a a -T. I. A. t i a -TIFR t i f r -TIF t i f -T.I.'s t i's -T.I. t i -Tiu t i u -tiv t i v -Tiv t i v -TIYM t i y m -Tiy's t i y's -TJFR t j f r -T.J.F. t j f -T. J. J. t j j -TJJ t j j -TJP t j p -TJRC t j r c -T. J. S. t j s -T. J. t j -T.J. t j -TJ t j -t'ju t j u -TJX t j x -tjz t j z -TKAG t k a g -TKA's t k a's -TKA t k a -T. K. B. t k b -TKD t k d -TKE t k e -TKI t k i -tko t k o -Tko t k o -TKO t k o -TKP t k p -TK's t k's -TKS t k s -tk t k -T. K. t k -T.K. t k -TK t k -TKT t k t -Tlas t l a's -TLA t l a -TLC's t l c's -TLC t l c -TlCu t l c u -TLDs t l d's -TLDS t l d s -TLD t l d -TLE t l e -TLF t l f -TLG t l g -TLH t l h -tli t l i -TLI t l i -TLM t l m -TLN t l n -tlp t l p -TLP t l p -TLRs t l r's -TLR t l r -TLs t l's -TLS t l s -TLTB t l t b -tl t l -T. L. t l -T.L. t l -TL t l -Tluk t l u k -TLU's t l u's -Tluszcz t l u s z c z -TLV t l v -TMA t m a -TMBG t m b g -TMB t m b -tmc t m c -TMC t m c -TMDL t m d l -TMD t m d -TMEM t m e m -TMF t m f -TMGs t m g's -TMG t m g -TMI t m i -TMJ t m j -T. M. M. t m m -TMMTX t m m t x -TMNT t m n t -TMOK t m o k -tmos t m o s -TMO's t m o's -TMOS t m o s -TMO t m o -TMPRSS t m p r s s -TMP t m p -tmRNA t m r n a -TMRS t m r s -TMR t m r -TMSI t m s i -TMSs t m s's -T.M.s t m's -TM's t m's -TMS t m s -tm t m -T. M. t m -T.M. t m -TM t m -T.M.T. t m t -TMT t m t -TMU t m u -TMX t m x -TMZ t m z -TNA's t n a's -TNAS t n a s -tna t n a -TNA t n a -tnbc t n b c -TNCA t n c a -TNCC t n c c -TNCDSB t n c d s b -TNC t n c -TNFA t n f a -TNFRSF t n f r s f -TNFSF t n f s f -TNF t n f -TNGHT t n g h t -TNG t n g -TNIP t n i p -TNI t n i -TNL t n l -TNMM t n m m -TNMS t n m s -TNM t n m -TNNC t n n c -TNN t n n -TNO t n o -TNPL t n p l -TNP t n p -TNQ t n q -TNRIS t n r i s -TNRP t n r p -TNR t n r -TNSDL t n s d l -TNSTC t n s t c -TNS t n s -TNTAs t n t a's -tn t n -T. N. t n -T.N. t n -TN t n -TNT's t n t's -TNT t n t -T.O.'s t o's -T. O. t o -T.O. t o -TOTP t o t p -tou t o u -Tou t o u -ToU t o u -TOU t o u -TPAC t p a c -TPAO t p a o -tPA t p a -TPA t p a -TPB t p b -TPC t p c -TPEC t p e c -TPF t p f -TPG t p g -TPH t p h -TPI t p i -TPLF t p l f -TPMS t p m s -TPM t p m -TPO t p o -TPP t p p -tpr t p r -TPR t p r -TPS t p s -tp t p -T. P. t p -T.P. t p -TP t p -TPU t p u -TPVs t p v's -TPWS t p w s -TPW t p w -TQR t q r -TQS t q s -T.Q. t q -TQ t q -tra t r a -Tra t r a -TRA t r a -TRAXX t r a x x -Trbic t r b i c -T. R. B. t r b -TRB t r b -Trcek t r c e k -TRC t r c -TRD t r d -TRFA t r f a -TRF t r f -TRG t r g -TRHS t r h s -TRH t r h -T.R.I.B.E. t r i b e -TRIZ t r i z -TrkA t r k a -TRL t r l -Trmcic t r m c i c -TRMM t r m m -T.R.M.P.A. t r m p a -T. R. M. t r m -tRNA t r n a -TRNC t r n c -TRPC t r p c -TRPM t r p m -TRPP t r p p -TRP's t r p's -TRPs t r p's -TRP t r p -TRPV t r p v -TRPY t r p y -TRSC t r s c -TRSI t r s i -trs t r s -T.R.S. t r s -TRS t r s -TRSV t r s v -tr t r -T. R. t r -T.R. t r -TR t r -TRT t r t -T.R.U. t r u -truTV t r u t v -TruTV t r u t v -Trve t r v e -TRVL t r v l -TRV t r v -TRW t r w -TRX t r x -TRZ t r z -Tsa t s a -TSA t s a -TSB t s b -TSCC t s c c -TSC t s c -TSF t s f -TSG t s g -TSH t s h -TSI t s i -tsit t s i t -Tsiv t s i v -TSJDF t s j d f -TSLA t s l a -TSLC t s l c -TSL t s l -TSN t s n -T.S.O.L. t s o l -tso t s o -Tso t s o -T.S.O. t s o -TSO t s o -TSP t s p -TSRA t s r a -TSRTC t s r t c -tsr t s r -TSR t s r -TSSA t s s a -TSS t s s -TSTA t s t a -TSTC t s t c -Ts t's -T. S. t s -T.S. t s -TS t s -TST t s t -tsus t s u s -Tsvi t s v i -TSV t s v -TSW t s w -TSX t s x -TTAB t t a b -Tta t t a -TTA t t a -TTBB t t b b -TTCP t t c p -TTC's t t c's -TTC t t c -TTDC t t d c -TTD t t d -tteok t t e o k -tte t t e -TTFF t t f f -TTHS t t h s -TTH t t h -TTI t t i -TTKG t t k g -TTKST t t k s t -TTL t t l -T.T.N. t t n -ttp t t p -TTP t t p -TTR t t r -TTS t t s -T&T t and t -T. T. t t -T.T. t t -TT t t -TTU t t u -TTV t t v -TTW t t w -TUAW t u a w -TUBB t u b b -TUDCA t u d c a -TUHUS t u h u s -TUKP's t u k p's -T'uqu t u q u -TUSCC t u s c c -TUSC t u s c -T.U. t u -TU t u -Tuzk t u z k -tvaan t v a a n -Tvam t v a m -TVARK t v a r k -tva t v a -TVA t v a -TVB's t v b's -TVB t v b -TVCM t v c m -TVC t v c -TVD t v d -TVETs t v e t's -TVE t v e -TVF t v f -TVGN t v g n -TVGOS t v g o s -TVii t v i i -TVIn t v i n -TVi t v i -TVI t v i -TVL t v l -TVMK t v m k -TVM t v m -TVN t v n -TVNZ's t v n z's -TVNZ t v n z -tvo t v o -Tvo t v o -TVO t v o -TVP t v p -TVRi t v r i -tvr t v r -TVR t v r -TVSpy t v s p y -tvs t v s -TV's t v's -TVs t v's -TVS t v s -tVTA t v t a -TVT t v t -tv t v -T. V. t v -T.V. t v -TV t v -TVXQ's t v x q's -TVXQ t v x q -TVX t v x -TWAP t w a p -TWA's t w a's -Twa t w a -TWA t w a -T. W. B. t w b -TWCS t w c s -TWC t w c -TWF t w f -Twi t w i -TWI t w i -TWK t w k -T.W.P. t w p -TWP t w p -TWR t w r -TWTs t w t's -TWT t w t -T. W. t w -T.W. t w -TW t w -T.W.U. t w u -TWU t w u -TXC t x c -TxDOT t x d o t -TXII t x i i -TXJS t x j s -TXK t x k -TXMM t x m m -TXNDC t x n d c -TXP t x p -TXTC t x t c -TXT t x t -T.X. t x -TX t x -TXU t x u -tya t y a -TYC t y c -TYIB t y i b -TYIN t y i n -Tyk t y k -Tza'ar t z a a r -Tze t z e -TZL t z l -Tzrif t z r i f -Tzuh t z u h -TZUM t z u m -Tzvi t z v i -UAAP's u a a p's -UAAP u a a p -UAB's u a b's -UAB u a b -UAC u a c -UADY u a d y -UAE's u a e's -U.A.E. u a e -UAE u a e -UAF u a f -UAHC u a h c -UAIC u a i c -UAI u a i -UALR u a l r -UAL u a l -UALVP u a l v p -UAM u a m -UANL u a n l -UAN u a n -UAOC u a o c -UAP u a p -UAR's u a r's -UARS u a r s -UAR u a r -UASLP u a s l p -UASL u a s l -Ua's u a's -UAs u a's -UAS u a s -uat u a t -U.A. u a -UA u a -UAVs u a v's -UAV u a v -U.A.W. u a w -UAW u a w -Uba u b a -UBA u b a -UBBC u b b c -UBB u b b -UbcM u b c m -UBC's u b c's -UBC u b c -UBD u b d -Ube u b e -UBE u b e -ubi u b i -Ubi u b i -U.B.I. u b i -UBI u b i -UBM's u b m's -UBM u b m -UBOS u b o s -UBO u b o -UBPR u b p r -UBP u b p -UBRE u b r e -UBRS u b r s -UB's u b's -UBS u b s -UB u b -UBU u b u -UBV u b v -UBX u b x -UCAC u c a c -Ucar u c a r -UCAR u c a r -Uca u c a -UCA u c a -UCAV u c a v -UCBH u c b h -UCBTLA u c b t l a -UCB u c b -UCCE u c c e -UCCJ u c c j -UCCS u c c s -UCC u c c -UCDA u c d a -UCDP u c d p -UCD u c d -UCF's u c f's -UCF u c f -UCG u c g -UCIEP u c i e p -UCI's u c i's -UCI u c i -UCLA's u c l a's -UCLA u c l a -UCLH u c l h -UCL u c l -Ucmak u c m a k -UCMJ u c m j -UCMMA u c m m a -UCMP u c m p -UCM u c m -Ucn u c n -UCOS u c o s -UCO u c o -UCP u c p -UCREF u c r e f -UCR u c r -UCSB u c s b -UCSC u c s c -UCSD's u c s d's -UCSD u c s d -UCSF u c s f -UCSN u c s n -uCs u c's -UCS u c s -UCTI u c t i -UCT u c t -Uc u c -U.C. u c -UC u c -Ucuncu u c u n c u -UCU u c u -UCVTS u c v t s -UCW u c w -Uczta u c z t a -UDAR u d a r -UDA's u d a's -Uda u d a -UDA u d a -UDC u d c -UDF u d f -UDG u d g -UDHR u d h r -UDICHI u d i c h i -Udit u d i t -UDK u d k -UDN u d n -Udny u d n y -UDP u d p -UDRP u d r p -UDR u d r -UDS u d s -UDTs u d t's -UDT u d t -ud u d -Ud u d -U. D. u d -UD u d -udu u d u -Udu u d u -UDYCO u d y c o -UEA u e a -UEBT u e b t -UEC u e c -Ueda's u e d a's -Uéda u e acute d a -UEFA's u e f a's -UEFA u e f a -UEFI u e f i -UEFS u e f s -UEF u e f -UEGCL u e g c l -UEI u e i -UET u e t -U. E. u e -UE u e -UFABC u f a b c -UFA's u f a's -UFCO u f c o -UFC's u f c's -UFC u f c -UFCW u f c w -UFD u f d -UFE u f e -UFF u f f -UFIA u f i a -UFIP u f i p -UFI u f i -UFJ u f j -UFL's u f l's -UFL u f l -UFM u f m -UFOFU u f o f u -UFORE u f o r e -UFO's u f o's -UFOs u f o's -Ufot u f o t -ufo u f o -Ufo u f o -UFO u f o -UFPE u f p e -UFPLS u f p l s -UFPR u f p r -UFRGS u f r g s -UFRJ u f r j -UFSB u f s b -UFSM u f s m -UF's u f's -UFS u f s -UFTP u f t p -UFT u f t -U. F. u f -UF u f -UFU u f u -UFW u f w -Uga u g a -UGA u g a -UGCCWA u g c c w a -UGC u g c -Uge u g e -UGK u g k -UGME u g m e -Ugni u g n i -UGP u g p -Ugra u g r a -Ugrszke u g r s z k e -UGS u g s -UGT u g t -Ug u g -UG u g -UHD u h d -UHF u h f -Uhha u h h a -UHMW u h m w -uhn u h n -UHOA u h o a -UHPT u h p t -UHP u h p -UHRA u h r a -UHRF u h r f -UHSAA u h s a a -UHTCs u h t c's -UHTI u h t i -Uhud u h u d -U.H. u h -uhur u h u r -UIAA u i a a -Uiam u i a m -UIA u i a -UIAW u i a w -Uibh u i b h -Uible u i b l e -UIC u i c -UID u i d -UIFL u i f l -Uig u i g -UIL u i l -UIMC u i m c -UIM u i m -UIN u i n -UIP u i p -uisae u i s a e -UISD u i s d -UiS u i s -UI's u i's -UIS u i s -UiTM u i t m -uit u i t -Uit u i t -UIT u i t -UI u i -UIV u i v -Uiy u i y -UJA u j a -UJC u j c -Ujed u j e d -UJEP u j e p -Ujsag u j s a g -UJS u j s -uj u j -U.J. u j -UJ u j -Uka u k a -UKA u k a -UKCC u k c c -UKC u k c -Ukhra u k h r a -Ukic u k i c -UKIP's u k i p's -Uki u k i -UKM u k m -UKNC u k n c -Ukoh u k o h -Uko u k o -UKPC u k p c -UKRDA's u k r d a's -UKRDA u k r d a -ukr u k r -Ukr u k r -UKR u k r -UK's u k's -UKTV's u k t v's -UKTV u k t v -Ukui u k u i -u'k u k -uk u k -Uk u k -U.K. u k -UK u k -UKUP u k u p -UKVRN u k v r n -UKW u k w -Ukwu u k w u -ULC u l c -ULDB u l d b -ULEB u l e b -ULEV u l e v -ULFA u l f a -Ulic u l i c -Ull u l l -Ulms u l m's -UL's u l's -ULS u l s -UlSU u l s u -Uluj u l u j -U.L. u l -UL u l -ulus u l u s -Ulus u l u's -ULV u l v -Ulwa u l w a -Ulwe u l w e -UMBC u m b c -Umbr u m b r -UMCC u m c c -UMC u m c -UMD u m d -umelcu u m e l c u -UMe u m e -UMF u m f -UMG u m g -UMHB u m h b -UMH u m h -UMKC's u m k c's -UMKC u m k c -UMK u m k -Umla u m l a -UML u m l -Umme u m m e -UMMSM u m m s m -UMNO's u m n o's -Umno u m n o -UMNO u m n o -UMN u m n -UMO u m o -UMPP u m p p -UMP's u m p's -Umri u m r i -UM's u m's -UMTS u m t s -UMTV u m t v -UMUC u m u c -U. M. u m -U.M. u m -UMWA's u m w a's -UMWA u m w a -UMW's u m w's -UNAB u n a b -UNAFF u n a f f -UNAH u n a h -UNAMIR u n a m i r -UNAPACK's u n a p a c k's -UNB u n b -UNCAC u n c a c -UNCAF u n c a f -UNCG's u n c g's -UNCG u n c g -UNCHE u n c h e -UNCHS u n c h s -UNCMAC u n c m a c -UNCRO u n c r o -UNC's u n c's -UNCTAD u n c t a d -UNC u n c -UNCW u n c w -UNFCCC u n f c c c -UNFICYP u n f i c y p -UNFPA u n f p a -UNFP u n f p -UNF u n f -UNFWP u n f w p -UNGC u n g c -UNHCR u n h c r -UNHRC's u n h r c's -UNH u n h -UNJLC u n j l c -Unli u n l i -UNL u n l -Unlu u n l u -UNLV u n l v -UNMCK u n m c k -UNMC u n m c -UNMEE's u n m e e's -UNMEE u n m e e -UNMIH u n m i h -UNMIK u n m i k -UNMIL u n m i l -UNMOVIC u n m o v i c -unm u n m -UNM u n m -UNNC u n n c -unnd u n n d -UNODC u n o d c -UNOMIG u n o m i g -UNPA u n p a -UNPD u n p d -UNPO u n p o -UNP u n p -UNRA u n r a -UNRSGN u n r s g n -UNRWA u n r w a -UNSCOM u n s c o m -UNSCR u n s c r -UNSC's u n s c's -UNSC u n s c -UNSh u n s h -Unst u n s t -Uns u n's -U.N.'s u n's -UNSW u n s w -UNTSO u n t s o -UNTS u n t s -Unt u n t -UNT u n t -U.N. u n -UNU u n u -UNV u n v -UNWE u n w e -UNWTO u n w t o -Unz u n z -UOCl u o c l -UODA u o d a -UofM u o f m -UOHI u o h i -Uoho u o h o -UOJCA u o j c a -UOL u o l -UOMINI u o m i n i -UoMs u o m's -UOP u o p -uORF u o r f -UOR u o r -UO's u o's -UO u o -UoW u o w -UPABA u p a b a -UPBs u p b's -UPCI u p c i -UPC u p c -UPDRS u p d r s -UPDS u p d s -Upd u p d -UPD u p d -UPEC u p e c -UPFA u p f a -UPF's u p f's -Upf u p f -UPF u p f -UPGMA u p g m a -UPHSD u p h s d -UPIN u p i n -Upir u p i r -UPI's u p i's -UPI u p i -UPLA u p l a -UPLB u p l b -UPL u p l -UPMC u p m c -UPMSS u p m s s -UPND u p n d -UPNE u p n e -UPnP u p n p -UPN u p n -UPOV u p o v -UPPP u p p p -Uppu u p p u -UPRR u p r r -UPR u p r -UPSC u p s c -U.P.S.E.B. u p s e b -UPSIDC u p s i d c -UPSID u p s i d -UPSI u p s i -UPSMF u p s m f -UPTI u p t i -UPTU u p t u -U.P. u p -UPW u p w -UPX u p x -UPyD u p y d -UQAIB u q a i b -UQAM's u q a m's -UQC u q c -U.Q.P. u q p -UQP u q p -uq u q -UQ u q -URAA u r a a -URAC u r a c -URAP u r a p -URBACT u r b a c t -URBED u r b e d -Urbz u r b z -URCL u r c l -URCSA u r c s a -URCs u r c's -URC u r c -Urdd u r d d -Urei u r e i -urf u r f -Urla u r l a -URLhttp u r l h t t p -URLs u r l's -URL u r l -URNU u r n u -UROC u r o c -URRACA u r r a c a -URSB u r s b -URSEC u r s e c -URSS u r s s -URW's u r w's -URW u r w -URZ u r z -USAACE u s a a c e -USAAC u s a a c -U.S.A.A.F. u s a a f -USAAF u s a a f -USAB u s a b -USACE u s a c e -USAC u s a c -USAFE u s a f e -USAF u s a f -USAHA u s a h a -USAID's u s a i d's -USAID u s a i d -Usal u s a l -usan u s a n -Usan u s a n -USAN u s a n -usao u s a o -USAO u s a o -USAPA u s a p a -USARPS u s a r p s -USASA u s a s a -USA's u s a's -usata u s a t a -USATF u s a t f -USAT u s a t -usa u s a -.USA u s a -U.S.A. u s a -U.S.A u s a -USA u s a -USAya u s a y a -USBA u s b a -USBC's u s b c's -USBC u s b c -USBs u s b's -USB u s b -USBWA u s b w a -USCCB u s c c b -USCF u s c f -USCGAUX u s c g a u x -USCGC u s c g c -USCG u s c g -USCHS u s c h s -Uscie u s c i e -USCIRF u s c i r f -USCIS u s c i s -USCITA u s c i t a -USCOB u s c o b -USCRP u s c r p -U.S.C.'s u s c's -USC's u s c's -USCT u s c t -U.S.C. u s c -USC u s c -USDA's u s d a's -U.S.D.A. u s d a -USDA u s d a -USDoE u s d o e -USDOJ u s d o j -USDP u s d p -USDTV u s d t v -usd u s d -USD u s d -USEER u s e e r -USENIX u s e n i x -USFbA u s f b a -USFE u s f e -USFF u s f f -USFLPA u s f l p a -USFL u s f l -USFS u s f s -USF u s f -USF&WS u s f and w s -USFWS u s f w s -USFW u s f w -USGA u s g a -U.S.G.S. u s g s -USGS u s g s -U.S. H.R. u s h r -UShs u s h's -Usia u s i a -USIA u s i a -usih u s i h -USIP's u s i p's -USIP u s i p -USISL u s i s l -USIS u s i s -USIU u s i u -USJA u s j a -USJ u s j -USLES u s l e s -USLHE u s l h e -USLMRA u s l m r a -USLTA u s l t a -USL u s l -Uslu u s l u -Usmar u s m a r -USMA u s m a -USMC M u s m c -USMCR u s m c r -USMC's u s m c's -USMC u s m c -USMLE u s m l e -USML u s m l -USMMA u s m m a -USM u s m -Usna u s n a -USNA u s n a -USNG u s n g -Usnic u s n i c -USNM u s n m -USNO u s n o -USNR u s n r -USNSCS u s n s c s -USNS u s n s -USNTDP u s n t d p -U.S.N. u s n -USN u s n -USOC's u s o c's -USOC u s o c -Usoi u s o i -Usos u s o's -Uso u s o -USO u s o -USPHL u s p h l -USPO u s p o -USPPD u s p p d -U.S.P.Q. u s p q -USPSA u s p s a -USPs u s p's -USPS u s p s -USPTA u s p t a -USPTO u s p t o -U.S.P. u s p -USP u s p -USRA u s r a -USRC u s r c -USSA u s s a -USSBA u s s b a -USSB u s s b -USSD u s s d -USSF's u s s f's -USSF u s s f -USSR's u s s r's -USSR u s s r -U.S.S.R u s s r -U.S.'s u s's -US's u s's -U.S.S. u s s -USS u s s -Usti u s t i -Ustka u s t k a -USTR's u s t r's -USTR u s t r -UST's u s t's -Usui u s u i -usum u s u m -U. S. u s -U.S. u s -Usut u s u t -USVC u s v c -Usvit u s v i t -USVI u s v i -U.S.V. u s v -USWA u s w a -USWNT's u s w n t's -USWNT u s w n t -U.S.W. u s w -USW u s w -Uta's u t a's -uta u t a -Uta u t a -UTA u t a -UTB u t b -Utca u t c a -UTC u t c -Utd's u t d's -Utd u t d -utea u t e a -UTEP u t e p -Utes u t e's -UTET u t e t -UTF u t f -Uth u t h -uti u t i -Uti u t i -UTJ u t j -UTMB u t m b -UTMF u t m f -UTM u t m -Utne u t n e -Utnur u t n u r -Uto u t o -UTO u t o -UTPB u t p b -UTP u t p -utrci u t r c i -UTRGV u t r g v -UTR u t r -utsav u t s a v -Utsav u t s a v -UTSC u t s c -UTSI u t s i -UT's u t's -UTS u t s -Utsu u t s u -UTTR u t t r -U.T. u t -Utu u t u -Utva u t v a -UTVA u t v a -UTV u t v -UUA u u a -UUCP u u c p -Uuh u u h -UUKKY u u k k y -Uul u u l -Uulu u u l u -UUP u u p -Uusi u u s i -UUs u u's -Uuto u u t o -UUT u u t -Uvac u v a c -UVB u v b -UVC u v c -Uvea u v e a -UVF u v f -UVIMB u v i m b -UVI u v i -uvnitr u v n i t r -Uvo u v o -UVT u v t -UVU u v u -U.V. u v -UV u v -UWAP u w a p -Uwasa u w a s a -UWASA u w a s a -U.W.A. u w a -UWA u w a -UWB u w b -UWC u w c -UWFi u w f i -UWF u w f -UWG u w g -UWH u w h -UWI u w i -UWM's u w m's -UWP u w p -UWRF u w r f -UWSP's u w s p's -UWSU u w s u -UW's u w's -UWS u w s -UW u w -UWW u w w -VASP v a s p -VA's v a's -V. A. v a -V.A. v a -VA v a -V&A v and a -Vav v a v -VAV v a v -Vay v a y -VBA v b a -VBBS v b b s -VBE v b e -VBI v b i -VBM v b m -VBR v b r -vb v b -V. B. v b -V.B. v b -VB v b -VCA's v c a's -VCA v c a -VCCS v c c s -VCDs v c d's -VCD v c d -VCE v c e -VCH v c h -VCIT v c i t -VCI v c i -VCJ v c j -V. C. M. v c m -VCO v c o -VCP v c p -VCQ v c q -VCRs v c r's -VCR v c r -VCSELs v c s e l's -VCSEL v c s e l -VCs v c's -VCS v c s -VCTs v c t's -VCT v c t -VCU v c u -vc v c -V. C. v c -V.C. v c -VC v c -VCXIV v c x i v -VCY v c y -VDAC v d a c -vda v d a -Vda v d a -VDCs v d c's -VDC v d c -vdiq v d i q -VDJ v d j -VDL's v d l's -VDL v d l -vdm v d m -VDM v d m -VDP v d p -VDR v d r -VDSL v d s l -VDUP v d u p -VDU v d u -V. D. v d -V.D. v d -VD v d -VDV v d v -V. D. W. v d w -vez v e z -Vez v e z -VEZ v e z -V.F.A. v f a -VFA v f a -VFC v f c -VFDS v f d s -V.F.D. v f d -VFD v f d -VFIL v f i l -VFLI v f l i -VFL's v f l's -VFL v f l -VFMC v f m c -VFM v f m -vfp v f p -vfr v f r -VFR v f r -VF's v f's -VFS v f s -VFTS v f t s -VFTX v f t x -VFU v f u -V. F. v f -V.F. v f -VF v f -VFW v f w -VFX v f x -VGA v g a -V'Ger v g e r -VGIK v g i k -VGKO v g k o -VGMA v g m a -vgmdb v g m d b -VGMDB v g m d b -VGM v g m -VGo v g o -VGP v g p -VGSCs v g s c's -VGSoM v g s o m -VGTU v g t u -VGU v g u -V. G. v g -V.G. v g -VG v g -VGV v g v -VGX v g x -VHB v h b -VHC v h c -VHD v h d -VHF v h f -VHL v h l -VHP v h p -VHR v h r -VHSL v h s l -VHS v h s -V. H. v h -VH v h -V.I.C.'s v i c's -Vict v i c t -VICT v i c t -viita v i i t a -vijf v i j f -vij v i j -Vij v i j -V.I.P.s v i p's -VIPs v i p's -V.I.P. v i p -VIP v i p -V. I. v i -V.I. v i -V. J. J. v j j -VJL v j l -VJs v j's -VJTF v j t f -V. J. v j -V.J. v j -VJ v j -VKG v k g -VKIBC's v k i b c's -V. K. N. v k n -VKOS v k o s -VKS v k s -V. K. v k -V.K. v k -VK v k -Vlah v l a h -VLA v l a -VLBA v l b a -VLBI v l b i -VLB v l b -VLCCs v l c c's -VLCS v l c s -VLC v l c -VLDB v l d b -VLDL v l d l -vlei v l e i -VLE v l e -VLEX v l e x -VLF v l f -Vlijt v l i j t -VLN v l n -VLQ v l q -VLR v l r -VLSI v l s i -VLS v l s -VLT v l t -V. L. v l -V.L. v l -VL v l -VMAs v m a's -VMAT v m a t -VMA v m a -VMCAS v m c a s -VMCM v m c m -VME v m e -VMFM v m f m -VMF v m f -VMG v m g -VMI's v m i's -VMI v m i -VMK v m k -VMM v m m -VMPS v m p s -VMS v m s -VMT v m t -vm v m -V. M. v m -V.M. v m -VM v m -VNAF v n a f -VNAV v n a v -VNCH v n c h -VNC v n c -vner v n e r -V.N.E. v n e -VNI v n i -VNO v n o -VNPT v n p t -VNQDD v n q d d -VNR v n r -VNSA v n s a -VNS v n s -VNTH v n t h -VNTR v n t r -VNU v n u -V. N. v n -V.N. v n -VN v n -V.O. v o -VO v o -Vov v o v -VOX's v o x's -Voz v o z -VPAL v p a l -VPD v p d -VPH v p h -vpis v p i s -V. P. I. v p i -VPI v p i -VPLS v p l s -VPL v p l -VPM v p m -VPNs v p n's -VPN v p n -VPO's v p o's -VPP v p p -VPRO's v p r o's -VPRO v p r o -VPS v p s -vpu v p u -V. P. v p -V.P. v p -VP v p -VQT v q t -vq v q -VQ v q -vrak v r a k -VRA v r a -Vrba v r b a -Vrbuv v r b u v -VRD v r d -Vrej v r e j -vrem v r e m -VRE v r e -VRF v r f -Vrin v r i n -VRI v r i -vrj v r j -VRLA v r l a -vrn v r n -vroee v r o e e -vrou v r o u -vrouw v r o u w -Vrouw v r o u w -VRO v r o -VRR v r r -Vrsac v r s a c -VRSA v r s a -VR's v r's -VRS v r s -Vrtis v r t i's -VRT's v r t's -VRT v r t -V&R v and r -vr v r -V. R. v r -V.R. v r -VR v r -VRV v r v -VSANs v s a n's -VSAN v s a n -VSAP's v s a p's -VSATs v s a t's -VSB's v s b's -VSB v s b -VSCP v s c p -VSC v s c -VSD v s d -Vsekh v s e k h -vse v s e -VSE v s e -VSFV v s f v -VSG v s g -VSI v s i -VSM v s m -VSNL v s n l -vso v s o -VSO v s o -VSPs v s p's -VSP v s p -VSS v s s -VSTM v s t m -VST v s t -VSU v s u -vs. versus -_vs._ versus -VSV v s v -VSW v s w -VSX v s x -vsyo v s y o -VSZ v s z -VTA v t a -VTB v t b -VTCO v t c o -VTD v t d -VTEC v t e c -VTE v t eL -Vtic v t i c -VTi v t i -VTL v t l -VTM v t m -VTR v t r -VTSK v t s k -V&T's v and t's -VTS v t s -VTU v t u -V&T v and t -VTVL v t v l -V.T. v t -VT v t -VTV v t v -Vuur v u u r -VUU v u u -V.V.B. v v b -VVCS v v c s -VVD v v d -VVIPs v v i p's -VVI v v i -VVMC v v m c -VVO v v o -VVS v v s -VVT v v t -V. V. v v -V.V. v v -VV v v -VWM v w m -VWR v w r -VW's v w's -VWs v w's -VWS v w s -VW v w -VXL v x l -VXR v x r -VX v x -Vyg v y g -vyr v y r -vy v y -VY v y -vz v z -V. Z. v z -WAAAAAGH w a a a a a g h -WAAAF w a a a f -WAAC w a a c -WAAF w a a f -WAAI w a a i -WAAV w a a v -Waay w a a y -WABB w a b b -WABC w a b c -WABD w a b d -WABG's w a b g's -WABG w a b g -W. A. C. w a c -W.A.C. w a c -Wa'il w a i l -WAIO w a i o -W.A.J. w a j -Wakf w a k f -W.A.K.O. w a k o -wa'l w a l -WANFL w a n f l -WAOK w a o k -waqf w a q f -waqt w a q t -Waqt w a q t -WAQZ w a q z -WARH w a r h -W.A.R. w a r -WASC w a s c -Wasl w a s l -WASL w a s l -W. A. S. w a s -W.A.S. w a s -Watfa w a t f a -WATL w a t l -WATW w a t w -wau w a u -Wau w a u -WAU w a u -WAVF w a v f -WAVM w a v m -W. A. w a -W.A. w a -WA w a -WAWLI w a w l i -waw w a w -WAWZ w a w z -WAYH w a y h -waza w a z a -Waza w a z a -WBAL's w b a l's -WBAM w b a m -WBA w b a -WBBL w b b l -WBBM w b b m -WBBP w b b p -WBCA w b c a -WBCN w b c n -W.B.C. w b c -WBC w b c -WBCY w b c y -WBEV w b e v -WBEZ w b e z -WBFJ w b f j -WBFO w b f o -WBF w b f -WBG w b g -WBHY w b h y -WBIC w b i c -WBIL w b i l -WBJEE w b j e e -WBKO w b k o -WBK w b k -WBLQ w b l q -WBME w b m e -WBNI w b n i -WBNX w b n x -WBON w b o n -WBO w b o -WBQC w b q c -WBRC w b r c -WBRE w b r e -WBRO w b r o -WBRZ w b r z -WBs w b's -WBS w b s -WBTI w b t i -WBT's w b t's -WBT w b t -WBTW w b t w -WBTZ w b t z -WBUR w b u r -WBVE w b v e -WBV w b v -W. B. w b -W.B. w b -WB w b -WBXH w b x h -WBZB w b z b -WBZ w b z -WCAA w c a a -WCAG w c a g -WCAU w c a u -WCA w c a -WCBA w c b a -WCBN w c b n -W&CBR w and c b r -WCBR w c b r -WCBS w c b s -WCB w c b -WCCES w c c e s -WCCG w c c g -WCCO w c c o -WCC's w c c's -WCC w c c -WCDA w c d a -WCEO w c e o -WCFL w c f l -WCF w c f -WCG w c g -WCHA w c h a -WCHL's w c h l's -WCHL w c h l -WCHS w c h s -WCHV w c h v -WCIU w c i u -WCKD w c k d -WCKY w c k y -WCLN w c l n -WCLU w c l u -WCLV w c l v -WCL w c l -WCMA w c m a -WCMC w c m c -WCMFA w c m f a -WCMH w c m h -WCMR w c m r -WCM w c m -WCNB w c n b -WCOZ w c o z -WCPO w c p o -WCPW w c p w -WCRC w c r c -WCRE w c r e -WCRL w c r l -WCSA w c s a -WCSM w c s m -WCS w c s -WCTD w c t d -WCTG w c t g -W.C.T.U. w c t u -WCTU w c t u -WCTV w c t v -WCT w c t -WCTX w c t x -WCVB's w c v b's -WCVB w c v b -WCVE w c v e -WCWA w c w a -wc w c -W. C. w c -W.C. w c -WC w c -WCWC w c w c -WCW w c w -WCXR w c x r -WCYB w c y b -WCZE w c z e -WDAF w d a f -WDA w d a -WDC w d c -WDDM w d d m -WDFN w d f n -WDF w d f -WDGY w d g y -WDH w d h -WDIA w d i a -WDIF w d i f -WDIG w d i g -WDIS w d i s -WDIV w d i v -WDJT w d j t -WDLA w d l a -WDL w d l -WDM w d m -WDNN w d n n -WDNS w d n s -WDNT w d n t -WDOK w d o k -WDO w d o -WDRE w d r e -WDR w d r -WDSE w d s e -WDSN w d s n -WDSO w d s o -WDSU w d s u -WDS w d s -WDTM w d t m -WDUQ w d u q -WDVE w d v e -WDVR w d v r -W. D. w d -W.D. w d -WDWS w d w s -WDW w d w -WDXZ w d x z -WDYZ w d y z -W. E. B. w e b -W.E.B. w e b -W. E. H. w e h -W.E.H. w e h -WEVD w e v d -WEVV w e v v -W. E. w e -W.E. w e -WEZQ w e z q -WFAA w f a a -WFAL w f a l -WFAN w f a n -WFB w f b -WFCA w f c a -WFC w f c -WFDC w f d c -WFDD w f d d -WFDF w f d f -WFDU w f d u -WFD w f d -WFGF w f g f -WFHE w f h e -W.F.H. w f h -WFH w f h -WFIKKN w f i k k n -WFIL w f i l -WFIRST w f i r s t -WFL's w f l's -WFL w f l -WFLX w f l x -WFMT w f m t -WFMU w f m u -W. F. M. w f m -WFM w f m -WF&NW w f and n w -WFNZ w f n z -WFOY w f o y -WFPC w f p c -WFPF w f p f -WFPMA w f p m a -W.F.P. w f p -WFP w f p -WFSBP w f s b p -WFS w f s -WFTDA w f t d a -WFTM w f t m -WFTV w f t v -WFT w f t -WFVA w f v a -W&F w and f -WFWA w f w a -W. F. w f -W.F. w f -WF w f -WFWM w f w m -WFXR w f x r -wga w g a -WGA w g a -WGBH w g b h -WGBI w g b i -W. G. B. w g b -WGB w g b -WGCB w g c b -W.G.C. w g c -WGC w g c -WGEM w g e m -WGFT w g f t -WGGH w g g h -WGHP w g h p -WGH w g h -WGHW w g h w -WGI w g i -WGN's w g n's -WGNT w g n t -wgn w g n -WGN w g n -WGOK w g o k -WGO w g o -WGP w g p -WGPX w g p x -WGRP w g r p -WGRT w g r t -WGRV w g r v -WGSA w g s a -WGSS w g s s -WGS w g s -WGUN w g u n -wg w g -W. G. w g -W.G. w g -WG w g -WGWW w g w w -WGZR w g z r -W.H.B. w h b -WHCA w h c a -WHCB w h c b -WHCC w h c c -W. H. C. w h c -W.H.C. w h c -WHC w h c -WHDG w h d g -WHDI w h d i -W.H.D. w h d -WHEC w h e c -WHFA w h f a -WHFM w h f m -WHFS w h f s -WHHS w h h s -W.H.H. w h h -WHJA w h j a -W.H.J. w h j -WHJY w h j y -WHKT w h k t -WHL's w h l's -WHL w h l -WHNO w h n o -WHP w h p -WHRC w h r c -WHSN w h s n -WHSS w h s s -W.H.S. w h s -WHS w h s -W. H. T. w h t -WHTZ w h t z -W&H w and h -W. H. w h -W.H. w h -WH w h -Whyld w h y l d -WHYT w h y t -W.I.B.C. w i b c -Wica w i c a -wici w i c i -WIC's w i c's -WICU w i c u -Wif w i f -WIF w i f -WIJD w i j d -Wijk w i j k -WIP w i p -W.I.S.E. w i s e -W.I.T.C.H. w i t c h -Wiwa w i w a -W. I. w i -WIXY's w i x y's -WJAL w j a l -WJAR w j a r -wjaz w j a z -WJBC w j b c -WJBF w j b f -WJBK w j b k -WJBR w j b r -WJCI w j c i -WJC w j c -WJEC w j e c -WJEF w j e f -WJER w j e r -WJET w j e t -W. J. J. O. w j j o -W.J.J.O. w j j o -WJJZ w j j z -WJKA's w j k a's -WJKA w j k a -WJKS w j k s -WJLA w j l a -WJMK w j m k -WJM w j m -WJOI w j o i -WJRD w j r d -WJR w j r -WJSC w j s c -W&J's w and j's -WJTD w j t d -WJUX w j u x -W&J w and j -W. J. w j -W.J. w j -WJ w j -WJXX w j x x -WJYL w j y l -WJYM w j y m -WJZA w j z a -WJZJ w j z j -WJZ w j z -WKAG w k a g -WKAQ w k a q -WKAR w k a r -WKA w k a -WKBD w k b d -WKBF's w k b f's -WKB w k b -WKBW w k b w -W. K. C. w k c -WKDM w k d m -WKDN w k d n -WKDZ w k d z -WKFS w k f s -W.K.H. w k h -WKH w k h -WKIK w k i k -WKIP w k i p -WKKB w k k b -WKKD w k k d -WKKZ w k k z -WKLH w k l h -WKNDS w k n d s -WKN w k n -WKQI w k q i -WKRR w k r r -WKSE w k s e -WKSM w k s m -WKSU w k s u -WKTU's w k t u's -WKTU w k t u -W.K.V. w k v -W. K. w k -W.K. w k -WKXJ w k x j -WKX w k x -WKYS's w k y s's -WKYT w k y t -WKY w k y -W.L.A.K. w l a k -WLB w l b -WLCH w l c h -W&LE w and l e -Wley w l e y -WLGH w l g h -WLH w l h -WLII w l i i -WLIR w l i r -WLJY w l j y -WLKT w l k t -WLKY w l k y -WLLM w l l m -WLNP w l n p -WLNQ w l n q -WLNS w l n s -WLPA w l p a -WLPR w l p r -WLPWR w l p w r -wlrs w l r s -WLSG w l s g -WLS w l s -WLTM w l t m -WLUK's w l u k's -WLUK w l u k -WLWC w l w c -W. L. w l -W.L. w l -WL w l -WLW w l w -WLYH's w l y h's -WLYN w l y n -WLY w l y -WLZK w l z k -WMAC w m a c -WMAG w m a g -WMAP w m a p -WMAQ w m a q -WMATA's w m a t a's -WMATA w m a t a -W.M.A. w m a -WMA w m a -WMAZ w m a z -WMBG w m b g -WMCAT w m c a t -WMCA w m c a -WMDB w m d b -WMDI w m d i -WMD w m d -WMEE w m e e -WMEN w m e n -WMET w m e t -WME w m e -WMEX w m e x -WMFC w m f c -WMFM w m f m -WMFP w m f p -W.M.F. w m f -WMF w m f -WMGF w m g f -WMG w m g -WMGY w m g y -WMHG w m h g -WMHT w m h t -WMIX w m i x -WMJR w m j r -WMJZ w m j z -WMKQ w m k q -WMKW w m k w -wml w m l -WMLW w m l w -WMLY w m l y -WMMS w m m s -W. M. M. w m m -WMNF w m n f -WMN w m n -WMOB w m o b -WMO w m o -WMRA w m r a -WMRO w m r o -WMSG w m s g -W.M.S. w m s -WMS w m s -WMTS w m t s -WMT w m t -WMTW w m t w -WMUB w m u b -WMU w m u -WMVM w m v m -WMV w m v -W&M w and m -W. M. w m -W.M. w m -WM w m -WMWM w m w m -WMXB w m x b -WMX w m x -WMYE w m y e -WMYL w m y l -WNAK w n a k -W.N.B.A. w n b a -WNBA w n b a -WNBC w n b c -WNBL w n b l -WNBR w n b r -WNB w n b -WNCN w n c n -WNC w n c -WNCX w n c x -WND w n d -WNED w n e d -WNEL w n e l -WNEM w n e m -WNEP w n e p -WNET w n e t -WNEW's w n e w's -WNEW w n e w -WNFM w n f m -WNGL w n g l -WNIT w n i t -WNJN w n j n -WNJO w n j o -WNJY w n j y -WNK w n k -WNLO w n l o -WNLU w n l u -WNL w n l -WNMX w n m x -WNO w n o -WNPT w n p t -WNRN w n r n -WNR's w n r's -WNRV w n r v -WNSH w n s h -WNSR w n s r -WNTM w n t m -WNT w n t -WNUA w n u a -WNUF w n u f -WNUSP w n u s p -WNWBL w n w b l -W. N. w n -W.N. w n -WN w n -WNWO w n w o -WNWS w n w s -WNW w n w -WNYA w n y a -WNYC's w n y c's -WNYC w n y c -WNYM w n y m -WNY w n y -WNYZ w n y z -wnzaa w n z a a -WOAA w o a a -WOAK w o a k -WOA w o a -WOC w o c -W&OD w and o d -W.O.G. w o g -WOGX's w o g x's -WOJG w o j g -WOLH w o l h -WOOM w o o m -WOPC w o p c -Worh w o r h -WOSM w o s m -W. O. w o -W.O. w o -WOWOW w o w o w -WOXY w o x y -WOZN w o z n -WPAC w p a c -WPA w p a -WPBC w p b c -WPBSA w p b s a -WPBS w p b s -WPBT w p b t -W. P. C. w p c -WPC w p c -WPDH w p d h -WPD w p d -WPFF w p f f -WPFL w p f l -WPF w p f -WPGC w p g c -WPHL w p h l -WPH w p h -WPIAL w p i a l -WPI w p i -WPIX w p i x -WPLA w p l a -WPLG w p l g -WPLS w p l s -WPMW w p m w -WPO w p o -WPPT w p p t -WPP w p p -WPRI w p r i -WPRO w p r o -WPRP w p r p -WPSC w p s c -WPSL w p s l -WPS w p s -WPTB w p t b -WPTD's w p t d's -wpt w p t -WPT w p t -WPTY w p t y -W. P. U. w p u -wp w p -W. P. w p -W.P. w p -WP w p -WPXN w p x n -WPYR w p y r -WQDE w q d e -WQHT w q h t -WQIV w q i v -WQLR w q l r -WQLZ w q l z -WQMS w q m s -WQOK w q o k -WQSO w q s o -WQSX w q s x -WQ w q -WQXI w q x i -WQXR w q x r -W. R. A. S. w r a s -WRAT w r a t -WRBU w r b u -WRCB w r c b -WRCN w r c n -WRC w r c -WRDL w r d l -Wrec w r e c -WRFD w r f d -WRFK w r f k -WRFM w r f m -WRFX w r f x -WRGO w r g o -WRGP w r g p -W. R. G. w r g -WRHA w r h a -WRHC w r h c -WRHI w r h i -W.R.I. w r i -WRKA w r k a -WRKO w r k o -WRKT w r k t -W.R.K. w r k -WRKY w r k y -WRNS w r n s -WRN w r n -WRNY w r n y -WRNZ w r n z -WROJ w r o j -WROK w r o k -WROO w r o o -WRPT w r p t -WRQQ w r q q -WRTA w r t a -WRTC w r t c -WRUV w r u v -WRU w r u -WRVL w r v l -WRVS w r v s -WRWB w r w b -W. R. w r -W.R. w r -WR w r -W.R.W. w r w -WRXT w r x t -WSAR w s a r -WSAV w s a v -W.S.A. w s a -WSA w s a -WSAZ w s a z -WSBG w s b g -WSBM w s b m -WSBT w s b t -WSB w s b -WSCA's w s c a's -WSCR w s c r -W. S. C. w s c -WSC w s c -WSDD w s d d -WSDG w s d g -WSDOT w s d o t -WSDP w s d p -WSDR w s d r -WSEG w s e g -WSEK w s e k -WSE w s e -WSFA w s f a -WSFL w s f l -WSFM w s f m -WSFS w s f s -WSFX w s f x -WSGE w s g e -WSGW w s g w -WSHL w s h l -WSHU w s h u -WSH w s h -WSIPC w s i p c -WSIS w s i s -WSJM w s j m -WSJO w s j o -WSJ w s j -WSLU w s l u -WSL w s l -WSMK w s m k -W.S.M. w s m -WSM w s m -WSNJ w s n j -WSNS w s n s -WSNT w s n t -WSN w s n -WSOF w s o f -WSOPE w s o p e -WSOP's w s o p's -WSOP w s o p -WSORR w s o r r -WSO w s o -WSOY w s o y -WSPA w s p a -WSPC w s p c -WSPD w s p d -WSPN w s p n -WSPS w s p s -WSPU w s p u -WSP w s p -WSRA w s r a -WSRE's w s r e's -WSRF w s r f -WSSA w s s a -WSSCC w s s c c -WSSSA w s s s a -WSS w s s -WSTE w s t e -WSTR w s t r -WSUA w s u a -WSUD w s u d -WSUE w s u e -WSUS w s u s -WSU w s u -WSV w s v -WSVZ w s v z -WSWI w s w i -ws w s -W. S. w s -W.S. w s -WS w s -WSW w s w -WSYN w s y n -WSYX w s y x -WTAA w t a a -WTAE w t a e -WTAF w t a f -WTAG w t a g -WTAM's w t a m's -W. T. A. w t a -WTA w t a -WTAW w t a w -WTCC w t c c -W. T. C. w t c -W.T.C. w t c -WTC w t c -WTDY w t d y -WTEL w t e l -WTEV's w t e v's -WTEV w t e v -WTF w t f -WTGB w t g b -WTHR's w t h r's -WTHS w t h s -WTIU w t i u -WTI w t i -WTIX w t i x -WTKG w t k g -WTKO w t k o -WTKR w t k r -WTKS w t k s -WTLS w t l s -WTMA w t m a -WTNH w t n h -WTN w t n -WTOP w t o p -WTO's w t o's -WTOS w t o s -WTO w t o -WTPA w t p a -WTPQ w t p q -WTR w t r -WTs w t's -WTS w t s -WTTF w t t f -WTTM w t t m -WTTs w t t's -WTT w t t -WTTW w t t w -WTTZ w t t z -WTVA w t v a -WTVC w t v c -WTVD w t v d -WTVI w t v i -WTVM w t v m -WTVU w t v u -WTVW w t v w -WTVX w t v x -WTWF w t w f -W. T. w t -W.T. w t -WT w t -WTX w t x -WUAB w u a b -WUAP w u a p -WUAs w u a's -WUC w u c -Wudl w u d l -Wuhr w u h r -Wuhu w u h u -WUKB w u k b -WUPG w u p g -WUPN w u p n -WUPZ w u p z -Wu's w u's -WUSW w u s w -WUTK w u t k -WUVC w u v c -wuv w u v -W. U. w u -Wuz w u z -WVa w v a -WVBX w v b x -WVCA w v c a -WVIT w v i t -WVJC w v j c -WVJP w v j p -WVLZ w v l z -WVNI w v n i -WVNY w v n y -W. V. O. w v o -WVO w v o -WVPA w v p a -WVPO w v p o -WVSSAC w v s s a c -WVSU w v s u -WVTU's w v t u's -WVTU w v t u -WVUE w v u e -WVU w v u -WVUW w v u w -W. V. w v -W.V. w v -WV w v -WWAA w w a a -WWAMI w w a m i -WWASP w w a s p -WWA w w a -WWAX w w a x -WWBZ w w b z -WWCD w w c d -WWC w w c -WWDB's w w d b's -WWDC w w d c -WWDR w w d r -WWD w w d -WWE's w w e's -Wwe w w e -WWE w w e -WWF's w w f's -WWF w w f -WWGC w w g c -WWHT w w h t -WWHW w w h w -WWIS w w i s -WWJP w w j p -WWKB w w k b -WWKL w w k l -WWML w w m l -WWMM w w m m -WWMX w w m x -WWMY w w m y -WWN w w n -WWNW w w n w -WWNY w w n y -WWOX w w o x -WWPG w w p g -WWP w w p -WWPW w w p w -WWRFC w w r f c -WWRV w w r v -WWSS w w s s -WWS w w s -WWTI's w w t i's -WWTI w w t i -WWTN w w t n -WWU w w u -WWVA w w v a -WWVR w w v r -WWV w w v -WWWF w w w f -WWWQ's w w w q's -W. W. w w -W.W. w w -WW w w -WWW w w w -WWWW w w w w -WWYD w w y d -WXB w x b -WXCN w x c n -WXDU w x d u -WXEL w x e l -WXIL w x i l -WXIN's w x i n's -WXIN w x i n -WXJM's w x j m's -WXJ w x j -WXN w x n -WXQW w x q w -WXTG w x t g -WXW w x w -wx w x -WX w x -W.Y. w y -WY w y -wyzc w y z c -WZBC w z b c -WZFG w z f g -WZFT w z f t -WZGC w z g c -WZJQ w z j q -WZKC w z k c -WZLR w z l r -WZLX w z l x -WZRB w z r b -WZRD w z r d -WZST w z s t -W. Z. w z -WZ w z -WZX w z x -WZZM w z z m -WZZX w z z x -xbg x b g -XBMC x b m c -XBM x b m -Xbra x b r a -XBRL x b r l -XBV x b v -XCB x c b -XCE x c e -XCMS x c m s -XCOM x c o m -XCOR x c o r -xCo x c o -XCR x c r -XCT x c t -XCVI x c v i -XCV x c v -X. C. x c -XC x c -XCX x c x -xda x d a -XDDM x d d m -XDH x d h -XDK x d k -XDRs x d r's -XDR x d r -XD's x d's -XDS x d s -X. D. x d -XD x d -Xfce x f c e -XFC x f c -XFDL x f d l -XFD x f d -XFL x f l -XFM x f m -XFS x f s -XFU x f u -xf x f -X.F. x f -XF x f -XHVFC x h v f c -xh x h -X. H. x h -XH x h -XJD x j d -XJ x j -XKE x k e -XKL x k l -xk x k -XK x k -XL x l -XMB x m b -xmc x m c -xml x m l -XML x m l -XMMS x m m s -XMPP x m p p -XMP x m p -XMRV x m r v -XMSN x m s n -XM's x m's -XMU x m u -X.M. x m -XM x m -XN x n -xO x o -X.O. x o -XO x o -XPA x p a -XPD x p d -XPe x p e -XPL x p l -XPN x p n -XP's x p's -XPS x p s -XPT x p t -XPW x p w -XP x p -X.Q. x q -XQ x q -XRCC x r c c -XRCO x r c o -XRDS x r d s -XRD x r d -XRE x r e -XR x r -XSD x s d -XSi x s i -XSI x s i -XSLT x s l t -xsr x s r -XS x s -XTB x t b -XTC x t c -XTG x t g -XTwas x t w a's -xt x t -XT x t -XVAS x v a -XV x v -XWA x w a -XWB x w b -XWF x w f -XWIS x w i s -X. W. x w -XYNU x y n u -XYP x y p -xy x y -XY x y -xyz x y z -xyZ x y z -XYZ x y z -XZ x z -Y. A. y a -Y.A. y a -YBA y b a -YBL y b l -YBNL y b n l -YBN y b n -YBR y b r -YBX y b x -Y. B. y b -Y.B. y b -YB y b -YBYS y b y s -YCC y c c -YCD y c d -ycia y c i a -ycie y c i e -YCL y c l -YCO y c o -YCU y c u -YCV y c v -yc y c -Y. C. y c -Y.C. y c -YC y c -YCYW y c y w -YDA y d a -Ydby y d b y -Yde y d e -YDG y d g -YdiB y d i b -YDK y d k -YDNC y d n c -yd y d -Y. D. y d -Y.D. y d -YD y d -Y.E. y e -YFCMD y f c m d -YFCs y f c's -YFCU y f c u -YFC y f c -yfle y f l e -YF&R y f and r -YF y f -YGB y g b -YGEX y g e x -YGLA y g l a -YGL y g l -YG's y g's -yg y g -Y. G. y g -Y.G. y g -YG y g -YHA y h a -YHWH y h w h -Y. H. y h -YH y h -YIF y i f -YIG y i g -YITP y i t p -Y. I. y i -Y.I. y i -YJO y j o -yj y j -Y. J. y j -YJ y j -Ykt y k t -Y. K. y k -Y.K. y k -YK y k -YKY y k y -Yle y l e -YLE y l e -YLF y l f -yl y l -Y. L. y l -YL y l -YMCA's y m c a's -Y.M.C.A. y m c a -YMCA y m c a -YMF y m f -Ymke y m k e -Y.M.M. y m m -YMM y m m -YMO y m o -ym y m -Y. M. y m -YM y m -yndi y n d i -Yndi y n d i -yne y n e -Yngve y n g v e -Ynis y n i's -YNK y n k -YNU y n u -yn y n -Yn y n -Y. N. y n -Y.N. y n -YN y n -Y.O. y o -YO y o -ypa y p a -YPC y p c -YPF y p f -YPG y p g -YPM y p m -YPO y p o -Y.P.P. y p p -YPP y p p -Y.P.S. y p s -YPT y p t -YPU y p u -Y.P. y p -YP y p -Y. Q. y q -YQ y q -yra y r a -Yra y r a -YRC y r c -YRF y r f -YRK y r k -YRM y r m -YRT y r t -Y&R y and r -YSK'da y s k d a -YSK'ya y s k y a -YSK y s k -YSL y s l -YSRCP y s r c p -YSR y s r -YSU's y s u's -YSU y s u -Ys y's -Y.S. y s -YS y s -YSY y s y -yta y t a -YTB y t b -Ytre y t r e -YTS y t s -YTV's y t v's -YTV y t v -Y&T y and t -Y. T. y t -Y.T. y t -YT y t -Y. V. y v -YWCA y w c a -YWC y w c -Y. W. y w -Y.Y. y y -Z. A. B. z a b -Z. A. z a -Z.A. z a -ZBC z b c -ZBS z b s -ZB z b -ZCE z c e -ZCGWM z c g w m -ZCMI z c m i -ZCTA z c t a -ZCY z c y -ZC z c -Zdar z d a r -zda z d a -ZDF z d f -ZDR z d r -Z.E.R.O. z e r o -ZFC z f c -ZFK z f k -ZFM z f m -ZFP z f p -ZFS z f s -ZFX z f x -ZF z f -ZGE z g e -ZGI z g i -zg z g -Z.G. z g -ZG z g -zh z h -Z. H. z h -Z.H. z h -ZH z h -zijn z i j n -zij z i j -Zij z i j -Z. I. z i -Z. J. z j -ZKK z k k -ZKM z k m -Z.K. z k -Z.L. z l -Z.M. z m -ZNA z n a -ZNF z n f -ZNK z n k -ZnO z n o -ZNW z n w -ZPAP z p a p -Zpav z p a v -ZPAV z p a v -ZPA z p a -ZPK z p k -Z.P. z p -ZP z p -ZQ z q -ZRC z r c -ZrI z r i -ZRK z r k -ZSC z s c -ZSE z s e -ZSF z s f -ZSG's z s g's -ZSIS z s i s -ZSL z s l -ZSO z s o -ZSSS z s s s -Zsuzsa z s u z s a -ZTE z t e -ZWYCI z w y c i -Z.W. z w -Z.X.V. z x v -Z.X. z x -ZYF z y f -Z. Y. z y -Z.Y. z y -Z. Z. z z -Z.Z. z z diff --git a/nemo_text_processing/text_normalization/en/data/whitelist/ipa_symbols.tsv b/nemo_text_processing/text_normalization/en/data/whitelist/ipa_symbols.tsv deleted file mode 100644 index f5559c711630..000000000000 --- a/nemo_text_processing/text_normalization/en/data/whitelist/ipa_symbols.tsv +++ /dev/null @@ -1,521 +0,0 @@ -a -aoj -aəj -aː -aːʲ -aː͡j -aː͡ɨ̯ -aˤ -aˤː -a̠ -a̠ː -a̰ -a͡e -a͡i -a͡iː -a͡i̯ -a͡j -a͡o -a͡u -a͡uː -a͡u̯ -a͡w -a͡ə -a͡ɨ̯ -a͡ɪ -a͡ʊ -b -bʱ -bʲ -bː -b̥ -c -cʰ -cː -ç -d -dʲ -dː -d̥ -d̪ -d̪ʱ -d͡z -d͡zʷ -d͡zː -d͡ʑ -d͡ʒ -d͡ʒʱ -d͡ʒʲ -d͡ʒː -e -eː -eːʲ -eː͡j -ẽː -ẽ͡j̃ -e̞ -e̞ː -e̯ -e͡i -e͡iː -e͡ɨ̯ -f -fʲ -fː -h -hː -i -iəj -iəw -iʲ -iː -iːʲ -ĩː -i̥ -i̯ -i͡u -i͡ə -i͡ɛ -j -jː -j̃ -k -kʰ -kʰː -kʲ -kʲʼ -kʷ -kʷʼ -kʼ -kː -k̚ -k̚ʲ -k̟̚ -k͈ -k͡p̚ -l -lʲ -lː -l̥ -l̩ -m -mʲ -mʲː -mː -m̥ -m̩ -n -nʲ -nː -n̥ -n̩ -o -oʲ -oː -oːʲ -ò -õ͡j̃ -õ͡w̃ -o̝ -o̞ -o̞ː -o̯ -o̰ -o͡u -o͡uː -p -pʰ -pʰː -pʲ -pʷʼ -pʼ -pː -p̚ -p̚ʲ -p͈ -p͜f -p͡f -q -qʷ -qʼ -r -rʲ -rː -r̂ -r̂ː -r̥ -r̩ -s -sʰ -sʲ -sʼ -sː -s͈ -t -tʰ -tʰː -tʲ -tʷʼ -tʼ -tː -t̚ -t̪ -t̪ʰ -t͈ -t͜s -t͡s -t͡sʰ -t͡sʰː -t͡sʲ -t͡sʷ -t͡sʼ -t͡sː -t͡ɕ -t͡ɕʰ -t͡ɕ͈ -t͡ʂ -t͡ʂʼ -t͡ʃ -t͡ʃʰ -t͡ʃʰː -t͡ʃʲ -t͡ʃʷ -t͡ʃʼ -t͡ʃː -u -uəj -uʲ -uː -uːʲ -ũː -ũ͡j̃ -u̯ -u͡e -u͡i -u͡j -u͡ɔ -u͡ə -v -vʲ -vː -w -w̃ -x -xʷ -xː -y -yː -yːʲ -y̯ -z -zʲ -zː -z̥ -à -àː -á -áː -â -âː -ã -ã̠ -æ -æː -æ̀ -æ̀ː -æ̂ -æ̂ː -æ͡ɪ -æ͡ʉ -ç -è -èː -é -éː -ê -êː -ì -ìː -í -íː -î -îː -ï -ð -ò -òː -ó -óː -ô -ôː -õ -õː -õ̞ -ø -øː -øːʲ -ø̯ -ù -ùː -ú -úː -û -ûː -ā -āː -ē -ēː -ĕ -ĕ͡ə -ě -ěː -ħ -ĩ -ĩː -ī -īː -ŋ -ŋʲ -ŋ̊ -ŋ̍ -ŋ̟ -ŋ̩ -ŋ͡m -ō -ŏ -ŏ͡ə -œ -œː -œ̃ -œ͡i -œ͡iː -œ͡ʏ -ř -řː -ũ -ũː -ū -ūː -ŭ -ŭ͡ə -ǎ -ǎː -ǐ -ǐː -ǒ -ǒː -ǔ -ǔː -ǣ -ǣː -ɐ -ɐː -ɐ̃ -ɐ̃͡j̃ -ɐ̃͡w̃ -ɐ̯ -ɐ̯̯ -ɑ -ɑː -ɑ̃ -ɑ̃ː -ɒ -ɒʲ -ɒː -ɓ -ɔ -ɔː -ɔˤː -ɔ̀ -ɔ̀ː -ɔ́ -ɔ́ː -ɔ̃ -ɔ̃ː -ɔ̰ -ɔ͡i̯ -ɔ͡ə -ɔ͡ɨ̯ -ɔ͡ɪ -ɔ͡ʊ -ɕ -ɕʰ -ɕː -ɕ͈ -ɖ -ɖʱ -ɗ -ɘ -ɘː -ə -əː -əˤ -ə̀ -ə́ -ə̃ -ə̯ -ə͡u̯ -ə͡w -ə͡ɨ -ə͡ɨ̯ -ɚ -ɛ -ɛʲ -ɛː -ɛˤː -ɛ̀ -ɛ̀ː -ɛ́ -ɛ́ː -ɛ̂ -ɛ̂ː -ɛ̃ -ɛ̃ː -ɛ̄ -ɛ̄ː -ɛ̰ -ɛ͡i -ɛ͡i̯ -ɛ͡u -ɛ͡u̯ -ɛ͡ɪ -ɛ͡ʊ -ɜ -ɜː -ɝ -ɝː -ɟ -ɟː -ɟ͡ʝ -ɡ -ɡʱ -ɡʲ -ɡʷ -ɡː -ɡ̊ -ɣ -ɤ -ɥ -ɦ -ɨ -ɨəj -ɨː -ɨ̃ᵝ -ɨ̞ -ɨ̥ᵝ -ɨ̯ -ɨ͡u̯ -ɨ͡w -ɨ͡ə -ɨᵝ -ɨᵝː -ɪ -ɪː -ɪ̀ -ɪ́ -ɪ̃ -ɪ̯ -ɪ̰ -ɪ͡u̯ -ɪ͡ʊ -ɫ -ɫː -ɬ -ɬʼ -ɭ -ɮ -ɯ -ɯː -ɯ̟̃ᵝ -ɯ̟̊ᵝ -ɯ̟ᵝ -ɯ̟ᵝː -ɰ -ɰ̃ -ɰᵝ -ɱ -ɱ̩ -ɲ -ɲː -ɲ̊ -ɲ̟ -ɳ -ɴ -ɸ -ɸʷ -ɹ -ɻ -ɽ -ɽʱ -ɾ -ɾʲ -ɾː -ɾ̝̊ -ʀ -ʁ -ʁʷ -ʁː -ʂ -ʂʷ -ʃ -ʃʰ -ʃʲ -ʃʷ -ʃʷʼ -ʃʼ -ʃː -ʈ -ʈʰ -ʉ -ʉː -ʊ -ʊ̀ -ʊ́ -ʊ̃ -ʊ̯ -ʊ̯͡i -ʊ̯͡ɨ -ʊ̰ -ʋ -ʌ -ʌ̹ -ʍ -ʎ -ʏ -ʏː -ʏ̯ -ʐ -ʐʷ -ʑ -ʒ -ʒʲ -ʒʷ -ʒː -ʔ -ʔʲ -ʔʷ -ʝ -˦ˀ˥ -˦˥ -˦˧˥ -˦˩ -˧ˀ˨ -˧˦ -˧˧ -˧˨ -˧˩ -˨˩ -˨˩˦ -˨˩˨ -β -θ -χ -χʷ -χː -ḛ -ḭ -ṵ -ẽ -ẽː -ẽ̞ -‿ \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv b/nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv deleted file mode 100644 index a55a04b9ab68..000000000000 --- a/nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv +++ /dev/null @@ -1,21 +0,0 @@ -Mr. mister -Mrs. misses -Dr. doctor -Drs. doctors -Co. company -Lt. lieutenant -Sgt. sergeant -St. saint -Jr. junior -Maj. major -Hon. honorable -Gov. governor -Capt. captain -Esq. esquire -Gen. general -Ltd. limited -Rev. reverend -Col. colonel -Mt. mount -Ft. fort -etc. et cetera diff --git a/nemo_text_processing/text_normalization/en/data/whitelist/symbol.tsv b/nemo_text_processing/text_normalization/en/data/whitelist/symbol.tsv deleted file mode 100644 index 6f2f8c69a8e6..000000000000 --- a/nemo_text_processing/text_normalization/en/data/whitelist/symbol.tsv +++ /dev/null @@ -1,23 +0,0 @@ -& and -# hash -@ at -§ section -™ trademark -® registered trademark -© copyright -_ underscore -% percent sign -* asterisk -+ plus -/ slash -= equal sign -^ circumflex -| vertical bar -~ tilde -$ dollar -£ pound -€ euro -₩ won -¥ yen -° degree -º degree diff --git a/nemo_text_processing/text_normalization/en/data/whitelist/tts.tsv b/nemo_text_processing/text_normalization/en/data/whitelist/tts.tsv deleted file mode 100644 index e81924755d2e..000000000000 --- a/nemo_text_processing/text_normalization/en/data/whitelist/tts.tsv +++ /dev/null @@ -1,3851 +0,0 @@ -Ph.D. PHD -Hon. honorable -Mt. Mount -Maj. Major -Rev. Reverend -w/o without -a/c number account number -c/o care of -Gov. governor -vs. versus -vs versus -dept. department -prof. professor -est. estimated -vol volume -vol. volume -bldg. building -Bldg. Building -apt. apartment -Apt. Apartment -World War I World War one -World War II World War two -etc. etcetera. -SnO2 tin four oxide -dept department -HVAC H-vac -SPDR spider -ZIP zip -~ approximately -κ kappa -ω omega -α alpha -ν nu -δ delta -Δ delta -Α alpha -β beta -Β beta -χ chi -Χ chi -ε epsilon -Ε epsilon -φ phi -Φ phi -γ gamma -Γ gamma -η eta -Η eta -ι iota -Ι iota -κ kappa -Κ kappa -λ lambda -Λ lambda -μ mu -Μ mu -ν nu -Ν nu -ο omicron -Ο omicron -π pi -Π pi -θ theta -Θ theta -ρ rho -Ρ rho -σ sigma -Σ sigma -τ tau -Τ tau -υ upsilon -Υ upsilon -ω omega -Ω omega -ξ xi -Ξ xi -ψ psi -Ψ psi -ζ zeta -Ζ zeta -ltd limited -int'l international -A. D AD -A.D AD -a. d AD -a.d AD -a. d. AD -a.d. AD -B. C BC -B.C BC -b. c BC -b.c BC -A. D. AD -A.D. AD -B. C. BC -B.C. BC -b. c. BC -b.c. BC -A. A. a a -A.A. AA -A&A A and A -AAAs AAA's -AaB AAB -Aabo AABO -aac AAC -AACNo AACNO -AADTs AADT's -Aadu AADU -AAFld AAFLD -Aap AAP -A. B. AB -A.B. AB -Abhi ABHI -abl ABL -Abl ABL -Abp ABP -Abr ABR -abv ABV -Abz ABZ -A&C A and C -A. C. AC -A.C. AC -A/C AC -acac ACAC -Acad ACAD -ACC&S ACC and S -Achs ACH's -Acis ACI's -acls ACLS -ACMs ACM's -AcpA ACPA -AcpB ACPB -acq ACQ -A&CR A and CR -AcSOC ACSOC -acu ACU -AC&W AC and W -Acy ACY -A&D A and D -Adab ADAB -A. D. AD -A.D. AD -A.D.A.M. adam -AdaSL ADASL -AdC ADC -ADCs ADC's -AD&D AD and D -AdK ADK -Adl ADL -ADLs ADL's -adm ADM -Adm ADM -admn ADMN -ADNs ADN's -AdP ADP -ADRs ADR's -ADSRs ADSR's -ADSs ADS's -Adwa ADWA -A&E A and E -A. E. AE -A.E. AE -Aed AED -aegte AEGTE -Aeka AEKA -ael AEL -Aema AEMA -aen AEN -aere AERE -AEre AERE -AErn AERN -Aert AERT -Aes AE's -aet AET -Afa AFA -af AF -Af AF -A. F. AF -A.F. AF -Afaf AFAF -afc AFC -AFCSThe AFCSTHE -Afd AFD -AfD AFD -AfDB AFDB -Afe AFE -Aff AFF -Afi AFI -Afiq AFIQ -AFMs AFM's -AfNS AFNS -Afo AFO -Afr AFR -A. G. AG -A.G. AG -Agco AGCO -Agda AGDA -AG&G AG and G -agg AGG -Agi AGI -Agis AGI's -agli AGLI -Agn AGN -AgN AGN -AGNs AGN's -AGRs AGR's -Agsu AGSU -agt AGT -Agte AGTE -Agu AGU -A. H. AH -A.H. AH -Ahdhra AHDHRA -ahe AHE -Ahe AHE -Ahirs AHIR's -Ahk AHK -Ahsa'i AHSAI -Ahta AHTA -aht AHT -Ahu AHU -Ahva AHVA -A&I A and I -A. I. AI -A.I. AI -Aias AIA's -aici AICI -Aicme AICME -AICs AIC's -Aiel AIEL -Aife AIFE -AiG AIG -Aigis AIGI's -Aija AIJA -Aik AIK -AIs AI's -Aist AIST -Aitu AITU -Aiud AIUD -aius AIUS -A. J. AJ -A.J. AJ -Ajba AJBA -Ajmi AJMI -Ajok AJOK -Ajsa AJSA -Aju AJU -Akaa AKAA -a.k.a. AKA -aka. AKA -aka AKA -Aka. AKA -Aka AKA -A&K A and K -A. K. AK -A.K. AK -AKAPs AKAP's -akas AKA's -AKAs AKA's -akh AKH -Akpa AKPA -Aku AKU -A&L A and L -A. L. AL -A.L. AL -Alh ALH -ALUs ALU's -A'ma AMA -A&M A and M -a.m. AM -a.m AM -A. M. AM -A.M. AM -AMAs AMA's -amb AMB -Amb AMB -AmBX AMBX -AMCs AMC's -Amda AMDA -Amd AMD -AM&FM AM and FM -Amha AMHA -aml AML -Amli AMLI -Amlwch AMLWCH -Amm AMM -AM&O AM and O -Ampt AMPT -amr AMR -A&M's A and M's -Ams' AM's -amsl AMSL -Amta AMTA -amt AMT -Amt AMT -Amts AMT's -Amu AMU -A. N. AN -A.N. AN -andFHL ANDFHL -andPPP ANDPPP -Anej ANEJ -Angti ANGTI -Angu ANGU -Anhe ANHE -Anr ANR -Anrep ANREP -A&O A and O -A. O. AO -A.O. AO -AOCCs AOCC's -Aodh AODH -Aodla AODLA -Aogo AOGO -Aoke AOKE -Aoko AOKO -Aola AOLA -Aol AOL -Aone AONE -Aoni AONI -Aoos AOO's -aor AOR -AORs AOR's -aos AOS -AoS AOS -Aotus AOTU's -aov AOV -aovf AOVF -A&P A and P -A. P. AP -A.P. AP -Apc APC -APCh APCH -APCs APC's -apg APG -Aph APH -Apiao APIAO -Api API -APi API -Apic APIC -APICv APICV -Apl APL -Aplu APLU -Apphttp APPHTTP -Appl APPL -appr APPR -Appts APPT's -appu APPU -Appu APPU -A&P's A and P's -Aps AP's -APs AP's -AP&T AP and T -Aqa AQA -Aql AQL -Aqr AQR -A&R A and R -A. R. AR -A.R. AR -ArgR ARGR -ARGs ARG's -ArH ARH -ARMv ARMV -Arnd ARND -arv ARV -Arv ARV -Arwi ARWI -As'ad ASAD -Asai ASAI -Asao ASAO -A.s A's -A. S. AS -A.S. AS -Asasp ASASP -asbl ASBL -asci ASCI -Asci ASCI -Asdis ASDI's -Asfi ASFI -Asn ASN -Aso ASO -Aspe ASPE -Asr ASR -Assn ASSN -assoc ASSOC -Assoc ASSOC -ASSPs ASSP's -Asst ASST -Aste ASTE -Astt ASTT -Aswa ASWA -A&T A and T -atac ATAC -Atac ATAC -Atad ATAD -Ata's ATA's -Atas ATA's -A. T. AT -A.T. AT -ATBs ATB's -atc ATC -atcc ATCC -Atg ATG -ath ATH -Ath ATH -Atia's ATIA's -Ati ATI -ATi ATI -Atid ATID -Atiiq ATIIQ -Atil ATIL -Atiq ATIQ -Ativ ATIV -Atka ATKA -Atl ATL -Atli ATLI -atm ATM -Atm ATM -ATMs ATM's -Atos ATO's -ATOs ATO's -atpB ATPB -atri ATRI -Atri ATRI -A&T's A and T's -A.T.s AT's -AT&SF AT and SF -AT&T AT and T -attd ATTD -AT&T's AT and T's -atv ATV -ATVs ATV's -AtxA ATXA -A. U. AU -A.U. AU -Aub AUB -Aubl AUBL -AUVs AUV's -Auw AUW -avab AVAB -A. V. AV -A.V. AV -AVCs AVC's -avg AVG -Avn AVN -Avo AVO -AvP AVP -AVRs AVR's -Avs' AV's -Avs AV's -avvo AVVO -Awa AWA -AWAs AWA's -A. W. AW -A.W. AW -Awo AWO -Awwa AWWA -A. X. AX -A. Y. AY -A.Y. AY -A. Z. AZ -A.Z. AZ -Azg AZG -Azi AZI -azm AZM -AzTV AZTV -Baad BAAD -Ba'al BAAL -Baal BAAL -Baam BAAM -Baap BAAP -ba'as BAA's -Baat BAAT -Ba'ath BAATH -B. A. BA -B.A. BA -B&A B and A -Bac BAC -bae BAE -Bae BAE -BAe BAE -Bauw BAUW -B'Av BAV -B&B B and B -bb BB -B. B. BB -B.B. BB -bbc BBC -BB&CI BB and CI -BBC&PJR BBC and PJR -BBQ'er BBQER -B&Bs B and B's -BB&S BB and S -BBSes BBSE's -BB&T BB and T -BbvCI BBVCI -bc BC -B. C. BC -B.C. BC -Bcci BCCI -B.C.'s BC's -BCs BC's -BCSic BCSIC -BdA BDA -bd BD -B. D. BD -B.D. BD -Bde BDE -bds BDS -BdU BDU -B. E. BE -B.E. BE -Bedw BEDW -bef BEF -bei BEI -Bei BEI -Beih BEIH -Beis BEI's -bej BEJ -BEMs BEM's -Ber BER -Bes BE's -bf BF -B. F. BF -B.F. BF -BFFs BFF's -bfy BFY -B. G. BG -B.G. BG -BglII BGLII -bgs BGS -Bgy BGY -Bha BHA -B&H B and H -B. H. BH -B.H. BH -BH&E BH and E -Bhe BHE -BHMs BHM's -bhttp BHTTP -B. I. BI -B.I. BI -Bie BIE -biedt BIEDT -Biem BIEM -B.I.G.'s BIG's -BiH BIH -B. J. BJ -B.J. BJ -BJPs BJP's -B.J.'s BJ's -B&K B and K -B. K. BK -B.K. BK -BKCa BKCA -B. L. BL -B.L. BL -ble BLE -bleg BLEG -blev BLEV -bli BLI -Blla BLLA -BLR&D BLR and D -BLTs BLT's -B&M B and M -B. M. BM -B.M. BM -bmd BMD -bmi BMI -BMPs BMP's -BMRs BMR's -bmt BMT -B&N B and N -bnb BNB -B. N. BN -B.N. BN -Boac BOAC -B&O B and O -B. O. BO -B.O. BO -Boc BOC -BoC BOC -BOCs BOC's -boj BOJ -Boj BOJ -B&O's B and O's -bp BP -B. P. BP -B.P. BP -bpl BPL -B&Q B and Q -B. Q. BQ -Brbic BRBIC -B. R. BR -B.R. BR -BRBs BRB's -Brne BRNE -Brno BRNO -BRTs BRT's -Brza BRZA -brzu BRZU -brzy BRZY -Brzyk BRZYK -BsaL BSAL -B. s B's -B.'s B's -Bs B's -B. S. BS -B.S. BS -bsd BSD -bt BT -B. T. BT -B.T. BT -Btry BTRY -btsan BTSAN -btus BTUS -BTUs BTU's -B.U. BU -B. V. BV -B.V. BV -Bwa BWA -B&W B and W -B. W. BW -B.W. BW -BWTs BWT's -bwwtv BWWTV -Bxa BXA -Bxe BXE -B. Y. BY -byc BYC -bygd BYGD -Byk BYK -byn BYN -Byo BYO -Byou BYOU -Byrl BYRL -bySLC BYSLC -byt BYT -B.Z. BZ -bzhed BZHED -Bzik BZIK -Bzyb BZYB -Caat CAAT -C. A. CA -C.A. CA -C&A C and A -CA&CC CA and CC -Cadw CADW -caeca CAECA -Cae CAE -C.A.M.'s CAM's -Capt. captain -cas CAS -Cas CA's -Casc CASC -cb CB -C. B. CB -C.B. CB -cbc CBC -CB&CNS CB and CNS -CBDs CBD's -CBGBs CBGB's -CbiXS CBIXS -CB&Q CB and Q -CBs CB's -'c C -CcaA CCAA -cca CCA -Cca CCA -C&C C and C -C. C. CC -C.C. CC -ccd CCD -CCDev CCDEV -CCDs CCD's -CCEd CCED -CCi CCI -ccm CCM -CCRCs CCRC's -cct CCT -ccus CCUS -C&D C and D -cdc CDC -cd CD -C. D. CD -C.D. CD -CD&DR CD and DR -CDi CDI -cDNA CDNA -cdnas CDNAS -cDNAs CDNA's -CDOs CDO's -CDPs CDP's -CD&R CD and R -cds CDS -CDs CD's -CDSPCo CDSPCO -CdtA CDTA -CdTe CDTE -CDTi CDTI -CD&V CD and V -Cec CEC -c'e CE -ce CE -C'e CE -Ce CE -C. E. CE -C.E. CE -Ceel CEEL -Cefn CEFN -C&EI's C and EI's -C&EN C and EN -cen CEN -Cen CEN -ceo CEO -Ceol CEOL -Ceorl CEORL -Ceos CEO's -CEOs' CEO's -CEOs CEO's -cer CER -Cer CER -ces CES -Ces CE's -Cex CEX -CeX CEX -Cfa CFA -C&F C and F -cf CF -C. F. CF -C.F. CF -CFOs CFO's -CFTs CFT's -C&G C and G -C. G. CG -C.G. CG -cgl CGL -C&H C and H -C. H. CH -C.H. CH -Chhu CHHU -Chirs CHIR's -CHP'den CHPDEN -CHPs CHP's -Chy CHY -Cia CIA -ci CI -Ci CI -C. I. CI -C.I. CI -Cicic CICIC -cii CII -cim CIM -Cim CIM -Cio CIO -CIOs CIO's -Cip CIP -cis CIS -CIs CI's -cit CIT -CiTD CITD -citS CITS -CitX CITX -Ciuc CIUC -Ciu CIU -CiU CIU -Cixi CIXI -C. J. CJ -C.J. CJ -C. K. CK -C.K. CK -ckx CKX -CLBs CLB's -clc CLC -cl CL -C. L. CL -C.L. CL -Cle CLE -cllr CLLR -CL&N CL and N -Clo CLO -CL&P CL and P -CLs CL's -CLTs CLT's -Clu CLU -Clwyd CLWYD -C&MA C and MA -cm CM -C. M. CM -C.M. CM -CMCs CMC's -Cmde CMDE -cmdr CMDR -cml CML -CMMs CMM's -cmn CMN -Cmte CMTE -cn CN -C. N. CN -C.N. CN -CNMs CNM's -cnr CNR -CNs CN's -CNTs CNT's -Cnut's CNUT's -CNVs CNV's -C&O C and O -C. O. CO -C.O. CO -Co. company -Col. colonel -C&P C and P -cp CP -C. P. CP -C.P. CP -CPCs CPC's -cpo CPO -CPUs CPU's -cq CQ -CQDs CQD's -cr CR -C. R. CR -C.R. CR -Cre CRE -crk CRK -Crkva CRKVA -crkve CRKVE -CRLs CRL's -crm CRM -Crne CRNE -Crni CRNI -Crno CRNO -CRs CR's -csa CSA -Csa CSA -Csak CSAK -Csaky CSAKY -C&S C and S -cs CS -C.s C's -Cs C's -C. S. CS -C.S. CS -C&SF C and SF -csg CSG -CsI CSI -CSis CSI's -CSIs CSI's -CSOs CSO's -cspA CSPA -csp CSP -CSPs CSP's -CSSNCs CSSNC's -Csuz CSUZ -CTAs CTA's -C&T C and T -cte CTE -CTLs CTL's -ctr CTR -Ctrip CTRIP -ctv CTV -cu CU -C. U. CU -Cuyp CUYP -C&V C and V -cvcp CVCP -'cv CV -cv CV -C. V. CV -C.V. CV -CVEs CVE's -CVOs CVO's -CVs CV's -CVTs CVT's -C. W. CW -C.W. CW -Cwej CWEJ -Cwele CWELE -cwm CWM -cwrt CWRT -C.X. CX -C. Y. CY -C.Y. CY -cyl CYL -Cyn CYN -CyP CYP -Cyrl CYRL -Cys CY's -czci CZCI -cz CZ -C.Z. CZ -czy CZY -Daai DAAI -D. A. DA -D.A. DA -D&AD D and AD -D.A.N.C.E. dance -Dav DAV -dBa DBA -db DB -D. B. DB -D.B. DB -DCCs DCC's -D&C D and C -dc DC -D. C. DC -D.C. DC -DCom DCOM -D.C.'s DC's -DCs DC's -DCUs DCU's -D&D D and D -D. D. DD -D.D. DD -DDi DDI -Ddoc DDOC -Ddu DDU -Deah DEAH -D. E. DE -D.E. DE -deg DEG -dei DEI -Dei DEI -DeI DEI -Deijm DEIJM -Deip DEIP -dez DEZ -Dez DEZ -Dfa DFA -DFCs DFC's -D. F. DF -D.F. DF -DfE DFE -DfES DFES -DFPs DFP's -DFs DF's -dfx DFX -D&G D and G -D. G. DG -D.G. DG -Dha DHA -DHBs DHB's -D&H D and H -dh DH -D. H. DH -D.H. DH -d'HEC DHEC -dhe DHE -Dheku DHEKU -Dhod DHOD -dhol DHOL -Dhol DHOL -Dhoo DHOO -Dhor DHOR -Dhou DHOU -dhr DHR -DHSs DHS's -D.I. DI -diy DIY -DiY DIY -Diyn DIYN -DIYs DIY's -Djam DJAM -dj DJ -D. J. DJ -D.J. DJ -DJ'ed DJED -Djem DJEM -Dji DJI -Djo DJO -D'Jok DJOK -djr DJR -D.J.s DJ's -DJs DJ's -DjVu DJVU -DjVus DJVU's -D. K. DK -D.K. DK -dla DLA -dl DL -D. L. DL -D.L. DL -dlia DLIA -DL&LR DL and LR -DLLs DLL's -dlo DLO -DL&W DL and W -D.M.C.'s DMC's -D&M D and M -D. M. DM -D.M. DM -DMed DMED -DM&E DM and E -DMs DM's -DMUs DMU's -Dna DNA -DnA DNA -DnaJ DNAJ -D. N. DN -D.N. DN -Dnepr DNEPR -dnes DNES -Dnes DNE's -DNFs DNF's -DNSBLs DNSBL's -dns DNS -D.O.A.'s DOA's -dod DOD -Dod DOD -DoD DOD -D. O. DO -D.O. DO -D'oh DOH -dok DOK -Dok DOK -domt DOMT -Domt DOMT -Dop DOP -dotCMS DOTCMS -DotSVN DOTSVN -Douw DOUW -dozd DOZD -d'OZ DOZ -Dozhd DOZHD -dp DP -D. P. DP -D.P. DP -dpon DPON -DProf DPROF -DPs DP's -D.Q. DQ -drc DRC -Dr. doctor -D. R. DR -D.R. DR -dri DRI -Dri DRI -DRIs DRI's -Driu DRIU -Driv DRIV -Drnis DRNI's -Dro DRO -Drs. doctors -Drska DRSKA -druj DRUJ -DsbA DSBA -DS&BB DS and BB -dsDNA DSDNA -ds DS -Ds D's -D. S. DS -D.S. DS -DSGi DSGI -dsi DSI -DSi DSI -dsl DSL -DSMs DSM's -dsn DSN -D&SNGRR D and SNGRR -dsu' DSU -DTCs DTC's -DTDs DTD's -D. T. DT -D.T. DT -DT&E DT and E -DT&I DT and I -dtl DTL -DTs DT's -dty DTY -D.U. DU -dva DVA -Dva DVA -DVCs DVC's -dvd DVD -DVDs DVD's -dv DV -D. V. DV -D.V. DV -dve DVE -dvfb DVFB -dvi DVI -dvije DVIJE -Dvin DVIN -Dvir DVIR -Dwa DWA -D&W D and W -D. W. DW -D.W. DW -Dwedw DWEDW -Dwi DWI -dwr DWR -dwur DWUR -dxa DXA -dx DX -D.X. DX -DXers DXER's -Dydd DYDD -dy DY -Dy DY -D. Y. DY -dypl DYPL -dyr DYR -dz DZ -dzis DZIS -Dzog DZOG -Dzor DZOR -Dzus DZU's -E. A. EA -E.A. EA -EAMs EAM's -eas EAS -Eas EA's -Eav EAV -E. B. EB -E.B. EB -Ebn EBN -E&BR E and BR -Ebru EBRU -EBWs EBW's -ecc ECC -Eccl ECCL -ec EC -Ec EC -E. C. EC -E.C. EC -ece ECE -Ece ECE -Ecem ECEM -ecg ECG -ecma ECMA -ECNs ECN's -ECP&DA's ECP and DA's -eCRM ECRM -ect ECT -ECVs ECV's -edb EDB -E. D. ED -E.D. ED -Edh EDH -edhe EDHE -EDMs EDM's -Edmx EDMX -edn EDN -Edn EDN -eds EDS -Eds ED's -Edw EDW -Eeb EEB -E. E. EE -E.E. EE -Eef EEF -Eega EEGA -eene EENE -eep EEP -Ees EE's -eeuw EEUW -Eeuw EEUW -EEZs EEZ's -efc EFC -ef EF -Ef EF -E. F. EF -E.F. EF -eFS EFS -Efs EF's -Efu EFU -Egba EGBA -egfl EGFL -e.g. for example -e. g. for example -EG&G EG and G -Egi EGI -E&GR E and GR -egy EGY -Egyl EGYL -EHAs EHA's -E. H. EH -E.H. EH -ehf EHF -Ehttp EHTTP -EiCs EIC's -E. I. EI -E.I. EI -eIF EIF -Eitr EITR -EITs EIT's -EJBs EJB's -ej EJ -E. J. EJ -E.J. EJ -E. K. EK -E.K. EK -eki EKI -Ekma EKMA -Ekow EKOW -eks EKS -Eks EK's -E. L. EL -E.L. EL -ELTs ELT's -ELUs ELU's -Embd EMBD -eMC EMC -EMDs EMD's -Emea EMEA -E&M E and M -E. M. EM -E.M. EM -Emge EMGE -emra EMRA -E&NA E and NA -Enas ENA's -EnBW ENBW -EncFS ENCFS -Encyc ENCYC -E. N. EN -E.N. EN -Enes ENE's -engl ENGL -Engl ENGL -Engr ENGR -Eni ENI -enn ENN -Enn ENN -ens ENS -Ens EN's -Enso ENSO -ENTJs ENTJ's -EoD EOD -E. O. EO -E.O. EO -Eois EOI's -eok EOK -Eole EOLE -Eol EOL -Eolss EOLS's -Eom EOM -eop EOP -eoptta EOPTTA -Eora EORA -Eorl EORL -Eors EOR's -E&P E and P -Epe EPE -ep EP -Ep EP -E. P. EP -E.P. EP -Eph EPH -Ephs EPH's -epi EPI -Epi EPI -ePO EPO -epos' EPO's -epos EPOS -Epos EPO's -E.P.s EP's -eq EQ -Eq EQ -E. R. ER -E.R. ER -ERJs ERJ's -Eru ERU -Eruv ERUV -Erv ERV -ES3 ES three -Esa ESA -Esam ESAM -Esa's ESA's -Esat ESAT -ESCs ESC's -Esd ESD -E. S. ES -E.S. ES -esi ESI -espn ESPN -ESPNhttp ESPNHTTP -esq ESQ -Esq ESQ -Esq. esquire -Esraa ESRAA -Esra ESRA -Esref ESREF -Esri ESRI -estd ESTD -Estd ESTD -esu ESU -Esva ESVA -Etad ETAD -ETBs ETB's -E. T. ET -E.T. ET -ETFs ETF's -Et'hem ETHEM -Eti ETI -etj ETJ -Eto ETO -ets ETS -Ets ET's -ETs ET's -ett ETT -Ett ETT -Ettre ETTRE -Etts ETT's -Etz ETZ -EUBs EUB's -E. U. EU -E.U. EU -eup EUP -Eurwg EURWG -Euse EUSE -Eusi EUSI -EvaGT EVAGT -E. V. EV -E.V. EV -evl EVL -Evna EVNA -evnt EVNT -EVs EV's -evv EVV -Ewa's EWA's -E. W. EW -E.W. EW -Ewha EWHA -EWOs EWO's -Ewu EWU -excl EXCL -exd EXD -E.X. EX -exhb EXHB -exh EXH -Exptl EXPTL -exsul EXSUL -E&Y E and Y -E. Y. EY -Eyk EYK -eyu EYU -Ezaa EZAA -ez EZ -Ez EZ -Ezh EZH -Ezu EZU -Ezy EZY -Faaa FAAA -faa FAA -Faa FAA -faama FAAMA -FabH FABH -FabR FABR -F.A.B.'s FAB's -Fadl FADL -fa FA -Fa FA -F. A. FA -F.A. FA -Faf FAF -Fafhrd FAFHRD -fai FAI -Fai FAI -FAIPs FAIP's -Faiq FAIQ -fajn FAJN -Fal FAL -farw FARW -fasc FASC -Fasc FASC -fas FAS -Fas FA's -FAs FA's -FasL FASL -F. B. FB -F.B. FB -F.B.G.s FBG's -FBOs FBO's -fcb FCB -FCBs FCB's -F&C F and C -fc FC -F. C. FC -F.C. FC -F.C.'s FC's -FD&C FD and C -FDCPAs FDCPA's -F. D. FD -F.D. FD -FDICs FDIC's -FdI FDI -F. E. FE -F.E. FE -FEGs FEG's -Fes FE's -ffc FFC -ff FF -F. F. FF -F.F. FF -ffm FFM -FFs FF's -F. G. FG -F.G. FG -fgk FGK -FGs FG's -F. H. FH -F.H. FH -fhm FHM -fiadh FIADH -fia FIA -FiBL FIBL -Fi'd FID -F.I F -F&I F and I -F. I. FI -F.I. FI -Figl FIGL -fija FIJA -Fio FIO -FiO FIO -F. J. FJ -F.J. FJ -F. K. FK -F.K. FK -Fla FLA -Fles FLE's -fl FL -F. L. FL -F.L. FL -fli FLI -flyr FLYR -fm FM -F. M. FM -F.M. FM -fMRI FMRI -FMs FM's -F. N. FN -F.N. FN -F. O. FO -FPDs FPD's -fp FP -F. P. FP -F.P. FP -FPGAs FPGA's -FPM&SA FPM and SA -fps FPS -FPSs FPS's -F.Q. FQ -fr FR -F. R. FR -F.R. FR -frs FRS -Fru FRU -Frwydr FRWYDR -fsf FSF -fs FS -F. s F's -Fs F's -F. S. FS -F.S. FS -fsn FSN -FSSs FSS's -FTAs FTA's -Ft. Fort -F. T. FT -F.T. FT -ftp FTP -FtsA FTSA -Ftuh FTUH -F. V. FV -F.V. FV -F&W F and W -F. W. FW -F.W. FW -fwr FWR -fx FX -Fyb FYB -F.Y. FY -Fyn FYN -fyi FYI -fyr FYR -Fyw FYW -F.Z. FZ -Gaac GAAC -G. A. GA -G.A. GA -Gbe GBE -GbE GBE -G. B. GB -G.B. GB -GB&NDR GB and NDR -G&C G and C -G. C. GC -G.C. GC -GCSEs GCSE's -GC&SF GC and SF -Gdal GDAL -Gde GDE -gd GD -G. D. GD -G.D. GD -Gdow GDOW -GDPs GDP's -gdr GDR -Gebr GEBR -Gebt GEBT -Ged GED -ge GE -G. E. GE -G.E. GE -Gek GEK -Gen. general -geq GEQ -Geu GEU -Gev GEV -GeV GEV -Gfa GFA -GF&A GF and A -G. F. GF -G.F. GF -G. G. GG -G.G. GG -Ghe GHE -G. H. GH -G.H. GH -GHGs GHG's -Ghir GHIR -ghra GHRA -GH&SA GH and SA -Giei GIEI -G. I. GI -G.I. GI -G. J. GJ -G.J. GJ -G. K. GK -G.K. GK -Gla GLA -G&L G and L -G. L. GL -G.L. GL -gli GLI -Gli GLI -Glis GLI's -glnA GLNA -Glos GLO's -gma GMA -GmbH GMBH -Gmel GMEL -gm GM -G. M. GM -G.M. GM -gmin GMIN -GMOs GMO's -GMs GM's -gmt GMT -Gmul GMUL -gnb GNB -G. N. GN -G.N. GN -GNP&BR GNP and BR -gnp GNP -GNPs GNP's -G.O. GO -GPCRs GPCR's -GPdI GPDI -G&P G and P -gp GP -G. P. GP -G.P. GP -GpIIb GPIIB -GPMGs GPMG's -GPRs GPR's -GPUs GPU's -G. Q. GQ -grac GRAC -gra GRA -Gra GRA -Grbac GRBAC -GRBs GRB's -grc GRC -GRCs GRC's -gre GRE -Gre GRE -gr GR -G. R. GR -G.R. GR -grrl GRRL -Gryf GRYF -gry GRY -Gry GRY -grz GRZ -G&S G and S -Gs G's -G. S. GS -G.S. GS -GSIs GSI's -GSOp GSOP -GTAs GTA's -gt GT -G. T. GT -G.T. GT -GTi GTI -GTs GT's -G. U. GU -G.U. GU -GUIs GUI's -G. V. GV -G.V. GV -GvpA GVPA -GWe GWE -Gwet GWET -gw GW -G. W. GW -G.W. GW -Gwi GWI -gwr GWR -Gy GY -G. Y. GY -Gyn GYN -Gyps GYP's -gyu GYU -Gyu GYU -H. A. HA -H.A. HA -H&A H and A -HBCo HBCO -HBGAs HBGA's -H. B. HB -H.B. HB -H&BR H and BR -HCEs HCE's -H. C. HC -H.C. HC -hcl HCL -HClO HCLO -HCoV HCOV -HCPs HCP's -Hcy HCY -Hdad HDAD -hDAF HDAF -HDDs HDD's -hd HD -H. D. HD -H.D. HD -hdh HDH -HDi HDI -H. E. HE -H.E. HE -hezb HEZB -Hezb HEZB -H&F H and F -H. F. HF -H.F. HF -hgcA HGCA -hg HG -H. G. HG -H.G. HG -HgO HGO -HGPs HGP's -HgU HGU -HGVs HGV's -H&H H and H -H. H. HH -H.H. HH -H. I. HI -H.I. HI -Hizb HIZB -hja HJA -H. J. HJ -H.J. HJ -Hkam HKAM -H&K H and K -H. K. HK -H.K. HK -Hla HLA -H. L. HL -H.L. HL -Hluk HLUK -hlutr HLUTR -Hly HLY -Hman HMAN -H&M H and M -hm HM -H. M. HM -H.M. HM -HMOs HMO's -hMRE HMRE -H&M's H and M's -H. N. HN -H.N. HN -H. O. HO -H.O. HO -hpc HPC -hp HP -H. P. HP -H.P. HP -H.Q. HQ -H&R H and R -H. R. HR -H.R. HR -hroa HROA -HRo HRO -hrs HRS -HRs HR's -hscy HSCY -Hsee HSEE -hs HS -Hs H's -H. S. HS -H.S. HS -hsi HSI -hSlo HSLO -HSPs HSP's -HSTs HST's -hsv HSV -hTAS HTAS -HTAs HTA's -htc HTC -ht HT -H. T. HT -H.T. HT -hti HTI -Htin HTIN -Htoo HTOO -HtrA HTRA -HTRs HTR's -http HTTP -https HTTPS -Htwa HTWA -H. U. HU -HVCs HVC's -hvcv HVCV -hvem HVEM -Hvem HVEM -H. V. HV -H.V. HV -H. W. HW -H.W. HW -hwy HWY -Hwy HWY -hxt HXT -H. Y. HY -H.Y. HY -H. Z. HZ -HZ&PC HZ and PC -iaaf IAAF -IaaS IAAS -Iaca IACA -Iacob IACOB -IACTs IACT's -Iacub IACUB -iagt IAGT -I. A. IA -I.A. IA -Iapa IAPA -iar IAR -Iar IAR -IAUCs IAUC's -Iax IAX -Ibac IBAC -Ibaes IBAE's -IBCs IBC's -IBDA'A IBDAA -Ibda IBDA -ibdal IBDAL -I. B. IB -I.B. IB -Ibm IBM -Ibne IBNE -ibn IBN -Ibra IBRA -Ibsa IBSA -Ibs IB's -iBT IBT -Ibu IBU -iby IBY -ICBMs ICBM's -Icche ICCHE -icf ICF -I&C I and C -ic IC -Ic IC -I. C. IC -I.C. IC -Ici ICI -iCN ICN -Ico ICO -IcRn ICRN -ics ICS -ICs IC's -ICTs ICT's -ICTVdB ICTVDB -ICv ICV -iCyt ICYT -Iddaa IDDAA -Idd IDD -ID'd IDD -id ID -I. D. ID -I.D. ID -Idi IDI -Idir IDIR -IDLHs IDLH's -Idm IDM -IDPs IDP's -ids IDS -IDs ID's -IDx IDX -i.e. that is -Ifa IFA -ifc IFC -IfF IFF -Ifft IFFT -ifi IFI -Ifi IFI -IFIs IFI's -IfM IFM -Ifni IFNI -IFNs IFN's -Ifop IFOP -IFRSs IFRS's -Iga IGA -IgA IGA -IGFs IGF's -'ig IG -Ig IG -I.G. IG -igi IGI -IgM IGM -ign IGN -Ign IGN -Ih IH -I. H. IH -ihi IHI -ihm IHM -ihn IHN -Ihn IHN -Ihor IHOR -Iht IHT -IIb IIB -IIc IIC -IIfx IIFX -Iiga IIGA -IIga IIGA -IIgs IIG's -IIGs IIG's -IIIb IIIB -IIIBy IIIBY -IIIc IIIC -IIId IIID -I. I. II -I.I. II -ija IJA -Ija IJA -ij IJ -I. J. IJ -Ijok IJOK -Ijui IJUI -Ikh IKH -I.K. IK -IKr IKR -Iksa IKSA -Iku IKU -Ilbe ILBE -IL&FS IL and FS -ili ILI -Ili ILI -I. L. IL -I.L. IL -Ilm ILM -ilu ILU -Ilu ILU -IMbd IMBD -imc IMC -imdb IMDB -IMDb. IMDB -IMDb IMDB -ime IME -I&M I and M -I. M. IM -I.M. IM -Iml IML -Imm IMM -Imms' IMM's -imoa IMOA -impr IMPR -Impr IMPR -iMSNs IMSN's -Imst IMST -Inba INBA -inb INB -incl INCL -ind IND -Ind IND -Infs INF's -inHg INHG -I. N. IN -I.N. IN -InlB INLB -InP INP -Inre INRE -Inspx INSPX -Ints INT's -INTs INT's -Intu INTU -inv INV -Ioba IOBA -IODs IOD's -io. IO -io IO -.I.o. IO -Io. IO -Io IO -.IO IO -I/O IO -Ioka IOKA -Iok IOK -IoM IOM -IOPs IOP's -iOS IOS -Io's IO's -I/Os IO's -I.O.'s IO's -Iosu IOSU -IoT IOT -IoW IOW -IPCs IPC's -ip IP -Ip IP -I.P. IP -Ipo IPO -IPPs IPP's -Ippu IPPU -iPPV IPPV -iPSC IPSC -iPSCs IPSC's -ipse IPSE -iPS IPS -IPs IP's -IPv IPV -iQ IQ -I.Q. IQ -Irgm IRGM -Irig IRIG -Iril IRIL -i'r IR -ir IR -Ir IR -I. R. IR -I.R. IR -Irla IRLA -Irmis IRMI's -iro IRO -Iro IRO -Irra IRRA -IRs IR's -iru IRU -Iru IRU -ISAv ISAV -Isba ISBA -isbn ISBN -ISBNs ISBN's -isCf ISCF -iSC ISC -Isc ISC -Isd ISD -isdn ISDN -Isgec ISGEC -ishq ISHQ -Ishq ISHQ -I. S. IS -I.S. IS -Isl ISL -ISMNs ISMN's -ISPs ISP's -Isra'il ISRAIL -Isra ISRA -iss ISS -Iss IS's -ISSNs ISSN's -Isu ISU -Ite ITE -ITHs ITH's -iti ITI -Iti ITI -I.T. IT -itk ITK -Itk ITK -ITNNs ITNN's -Itoi ITOI -iTP ITP -Itse ITSE -itt ITT -Itu ITU -Itz ITZ -Iucn IUCN -IUDs IUD's -Iuz IUZ -I. V. IV -I.V. IV -IVs IV's -Ivu IVU -Iwas IWA's -Iwaz IWAZ -IWBs IWB's -iwi IWI -iw IW -I. W. IW -iwrg IWRG -iwspy IWSPY -Iwuh IWUH -IXb IXB -IXBs IXB's -IXCs IXC's -IXe IXE -iXL IXL -ixtle IXTLE -Iya IYA -Iyar IYAR -iyem IYEM -Iyi IYI -iy IY -I. Y. IY -I.Y. IY -Jaf JAF -J. A. JA -J&B J and B -jb JB -J. B. JB -J.B. JB -jcis JCIS -jc JC -J. C. JC -J.C. JC -JCRs JCR's -JdeBP JDEBP -jdi JDI -jd JD -J. D. JD -J.D. JD -jdk JDK -J&D's J and D's -J.D.s JD's -J. E. JE -J.E. JE -jf JF -J. F. JF -J.F. JF -J. G. JG -J.G. JG -jh JH -J. H. JH -J.H. JH -JHs JH's -JHSVs JHSV's -JHud JHUD -J. I. JI -J.I. JI -J&J J and J -J. J. JJ -J.J. JJ -Jka JKA -jkd JKD -J&K J and K -J. K. JK -J.K. JK -jkx JKX -J. L. JL -J.L. JL -J. M. JM -J.M. JM -jnb JNB -J. N. JN -J.N. JN -Jno JNO -jnr JNR -J. O. JO -J.O. JO -jpg JPG -J&P J and P -jp JP -J.P. JP -J.Q. JQ -J&R J and R -J. R. JR -J.R. JR -Jr. junior -jr. junior -Jr junior -jr junior -J.R.'s JR's -jsb JSB -Js J's -J. S. JS -J.S. JS -J&T J and T -J. T. JT -J.T. JT -JT&KW JT and KW -J. U. JU -J&V J and V -J. V. JV -J.V. JV -Jwa JWA -J. W. JW -J.W. JW -jx JX -J. Y. JY -J.Y. JY -Jym JYM -Jymn JYMN -J. Z. JZ -J.Z. JZ -K. A. KA -K.A. KA -kbi KBI -K&B K and B -K. B. KB -K.B. KB -K. C. KC -K.C. KC -K.C.'s KC's -kDa KDA -kdal KDAL -K&D K and D -kd KD -K. D. KD -K.D. KD -K. D.'s KD's -K.D.'s KD's -ke KE -Ke KE -K. E. KE -K.E. KE -kfc KFC -K.F. KF -kgr KGR -kgt KGT -KgU KGU -kgv KGV -Khizr KHIZR -K. H. KH -K.H. KH -Khlav KHLAV -Khmu KHMU -Khri KHRI -Khru KHRU -K. I. KI -K.I. KI -Kjer KJER -kj KJ -K. J. KJ -K.J. KJ -Kjop KJOP -KJo's KJO's -K. K. KK -K.K. KK -Kle KLE -kl KL -K. L. KL -K.L. KL -K&M K and M -km KM -K. M. KM -K.M. KM -K&N K and N -kn KN -K. N. KN -K.N. KN -k'o KO -K. O. KO -K.O. KO -KPIs KPI's -K&P K and P -K. P. KP -K.P. KP -Kppen KPPEN -kptm KPTM -krc KRC -KRCs KRC's -K.R.I.T.'s KRIT's -Krka KRKA -kr KR -K. R. KR -K.R. KR -Krne KRNE -kroz KROZ -Kroz KROZ -Kru KRU -ksa KSA -kset KSET -Kseur KSEUR -Ksevt KSEVT -ks KS -K. S. KS -K.S. KS -Ktav KTAV -KT&K KT and K -kt KT -K. T. KT -K.T. KT -Kuaa KUAA -Kud KUD -kuih KUIH -K. U. KU -K.U. KU -Kutb KUTB -Kvik KVIK -Kvit KVIT -K. V. KV -K.V. KV -Kvyat KVYAT -kwa KWA -kwe KWE -Kyse KYSE -kyt KYT -Kyt KYT -kz KZ -K. Z. KZ -L. A. LA -L.A.'s LA's -Lay's LAY's -LBi LBI -L. B. LB -L.B. LB -LBPs LBP's -LCAs LCA's -lcc LCC -LCCs LCC's -LC&DR LC and DR -LCDs LCD's -LCIs LCI's -L. C. LC -L.C. LC -lcn LCN -LCSs LCS's -LCTs LCT's -LCVPs LCVP's -Lda LDA -LDCs LDC's -L. D. LD -L.D. LD -L. E. LE -L.E. LE -LFFCs LFFC's -L. F. LF -L.F. LF -LFs LF's -LFSRs LFSR's -L. G. LG -L.G. LG -LGMs LGM's -LG&RDD LG and RDD -LGs LG's -LGUs LGU's -LGVs LGV's -Lha LHA -L. H. LH -L.H. LH -L. I. LI -L.I. LI -Lje LJE -L. J. LJ -L.J. LJ -L. K. LK -lks LKS -Llapi LLAPI -lle LLE -lli LLI -L&L L and L -L. L. LL -L.L. LL -llp LLP -llu LLU -LMGs LMG's -lm LM -L. M. LM -L.M. LM -LMPs LMP's -LMQs LMQ's -LnAIB LNAIB -L&N L and N -L. N. LN -L.N. LN -L.O.C.'s LOC's -Lokk LOKK -l'OL LOL -L. O. LO -L.O. LO -LPARs LPAR's -L&P L and P -lp LP -L. P. LP -L.P. LP -L&PM L and PM -LPMud LPMUD -LPs LP's -LPThe LPTHE -lr LR -L. R. LR -L.R. LR -LRTs LRT's -LRVs LRV's -LSDs LSD's -LSi LSI -ls LS -Ls L's -L. S. LS -L.S. LS -LSTs LST's -LSVCCs LSVCC's -Ltda LTDA -Ltd. limited -L&T L and T -Lt. lieutenant -lt LT -L. T. LT -L.T. LT -ltoh LTOH -L. U. LU -lv LV -L. V. LV -L.V. LV -L. W. LW -L.W. LW -L.Y. LY -L&YR L and YR -M. A. MA -M.A. MA -M&A M and A -Mbewu MBEWU -mbi MBI -M. B. MB -M.B. MB -mBo MBO -Mbre MBRE -mbr MBR -MCCs MCC's -M. C. MC -M.C. MC -MCs MC's -mcyG MCYG -mdb MDB -MDCs MDC's -mdDA MDDA -MD&DI MD and DI -Mde MDE -MDGs MDG's -MDHUs MDHU's -mDia MDIA -MDic MDIC -mdla MDLA -md MD -M. D. MD -M.D. MD -mdr MDR -M. E. ME -M.E. ME -MFDs MFD's -MFe MFE -M. F. MF -M.F. MF -MFTs MFT's -Mgadla MGADLA -Mgal MGAL -mga MGA -Mga MGA -Mgbo MGBO -MGen MGEN -M&G M and G -M. G. MG -M.G. MG -M&GN M and GN -M&GR's M and GR's -Mha MHA -MHCs MHC's -MH&L MH and L -M&H M and H -M. H. MH -M.H. MH -Mhor MHOR -Mhow MHOW -M.I.A.'s MIA's -M&I M and I -M. I. MI -M.I. MI -M&J M and J -M. J. MJ -M.J. MJ -M. K. MK -M.K. MK -MKs MK's -MLAs MLA's -mlc MLC -MLCs MLC's -mlg MLG -mli MLI -Mlle MLLE -M&L M and L -M. L. ML -M.L. ML -MLPs MLP's -MM&A MM and A -M&M M and M -M. M. MM -M.M. MM -M&M's M and M's -MNCs MNC's -M&NF M and NF -M. N. MN -M.N. MN -M.O.G.U.E.R.A.'s MOGUERA's -M. O. MO -M.O. MO -MpA MPA -MPAs MPA's -MPBu MPBU -MP&I MP and I -MPi MPI -MPLMs MPLM's -M&P M and P -M. P. MP -M.P. MP -MPPs MPP's -M.P.'s MP's -MPs MP's -MPThe MPTHE -Mpu MPU -M. Q. MQ -Mra MRA -MRBs MRB's -mre MRE -MR&LE MR and LE -Mr. mister -M. R. MR -M.R. MR -mRNA MRNA -mRNAs MRNA's -Mrs. misses -MRTs MRT's -msd MSD -Mse MSE -M&S M and S -Ms. miss -M. S. MS -M.S. MS -MSPs MSP's -mst MST -Mta MTA -MTAs MTA's -M&T M and T -M. T. MT -M.T. MT -Mtor MTOR -mtvU MTVU -MTVu MTVU -M. U. MU -M.U.s MU's -MVMs MVM's -M. V. MV -M.V. MV -MVPs MVP's -MVs MV's -MWe MWE -M. W. MW -M.W. MW -M. X. MX -myb MYB -myc MYC -Myc MYC -MyDD MYDD -M. Y. MY -M.Y. MY -MySQL MYSQL -M. Z. MZ -N. A. NA -N.A. NA -N.B.A.'s NBA's -NBAs NBA's -N. B. NB -N.B. NB -nbs NBS -NBTwo NBTWO -NCAAs NCAA's -NCCs NCC's -N. C. NC -N.C. NC -NCOs NCO's -N. D. ND -N.D. ND -N. E. NE -N.E. NE -ner NER -NFATc NFATC -nfed NFED -N. F. NF -N.F. NF -Nge NGE -N. G. NG -N.G. NG -NGOs NGO's -Nha NHA -NHCEs NHCE's -NHCs NHC's -nhi NHI -NHM&W NHM and W -N. H. NH -N.H. NH -NHPs NHP's -N. I. NI -N.I. NI -N'I NI -N. J. NJ -N.J. NJ -Njoo NJOO -N.J.'s NJ's -N. K. NK -N.K. NK -nkvd NKVD -nkv NKV -N. L. NL -N.L. NL -NLRs NLR's -N. M. NM -N.M. NM -N. N. NN -N.N. NN -NPCs NPC's -NPMs NPM's -N. P. NP -N.P. NP -NPs NP's -N. Q. NQ -nri NRI -Nri NRI -NRIs NRI's -NRJs NRJ's -nr NR -N. R. NR -N.R. NR -N. S. NS -N.S. NS -NSOs NSO's -Nta NTA -ntb NTB -N. T. NT -N.T. NT -ntw NTW -N.U. NU -NvDA's NVDA's -N. V. NV -N.V. NV -nwa NWA -N&W N and W -N. W. NW -N.W. NW -nyc NYC -N. Y. NY -N.Y. NY -N'Zif NZIF -NZiK NZIK -N'Zi NZI -N.Z. NZ -O. A. OA -O.A. OA -obl OBL -Oblt OBLT -O.B. OB -O. C. OC -O.C. OC -Octl OCTL -O. D. OD -O.E. OE -O.F. OF -ofr OFR -O&G O and G -O. G. OG -O.G. OG -O. H. OH -O.H. OH -O. I. OI -O.I. OI -O. J. OJ -O.J. OJ -OK'd OKD -OKd OKD -oke OKE -Oke OKE -O&K O and K -O. K. OK -O.K. OK -O. L. OL -O.L. OL -Olo OLO -Olov OLOV -ols OLS -OLs OL's -olvwm OLVWM -olwm OLWM -O&MFL O and MFL -omg OMG -OMGs OMG's -O. M. OM -O.M. OM -OmOm OMOM -Om's OM's -oncu ONCU -ond OND -onf ONF -O. N. ON -O.N. ON -O&O O and O -O. O. OO -O.O. OO -Oop OOP -oor OOR -oose OOSE -Oo's OO's -Ootw OOTW -OPMs OPM's -O. P. OP -O.P. OP -opr OPR -Opr OPR -ORFs ORF's -OR&N OR and N -O. R. OR -osaa OSAA -osa OSA -Osa OSA -OSBs OSB's -O. S. OS -O.S. OS -OSTs OST's -osv OSV -O.T. OT -OTs OT's -Otu OTU -otv OTV -O.U. OU -ovca OVCA -Ovca OVCA -Ovda OVDA -Ovo OVO -ov OV -Ov OV -O. V. OV -O.V. OV -OWCs OWC's -O. W. OW -O.W. OW -pa PA -P. A. PA -P.A. PA -pBCE PBCE -pbc PBC -pb PB -P. B. PB -P.B. PB -PCBs PCB's -PCDDs PCDD's -PCeU PCEU -PCIe PCIE -pci PCI -pcl PCL -PcoA PCOA -pc PC -P. C. PC -P.C. PC -PCRev PCREV -pcs PCS -P.C.s PC's -pDAB PDAB -PDAs PDA's -pdbp PDBP -PDBsum PDBSUM -PDCs PDC's -PD&D PD and D -PDEs PDE's -pdf PDF -PDFs PDF's -P&D P and D -P. D. PD -P.D. PD -PDPs PDP's -PDs PD's -P.E.I.'s PEI's -P. E. PE -P.E. PE -PFs PF's -PFW&C PFW and C -PGMs PGM's -PG&N PG and N -P&G P and G -P. G. PG -P.G. PG -Phlo PHLO -P&H P and H -P. H. PH -P.H. PH -php PHP -PHPs PHP's -P&I P and I -P. I. PI -P.I. PI -Piz PIZ -pj PJ -P. J. PJ -P.J. PJ -PJs PJ's -P. K. PK -P.K. PK -plc PLC -PLCs PLC's -P. L. PL -P.L. PL -PMMoV PMMOV -p.m. PM -p.m PM -P. M. PM -P.M. PM -PMs PM's -PNaCl PNACL -pna PNA -Pnau PNAU -pnb PNB -PNEs PNE's -PNETs PNET's -pneus PNEUS -png PNG -PNNs PNN's -PNoy PNOY -pn PN -P. N. PN -P.N. PN -P.O.D.'s POD's -P&O P and O -P. O. PO -P.O. PO -P.O.W.'s POW's -P&PH P and PH -PPi PPI -P. P. PP -P.P. PP -PPVs PPV's -PPy PPY -pr PR -P. R. PR -P.R. PR -P. s P's -P.'s P's -Ps P's -P. S. PS -P.S. PS -PSSAs PSSA's -pTA PTA -PTAs PTA's -Pte PTE -PTEs PTE's -ptf PTF -Ptie PTIE -PTLs PTL's -Ptol PTOL -pt PT -P. T. PT -P.T. PT -PTTs PTT's -P'Twa PTWA -Puiu PUIU -Pul PUL -PVAs PVA's -PvdA PVDA -pve PVE -P. V. PV -P.V. PV -pvr PVR -PVs PV's -pwll PWLL -P&W P and W -P. W. PW -P.W. PW -Pyi PYI -Pyk PYK -Pyl PYL -PyL PYL -P. Y. PY -Pyu PYU -Pyw PYW -Pyx PYX -P. Z. PZ -Q. A. QA -Q&A Q and A -Q&A's Q and A's -Q&As Q and A's -QbA QBA -Q.B. QB -Q.C. QC -Q. E. QE -Q.H. QH -Q.I. QI -Q. J. QJ -Q. N. QN -QPOs QPO's -Q.V. QV -R. A. RA -R.A. RA -R&A R and A -R&AW R and AW -RbAg RBAG -RBCs RBC's -RBIs RBI's -R&B R and B -R. B. RB -R.B. RB -rca RCA -R&C R and C -rc RC -R. C. RC -R.C. RC -RCTs RCT's -RdE RDE -R&D R and D -R. D. RD -R.D. RD -R.E.M.'s REM's -REPLs REPL's -R. E. RE -R.E. RE -R. F. RF -R.F. RF -R&G R and G -R. G. RG -R.G. RG -RHIBs RHIB's -Rhiw RHIW -rhl RHL -RhoG RHOG -Rho's RHO's -R. H. RH -R.H. RH -R&I R and I -R. I. RI -R.I. RI -riu RIU -Riu RIU -rivs RIVS -Rivu RIVU -Rixt RIXT -rjf RJF -R. J. RJ -R.J. RJ -R. K. RK -R.K. RK -R. L. RL -R.L. RL -rly RLY -RMDs RMD's -RMLs RML's -rm RM -R. M. RM -R.M. RM -RMSDs RMSD's -RMs RM's -rna RNA -RNAs RNA's -Rnet RNET -Rnic RNIC -RNNs RNN's -RNPs RNP's -R. N. RN -R.N. RN -rOmpB ROMPB -Rooi ROOI -R. O. RO -R.O. RO -ROVs ROV's -Roxb ROXB -Roxx ROXX -RP&C RP and C -rpc RPC -Rpe RPE -rpg RPG -RPGs RPG's -rpm RPM -R. P. RP -R.P. RP -RPs RP's -R. Q. RQ -R.Q. RQ -rra RRA -rrd RRD -Rreli RRELI -rre RRE -rRNA RRNA -rRNAs RRNA's -R&R R and R -rr RR -R. R. RR -R.R. RR -rsh RSH -Rsis RSI's -rsly RSLY -RsmA RSMA -RSpec RSPEC -rs RS -Rs R's -R. S. RS -R.S. RS -RSu RSU -rtb RTB -RTCs RTC's -RTEjr RTEJR -RTeOR RTEOR -RTOs RTO's -rtPA RTPA -rt RT -R. T. RT -R.T. RT -rts RTS -RTVFBiH RTVFBIH -Ruao RUAO -Rukn RUKN -Ruk RUK -Rupf RUPF -Rupr RUPR -rup RUP -Rup RUP -R. U. RU -R.U. RU -RutB RUTB -Ruu RUU -Ruwa RUWA -Ruy RUY -Ruyt RUYT -Rvat RVAT -Rvo RVO -R. V. RV -R.V. RV -rvs RVS -rwa RWA -Rwa RWA -rwb RWB -RWEs RWE's -rwjf RWJF -R. W. RW -R.W. RW -Rxa RXA -rz RZ -R. Z. RZ -Sa'id SAID -S. A. SA -S.A. SA -Saxl SAXL -Sa'yo SAYO -Sbai SBAI -sbc SBC -SBCs SBC's -sbk SBK -Sborz SBORZ -sb SB -S. B. SB -S.B. SB -Sbu SBU -scr SCR -SCRs SCR's -scry SCRY -sc SC -S. C. SC -S.C. SC -Scuf SCUF -Scymn SCYMN -SD&AE SD and AE -Sdei SDEI -sde SDE -Sde SDE -SDG&E SDG and E -sdk SDK -S. D. SD -S.D. SD -SDSM&T's SDSM and T's -Sejms SEJM's -sejr SEJR -Sekl SEKL -Sek SEK -S. E. SE -S.E. SE -ses SES -Ses SE's -SEs SE's -S.E.S.'s SES's -Seyh SEYH -sfadb SFADB -Sfax SFAX -SFFCo SFFCO -sfn SFN -'sf SF -sf SF -S. F. SF -S.F. SF -sfs SFS -S.F.'s SF's -SG&A SG and A -sgb SGB -Sgip SGIP -sgml SGML -sgra SGRA -S. G. SG -S.G. SG -SGSNs SGSN's -Sgt. sergeant -sgt SGT -S.H.I.E.L.D.'s SHIELD's -Shma SHMA -S. H. SH -S.H. SH -S. I. SI -S.I. SI -sjef SJEF -Sjon SJON -S. J. SJ -S.J. SJ -S. K. SK -S.K. SK -S.L.A.A.'s SLAA's -sla SLA -SLAs SLA's -S&L S and L -SLS&E SLS and E -S. L. SL -S.L. SL -SMe SME -SMEs SME's -SmI SMI -SMPSs SMPS's -smr SMR -S&M S and M -sm SM -S. M. SM -S.M. SM -S.M.'s SM's -SMs SMS -Smyl SMYL -SNESjr SNESJR -sngle SNGLE -SNPs SNP's -SnSe SNSE -S. N. SN -S.N. SN -S. O. SO -S.O. SO -Sos SO's -Sovn SOVN -Sov SOV -S&P 500 S and P five hundred -SPCAs SPCA's -Spe SPE -Spoa SPOA -SpPIn SPPIN -S&P S and P -S. P. SP -S.P. SP -SPs SP's -SPUs SPU's -sql SQL -sq SQ -S. Q. SQ -Sra SRA -SRAs SRA's -Srba SRBA -Srbi SRBI -SRBs SRB's -SRGs SRG's -Srhir SRHIR -SRLGs SRLG's -SRMs SRM's -Srni SRNI -srp SRP -sr SR -S. R. SR -S.R. SR -Srul SRUL -srx SRX -SSAs SSA's -SSDs SSD's -ssl SSL -SSoSV SSOSV -SSRIs SSRI's -S&S S and S -ss SS -S. s S's -Ss S's -S. S. SS -S.S. SS -Ssu SSU -ssw SSW -Ssy SSY -ST&AJ ST and AJ -STDs STD's -stfv STFV -STGs STG's -STIs STI's -STi STI -stl STL -Stryj STRYJ -S&T S and T -STScI STSCI -S. T. ST -S.T. ST -suo SUO -Suo SUO -S. U. SU -Susz SUSZ -SUTs SUT's -Suu SUU -SUVs SUV's -Suy SUY -Svac SVAC -svar SVAR -sve SVE -Sve SVE -SVMs SVM's -svn SVN -svom SVOM -sv SV -S. V. SV -S.V. SV -SVTs SVT's -Swe SWE -SwRI SWRI -S&W S and W -sw SW -S. W. SW -S.W. SW -sx SX -S.X. SX -SysML SYSML -SysRq SYSRQ -Sys SY's -SysV SYSV -S. Y. SY -S.Y. SY -SyT SYT -syv SYV -Syxx SYXX -Szasz SZASZ -Szer SZER -szkic SZKIC -Szklo SZKLO -Szlak SZLAK -SzMME SZMME -Szpir SZPIR -sz SZ -S. Z. SZ -Szu SZU -Szyk SZYK -taf TAF -Taf TAF -T. A. TA -T.A. TA -T&A T and A -TBCs TBC's -TBMs TBM's -tbh TBH -T. B. TB -T.B. TB -TCiAP TCIAP -TCKs TCK's -tc TC -T. C. TC -T.C. TC -TCUs TCU's -tcu TCU -tdb TDB -TDCi TDCI -TdIF TDIF -TDs TD's -T. D. TD -T.D. TD -Teatr TEATR -T. E. TE -T.E. TE -T&F T and F -tf TF -T. F. TF -T.F. TF -tge TGE -tgf TGF -tg TG -T. G. TG -T.G. TG -TGVs TGV's -tgv TGV -Thok THOK -ThSe THSE -T. H. TH -T.H. TH -T.I.'s TI's -T.I. TI -Tiu TIU -tiv TIV -Tiv TIV -Tiy's TIY's -T. J. TJ -T.J. TJ -t'ju TJU -tjz TJZ -tko TKO -Tko TKO -tk TK -T. K. TK -T.K. TK -Tlas TLA's -TlCu TLCU -TLDs TLD's -tli TLI -tlp TLP -TLRs TLR's -TLs TL's -tl TL -T. L. TL -T.L. TL -Tluk TLUK -Tluszcz TLUSZCZ -tmc TMC -TMGs TMG's -tmos TMOS -tmRNA TMRNA -TMSs TMS's -T.M.s TM's -tm TM -T. M. TM -T.M. TM -tna TNA -tnbc TNBC -TNTAs TNTA's -tn TN -T. N. TN -T.N. TN -T.O.'s TO's -T. O. TO -T.O. TO -tou TOU -Tou TOU -ToU TOU -tPA TPA -tpr TPR -tp TP -T. P. TP -T.P. TP -TPVs TPV's -T.Q. TQ -tra TRA -Tra TRA -Trbic TRBIC -Trcek TRCEK -TrkA TRKA -Trmcic TRMCIC -tRNA TRNA -TRPs TRP's -trs TRS -tr TR -T. R. TR -T.R. TR -truTV TRUTV -TruTV TRUTV -Trve TRVE -Tsa TSA -tsit TSIT -Tsiv TSIV -tso TSO -Tso TSO -tsr TSR -Ts T's -T. S. TS -T.S. TS -tsus TSUS -Tsvi TSVI -Tta TTA -tteok TTEOK -tte TTE -ttp TTP -T&T T and T -T. T. TT -T.T. TT -T'uqu TUQU -T.U. TU -Tuzk TUZK -tvaan TVAAN -Tvam TVAM -tva TVA -TVETs TVET's -TVii TVII -TVIn TVIN -TVi TVI -tvo TVO -Tvo TVO -TVRi TVRI -tvr TVR -TVSpy TVSPY -tvs TVS -TVs TV's -tVTA TVTA -tv TV -T. V. TV -T.V. TV -Twa TWA -Twi TWI -TWTs TWT's -T. W. TW -T.W. TW -TxDOT TXDOT -T.X. TX -tya TYA -Tyk TYK -Tza'ar TZAAR -Tze TZE -Tzrif TZRIF -Tzuh TZUH -Tzvi TZVI -Ua's UA's -UAs UA's -uat UAT -U.A. UA -UAVs UAV's -Uba UBA -UbcM UBCM -Ube UBE -ubi UBI -Ubi UBI -Ucar UCAR -Uca UCA -Ucmak UCMAK -Ucn UCN -uCs UC's -Uc UC -U.C. UC -Ucuncu UCUNCU -Uczta UCZTA -Uda UDA -Udit UDIT -Udny UDNY -UDTs UDT's -ud UD -Ud UD -U. D. UD -udu UDU -Udu UDU -Ueda's UEDA's -Uéda UE acute DA -U. E. UE -UFOs UFO's -Ufot UFOT -ufo UFO -Ufo UFO -U. F. UF -Uga UGA -Uge UGE -Ugni UGNI -Ugra UGRA -Ugrszke UGRSZKE -Ug UG -Uhha UHHA -uhn UHN -UHTCs UHTC's -Uhud UHUD -U.H. UH -uhur UHUR -Uiam UIAM -Uibh UIBH -Uible UIBLE -Uig UIG -uisae UISAE -UiS UIS -UiTM UITM -uit UIT -Uit UIT -Uiy UIY -Ujed UJED -Ujsag UJSAG -uj UJ -U.J. UJ -Uka UKA -Ukhra UKHRA -Ukic UKIC -Uki UKI -Ukoh UKOH -Uko UKO -ukr UKR -Ukr UKR -Ukui UKUI -u'k UK -uk UK -Uk UK -U.K. UK -Ukwu UKWU -Ulic ULIC -Ull ULL -Ulms ULM's -UlSU ULSU -Uluj ULUJ -U.L. UL -ulus ULUS -Ulus ULU's -Ulwa ULWA -Ulwe ULWE -Umbr UMBR -umelcu UMELCU -UMe UME -Umla UMLA -Umme UMME -Umno UMNO -Umri UMRI -U. M. UM -U.M. UM -Unli UNLI -Unlu UNLU -unm UNM -unnd UNND -UNSh UNSH -Unst UNST -Uns UN's -U.N.'s UN's -Unt UNT -U.N. UN -Unz UNZ -UOCl UOCL -UofM UOFM -Uoho UOHO -UoMs UOM's -uORF UORF -UoW UOW -UPBs UPB's -Upd UPD -Upf UPF -Upir UPIR -UPnP UPNP -Uppu UPPU -U.P. UP -UPyD UPYD -uq UQ -Urbz URBZ -URCs URC's -Urdd URDD -Urei UREI -urf URF -Urla URLA -URLhttp URLHTTP -URLs URL's -Usal USAL -usan USAN -Usan USAN -usao USAO -usata USATA -usa USA -.USA USA -USAya USAYA -USBs USB's -Uscie USCIE -U.S.C.'s USC's -USDoE USDOE -usd USD -USFbA USFBA -USF&WS USF and WS -UShs USH's -Usia USIA -usih USIH -Uslu USLU -Usmar USMAR -Usna USNA -Usnic USNIC -Usoi USOI -Usos USO's -Uso USO -USPs USP's -U.S.'s US's -Usti USTI -Ustka USTKA -Usui USUI -usum USUM -U. S. US -U.S. US -Usut USUT -Usvit USVIT -Uta's UTA's -uta UTA -Uta UTA -Utca UTCA -Utd's UTD's -Utd UTD -utea UTEA -Utes UTE's -Uth UTH -uti UTI -Uti UTI -Utne UTNE -Utnur UTNUR -Uto UTO -utrci UTRCI -utsav UTSAV -Utsav UTSAV -Utsu UTSU -U.T. UT -Utu UTU -Utva UTVA -Uuh UUH -Uul UUL -Uulu UULU -Uusi UUSI -UUs UU's -Uuto UUTO -Uvac UVAC -Uvea UVEA -uvnitr UVNITR -Uvo UVO -U.V. UV -Uwasa UWASA -UWFi UWFI -V. A. VA -V.A. VA -V&A V and A -Vav VAV -Vay VAY -vb VB -V. B. VB -V.B. VB -VCDs VCD's -VCRs VCR's -VCSELs VCSEL's -VCs VC's -VCTs VCT's -vc VC -V. C. VC -V.C. VC -vda VDA -Vda VDA -VDCs VDC's -vdiq VDIQ -vdm VDM -V. D. VD -V.D. VD -vez VEZ -Vez VEZ -vfp VFP -vfr VFR -V. F. VF -V.F. VF -V'Ger VGER -vgmdb VGMDB -VGo VGO -VGSCs VGSC's -VGSoM VGSOM -V. G. VG -V.G. VG -V. H. VH -V.I.C.'s VIC's -Vict VICT -viita VIITA -vijf VIJF -vij VIJ -Vij VIJ -V.I.P.s VIP's -VIPs VIP's -V. I. VI -V.I. VI -VJs VJ's -V. J. VJ -V.J. VJ -V. K. VK -V.K. VK -Vlah VLAH -VLCCs VLCC's -vlei VLEI -Vlijt VLIJT -V. L. VL -V.L. VL -VMAs VMA's -vm VM -V. M. VM -V.M. VM -vner VNER -V. N. VN -V.N. VN -V.O. VO -Vov VOV -Voz VOZ -vpis VPIS -VPNs VPN's -vpu VPU -V. P. VP -V.P. VP -vq VQ -vrak VRAK -Vrba VRBA -Vrbuv VRBUV -Vrej VREJ -vrem VREM -Vrin VRIN -vrj VRJ -vrn VRN -vroee VROEE -vrou VROU -vrouw VROUW -Vrouw VROUW -Vrsac VRSAC -Vrtis VRTI's -V&R V and R -vr VR -V. R. VR -V.R. VR -VSANs VSAN's -VSATs VSAT's -Vsekh VSEKH -vse VSE -vso VSO -VSPs VSP's -vs. versus -_vs._ versus -vsyo VSYO -VTE VT eL -Vtic VTIC -VTi VTI -V&T's V and T's -V&T V and T -V.T. VT -Vuur VUUR -VVIPs VVIP's -V. V. VV -V.V. VV -VWs VW's -Vyg VYG -vyr VYR -vy VY -vz VZ -V. Z. VZ -Waay WAAY -Wa'il WAIL -Wakf WAKF -wa'l WAL -waqf WAQF -waqt WAQT -Waqt WAQT -Wasl WASL -Watfa WATFA -wau WAU -Wau WAU -W. A. WA -W.A. WA -waw WAW -waza WAZA -Waza WAZA -WBs WB's -W. B. WB -W.B. WB -W&CBR W and CBR -wc WC -W. C. WC -W.C. WC -W. D. WD -W.D. WD -W. E. WE -W.E. WE -WF&NW WF and NW -W&F W and F -W. F. WF -W.F. WF -wga WGA -wgn WGN -wg WG -W. G. WG -W.G. WG -W&H W and H -W. H. WH -W.H. WH -Whyld WHYLD -Wica WICA -wici WICI -Wif WIF -Wijk WIJK -Wiwa WIWA -W. I. WI -wjaz WJAZ -W&J's W and J's -W&J W and J -W. J. WJ -W.J. WJ -W. K. WK -W.K. WK -W&LE W and LE -Wley WLEY -wlrs WLRS -W. L. WL -W.L. WL -wml WML -W&M W and M -W. M. WM -W.M. WM -W. N. WN -W.N. WN -wnzaa WNZAA -W&OD W and OD -Worh WORH -W. O. WO -W.O. WO -wpt WPT -wp WP -W. P. WP -W.P. WP -Wrec WREC -W. R. WR -W.R. WR -ws WS -W. S. WS -W.S. WS -WTs WT's -WTTs WTT's -W. T. WT -W.T. WT -WUAs WUA's -Wudl WUDL -Wuhr WUHR -Wuhu WUHU -Wu's WU's -wuv WUV -W. U. WU -Wuz WUZ -WVa WVA -W. V. WV -W.V. WV -Wwe WWE -W. W. WW -W.W. WW -wx WX -W.Y. WY -wyzc WYZC -W. Z. WZ -xbg XBG -Xbra XBRA -xCo XCO -X. C. XC -xda XDA -XDRs XDR's -X. D. XD -Xfce XFCE -xf XF -X.F. XF -xh XH -X. H. XH -xk XK -xmc XMC -xml XML -X.M. XM -xO XO -X.O. XO -XPe XPE -X.Q. XQ -XSi XSI -xsr XSR -XTwas XTWA's -xt XT -X. W. XW -xy XY -xyz XYZ -xyZ XYZ -Y. A. YA -Y.A. YA -Y. B. YB -Y.B. YB -ycia YCIA -ycie YCIE -yc YC -Y. C. YC -Y.C. YC -Ydby YDBY -Yde YDE -YdiB YDIB -yd YD -Y. D. YD -Y.D. YD -Y.E. YE -YFCs YFC's -yfle YFLE -YF&R YF and R -yg YG -Y. G. YG -Y.G. YG -Y. H. YH -Y. I. YI -Y.I. YI -yj YJ -Y. J. YJ -Ykt YKT -Y. K. YK -Y.K. YK -Yle YLE -yl YL -Y. L. YL -Ymke YMKE -ym YM -Y. M. YM -yndi YNDI -Yndi YNDI -yne YNE -Yngve YNGVE -Ynis YNI's -yn YN -Yn YN -Y. N. YN -Y.N. YN -Y.O. YO -ypa YPA -Y.P. YP -Y. Q. YQ -yra YRA -Yra YRA -Y&R Y and R -YSK'da YSKDA -YSK'ya YSKYA -Ys Y's -Y.S. YS -yta YTA -Ytre YTRE -Y&T Y and T -Y. T. YT -Y.T. YT -Y. V. YV -Y. W. YW -Y.Y. YY -Z. A. ZA -Z.A. ZA -Zdar ZDAR -zda ZDA -zg ZG -Z.G. ZG -zh ZH -Z. H. ZH -Z.H. ZH -zijn ZIJN -zij ZIJ -Zij ZIJ -Z. I. ZI -Z. J. ZJ -Z.K. ZK -Z.L. ZL -Z.M. ZM -ZnO ZNO -Zpav ZPAV -Z.P. ZP -ZrI ZRI -Zsuzsa ZSUZSA -Z.W. ZW -Z.X. ZX -Z. Y. ZY -Z.Y. ZY -Z. Z. ZZ -Z.Z. ZZ diff --git a/nemo_text_processing/text_normalization/en/graph_utils.py b/nemo_text_processing/text_normalization/en/graph_utils.py deleted file mode 100644 index 33c512f24109..000000000000 --- a/nemo_text_processing/text_normalization/en/graph_utils.py +++ /dev/null @@ -1,196 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import string -from pathlib import Path -from typing import Dict - -import pynini -from nemo_text_processing.text_normalization.en.utils import get_abs_path -from pynini import Far -from pynini.examples import plurals -from pynini.export import export -from pynini.lib import byte, pynutil, utf8 - -NEMO_CHAR = utf8.VALID_UTF8_CHAR - -NEMO_DIGIT = byte.DIGIT -NEMO_LOWER = pynini.union(*string.ascii_lowercase).optimize() -NEMO_UPPER = pynini.union(*string.ascii_uppercase).optimize() -NEMO_ALPHA = pynini.union(NEMO_LOWER, NEMO_UPPER).optimize() -NEMO_ALNUM = pynini.union(NEMO_DIGIT, NEMO_ALPHA).optimize() -NEMO_HEX = pynini.union(*string.hexdigits).optimize() -NEMO_NON_BREAKING_SPACE = u"\u00A0" -NEMO_SPACE = " " -NEMO_WHITE_SPACE = pynini.union(" ", "\t", "\n", "\r", u"\u00A0").optimize() -NEMO_NOT_SPACE = pynini.difference(NEMO_CHAR, NEMO_WHITE_SPACE).optimize() -NEMO_NOT_QUOTE = pynini.difference(NEMO_CHAR, r'"').optimize() - -NEMO_PUNCT = pynini.union(*map(pynini.escape, string.punctuation)).optimize() -NEMO_GRAPH = pynini.union(NEMO_ALNUM, NEMO_PUNCT).optimize() - -NEMO_SIGMA = pynini.closure(NEMO_CHAR) - -delete_space = pynutil.delete(pynini.closure(NEMO_WHITE_SPACE)) -delete_zero_or_one_space = pynutil.delete(pynini.closure(NEMO_WHITE_SPACE, 0, 1)) -insert_space = pynutil.insert(" ") -delete_extra_space = pynini.cross(pynini.closure(NEMO_WHITE_SPACE, 1), " ") -delete_preserve_order = pynini.closure( - pynutil.delete(" preserve_order: true") - | (pynutil.delete(" field_order: \"") + NEMO_NOT_QUOTE + pynutil.delete("\"")) -) - -suppletive = pynini.string_file(get_abs_path("data/suppletive.tsv")) -# _v = pynini.union("a", "e", "i", "o", "u") -_c = pynini.union( - "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z" -) -_ies = NEMO_SIGMA + _c + pynini.cross("y", "ies") -_es = NEMO_SIGMA + pynini.union("s", "sh", "ch", "x", "z") + pynutil.insert("es") -_s = NEMO_SIGMA + pynutil.insert("s") - -graph_plural = plurals._priority_union( - suppletive, plurals._priority_union(_ies, plurals._priority_union(_es, _s, NEMO_SIGMA), NEMO_SIGMA), NEMO_SIGMA -).optimize() - -SINGULAR_TO_PLURAL = graph_plural -PLURAL_TO_SINGULAR = pynini.invert(graph_plural) -TO_LOWER = pynini.union(*[pynini.cross(x, y) for x, y in zip(string.ascii_uppercase, string.ascii_lowercase)]) -TO_UPPER = pynini.invert(TO_LOWER) -MIN_NEG_WEIGHT = -0.0001 -MIN_POS_WEIGHT = 0.0001 - - -def generator_main(file_name: str, graphs: Dict[str, 'pynini.FstLike']): - """ - Exports graph as OpenFst finite state archive (FAR) file with given file name and rule name. - - Args: - file_name: exported file name - graphs: Mapping of a rule name and Pynini WFST graph to be exported - """ - exporter = export.Exporter(file_name) - for rule, graph in graphs.items(): - exporter[rule] = graph.optimize() - exporter.close() - print(f'Created {file_name}') - - -def get_plurals(fst): - """ - Given singular returns plurals - - Args: - fst: Fst - - Returns plurals to given singular forms - """ - return SINGULAR_TO_PLURAL @ fst - - -def get_singulars(fst): - """ - Given plural returns singulars - - Args: - fst: Fst - - Returns singulars to given plural forms - """ - return PLURAL_TO_SINGULAR @ fst - - -def convert_space(fst) -> 'pynini.FstLike': - """ - Converts space to nonbreaking space. - Used only in tagger grammars for transducing token values within quotes, e.g. name: "hello kitty" - This is making transducer significantly slower, so only use when there could be potential spaces within quotes, otherwise leave it. - - Args: - fst: input fst - - Returns output fst where breaking spaces are converted to non breaking spaces - """ - return fst @ pynini.cdrewrite(pynini.cross(NEMO_SPACE, NEMO_NON_BREAKING_SPACE), "", "", NEMO_SIGMA) - - -class GraphFst: - """ - Base class for all grammar fsts. - - Args: - name: name of grammar class - kind: either 'classify' or 'verbalize' - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, name: str, kind: str, deterministic: bool = True): - self.name = name - self.kind = kind - self._fst = None - self.deterministic = deterministic - - self.far_path = Path(os.path.dirname(__file__) + '/grammars/' + kind + '/' + name + '.far') - if self.far_exist(): - self._fst = Far(self.far_path, mode="r", arc_type="standard", far_type="default").get_fst() - - def far_exist(self) -> bool: - """ - Returns true if FAR can be loaded - """ - return self.far_path.exists() - - @property - def fst(self) -> 'pynini.FstLike': - return self._fst - - @fst.setter - def fst(self, fst): - self._fst = fst - - def add_tokens(self, fst) -> 'pynini.FstLike': - """ - Wraps class name around to given fst - - Args: - fst: input fst - - Returns: - Fst: fst - """ - return pynutil.insert(f"{self.name} {{ ") + fst + pynutil.insert(" }") - - def delete_tokens(self, fst) -> 'pynini.FstLike': - """ - Deletes class name wrap around output of given fst - - Args: - fst: input fst - - Returns: - Fst: fst - """ - res = ( - pynutil.delete(f"{self.name}") - + delete_space - + pynutil.delete("{") - + delete_space - + fst - + delete_space - + pynutil.delete("}") - ) - return res @ pynini.cdrewrite(pynini.cross(u"\u00A0", " "), "", "", NEMO_SIGMA) diff --git a/nemo_text_processing/text_normalization/en/taggers/__init__.py b/nemo_text_processing/text_normalization/en/taggers/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/text_normalization/en/taggers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/en/taggers/abbreviation.py b/nemo_text_processing/text_normalization/en/taggers/abbreviation.py deleted file mode 100644 index 640bb487d0f8..000000000000 --- a/nemo_text_processing/text_normalization/en/taggers/abbreviation.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_UPPER, GraphFst, insert_space -from pynini.lib import pynutil - - -class AbbreviationFst(GraphFst): - """ - Finite state transducer for classifying electronic: as URLs, email addresses, etc. - e.g. "ABC" -> tokens { abbreviation { value: "A B C" } } - - Args: - whitelist: whitelist FST - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, whitelist: 'pynini.FstLike', deterministic: bool = True): - super().__init__(name="abbreviation", kind="classify", deterministic=deterministic) - - dot = pynini.accep(".") - # A.B.C. -> A. B. C. - graph = NEMO_UPPER + dot + pynini.closure(insert_space + NEMO_UPPER + dot, 1) - # A.B.C. -> A.B.C. - graph |= NEMO_UPPER + dot + pynini.closure(NEMO_UPPER + dot, 1) - # ABC -> A B C - graph |= NEMO_UPPER + pynini.closure(insert_space + NEMO_UPPER, 1) - - # exclude words that are included in the whitelist - graph = pynini.compose( - pynini.difference(pynini.project(graph, "input"), pynini.project(whitelist.graph, "input")), graph - ) - - graph = pynutil.insert("value: \"") + graph.optimize() + pynutil.insert("\"") - graph = self.add_tokens(graph) - self.fst = graph.optimize() diff --git a/nemo_text_processing/text_normalization/en/taggers/cardinal.py b/nemo_text_processing/text_normalization/en/taggers/cardinal.py deleted file mode 100644 index 9b94143412ab..000000000000 --- a/nemo_text_processing/text_normalization/en/taggers/cardinal.py +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_DIGIT, - NEMO_NOT_QUOTE, - NEMO_SIGMA, - GraphFst, - insert_space, -) -from nemo_text_processing.text_normalization.en.taggers.date import get_four_digit_year_graph -from nemo_text_processing.text_normalization.en.utils import get_abs_path -from pynini.examples import plurals -from pynini.lib import pynutil - - -class CardinalFst(GraphFst): - """ - Finite state transducer for classifying cardinals, e.g. - -23 -> cardinal { negative: "true" integer: "twenty three" } } - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True, lm: bool = False): - super().__init__(name="cardinal", kind="classify", deterministic=deterministic) - - self.lm = lm - self.deterministic = deterministic - # TODO replace to have "oh" as a default for "0" - graph = pynini.Far(get_abs_path("data/number/cardinal_number_name.far")).get_fst() - self.graph_hundred_component_at_least_one_none_zero_digit = ( - pynini.closure(NEMO_DIGIT, 2, 3) | pynini.difference(NEMO_DIGIT, pynini.accep("0")) - ) @ graph - - graph_digit = pynini.string_file(get_abs_path("data/number/digit.tsv")) - graph_zero = pynini.string_file(get_abs_path("data/number/zero.tsv")) - - single_digits_graph = pynini.invert(graph_digit | graph_zero) - self.single_digits_graph = single_digits_graph + pynini.closure(insert_space + single_digits_graph) - - if not deterministic: - # for a single token allow only the same normalization - # "007" -> {"oh oh seven", "zero zero seven"} not {"oh zero seven"} - single_digits_graph_zero = pynini.invert(graph_digit | graph_zero) - single_digits_graph_oh = pynini.invert(graph_digit) | pynini.cross("0", "oh") - - self.single_digits_graph = single_digits_graph_zero + pynini.closure( - insert_space + single_digits_graph_zero - ) - self.single_digits_graph |= single_digits_graph_oh + pynini.closure(insert_space + single_digits_graph_oh) - - single_digits_graph_with_commas = pynini.closure( - self.single_digits_graph + insert_space, 1, 3 - ) + pynini.closure( - pynutil.delete(",") - + single_digits_graph - + insert_space - + single_digits_graph - + insert_space - + single_digits_graph, - 1, - ) - - optional_minus_graph = pynini.closure(pynutil.insert("negative: ") + pynini.cross("-", "\"true\" "), 0, 1) - - graph = ( - pynini.closure(NEMO_DIGIT, 1, 3) - + (pynini.closure(pynutil.delete(",") + NEMO_DIGIT ** 3) | pynini.closure(NEMO_DIGIT ** 3)) - ) @ graph - - self.graph = graph - self.graph_with_and = self.add_optional_and(graph) - - if deterministic: - long_numbers = pynini.compose(NEMO_DIGIT ** (5, ...), self.single_digits_graph).optimize() - final_graph = plurals._priority_union(long_numbers, self.graph_with_and, NEMO_SIGMA).optimize() - cardinal_with_leading_zeros = pynini.compose( - pynini.accep("0") + pynini.closure(NEMO_DIGIT), self.single_digits_graph - ) - final_graph |= cardinal_with_leading_zeros - else: - leading_zeros = pynini.compose(pynini.closure(pynini.accep("0"), 1), self.single_digits_graph) - cardinal_with_leading_zeros = ( - leading_zeros + pynutil.insert(" ") + pynini.compose(pynini.closure(NEMO_DIGIT), self.graph_with_and) - ) - - # add small weight to non-default graphs to make sure the deterministic option is listed first - final_graph = ( - self.graph_with_and - | pynutil.add_weight(self.single_digits_graph, 0.0001) - | get_four_digit_year_graph() # allows e.g. 4567 be pronouced as forty five sixty seven - | pynutil.add_weight(single_digits_graph_with_commas, 0.0001) - | cardinal_with_leading_zeros - ) - - final_graph = optional_minus_graph + pynutil.insert("integer: \"") + final_graph + pynutil.insert("\"") - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() - - def add_optional_and(self, graph): - graph_with_and = graph - - if not self.lm: - graph_with_and = pynutil.add_weight(graph, 0.00001) - not_quote = pynini.closure(NEMO_NOT_QUOTE) - no_thousand_million = pynini.difference( - not_quote, not_quote + pynini.union("thousand", "million") + not_quote - ).optimize() - integer = ( - not_quote + pynutil.add_weight(pynini.cross("hundred ", "hundred and ") + no_thousand_million, -0.0001) - ).optimize() - - no_hundred = pynini.difference(NEMO_SIGMA, not_quote + pynini.accep("hundred") + not_quote).optimize() - integer |= ( - not_quote + pynutil.add_weight(pynini.cross("thousand ", "thousand and ") + no_hundred, -0.0001) - ).optimize() - - optional_hundred = pynini.compose((NEMO_DIGIT - "0") ** 3, graph).optimize() - optional_hundred = pynini.compose(optional_hundred, NEMO_SIGMA + pynini.cross(" hundred", "") + NEMO_SIGMA) - graph_with_and |= pynini.compose(graph, integer).optimize() - graph_with_and |= optional_hundred - return graph_with_and diff --git a/nemo_text_processing/text_normalization/en/taggers/date.py b/nemo_text_processing/text_normalization/en/taggers/date.py deleted file mode 100644 index 45031b6e54de..000000000000 --- a/nemo_text_processing/text_normalization/en/taggers/date.py +++ /dev/null @@ -1,368 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_CHAR, - NEMO_DIGIT, - NEMO_LOWER, - NEMO_SIGMA, - TO_LOWER, - GraphFst, - delete_extra_space, - delete_space, - insert_space, -) -from nemo_text_processing.text_normalization.en.utils import ( - augment_labels_with_punct_at_end, - get_abs_path, - load_labels, -) -from pynini.examples import plurals -from pynini.lib import pynutil - -graph_teen = pynini.invert(pynini.string_file(get_abs_path("data/number/teen.tsv"))).optimize() -graph_digit = pynini.invert(pynini.string_file(get_abs_path("data/number/digit.tsv"))).optimize() -ties_graph = pynini.invert(pynini.string_file(get_abs_path("data/number/ty.tsv"))).optimize() -year_suffix = load_labels(get_abs_path("data/date/year_suffix.tsv")) -year_suffix.extend(augment_labels_with_punct_at_end(year_suffix)) -year_suffix = pynini.string_map(year_suffix).optimize() - - -def get_ties_graph(deterministic: bool = True): - """ - Returns two digit transducer, e.g. - 03 -> o three - 12 -> thirteen - 20 -> twenty - """ - graph = graph_teen | ties_graph + pynutil.delete("0") | ties_graph + insert_space + graph_digit - - if deterministic: - graph = graph | pynini.cross("0", "oh") + insert_space + graph_digit - else: - graph = graph | (pynini.cross("0", "oh") | pynini.cross("0", "zero")) + insert_space + graph_digit - - return graph.optimize() - - -def get_four_digit_year_graph(deterministic: bool = True): - """ - Returns a four digit transducer which is combination of ties/teen or digits - (using hundred instead of thousand format), e.g. - 1219 -> twelve nineteen - 3900 -> thirty nine hundred - """ - graph_ties = get_ties_graph(deterministic) - - graph_with_s = ( - (graph_ties + insert_space + graph_ties) - | (graph_teen + insert_space + (ties_graph | pynini.cross("1", "ten"))) - ) + pynutil.delete("0s") - - graph_with_s |= (graph_teen | graph_ties) + insert_space + pynini.cross("00", "hundred") + pynutil.delete("s") - graph_with_s = graph_with_s @ pynini.cdrewrite( - pynini.cross("y", "ies") | pynutil.insert("s"), "", "[EOS]", NEMO_SIGMA - ) - - graph = graph_ties + insert_space + graph_ties - graph |= (graph_teen | graph_ties) + insert_space + pynini.cross("00", "hundred") - - thousand_graph = ( - graph_digit - + insert_space - + pynini.cross("00", "thousand") - + (pynutil.delete("0") | insert_space + graph_digit) - ) - thousand_graph |= ( - graph_digit - + insert_space - + pynini.cross("000", "thousand") - + pynini.closure(pynutil.delete(" "), 0, 1) - + pynini.accep("s") - ) - - graph |= graph_with_s - if deterministic: - graph = plurals._priority_union(thousand_graph, graph, NEMO_SIGMA) - else: - graph |= thousand_graph - - return graph.optimize() - - -def _get_two_digit_year_with_s_graph(): - # to handle '70s -> seventies - graph = ( - pynini.closure(pynutil.delete("'"), 0, 1) - + pynini.compose( - ties_graph + pynutil.delete("0s"), pynini.cdrewrite(pynini.cross("y", "ies"), "", "[EOS]", NEMO_SIGMA) - ) - ).optimize() - return graph - - -def _get_year_graph(cardinal_graph, deterministic: bool = True): - """ - Transducer for year, only from 1000 - 2999 e.g. - 1290 -> twelve nineteen - 2000 - 2009 will be verbalized as two thousand. - - Transducer for 3 digit year, e.g. 123-> one twenty three - - Transducer for year with suffix - 123 A.D., 4200 B.C - """ - graph = get_four_digit_year_graph(deterministic) - graph = (pynini.union("1", "2") + (NEMO_DIGIT ** 3) + pynini.closure(pynini.cross(" s", "s") | "s", 0, 1)) @ graph - - graph |= _get_two_digit_year_with_s_graph() - - three_digit_year = (NEMO_DIGIT @ cardinal_graph) + insert_space + (NEMO_DIGIT ** 2) @ cardinal_graph - year_with_suffix = ( - (get_four_digit_year_graph(deterministic=True) | three_digit_year) + delete_space + insert_space + year_suffix - ) - graph |= year_with_suffix - return graph.optimize() - - -def _get_two_digit_year(cardinal_graph, single_digits_graph): - wo_digit_year = NEMO_DIGIT ** (2) @ plurals._priority_union(cardinal_graph, single_digits_graph, NEMO_SIGMA) - return wo_digit_year - - -class DateFst(GraphFst): - """ - Finite state transducer for classifying date, e.g. - jan. 5, 2012 -> date { month: "january" day: "five" year: "twenty twelve" preserve_order: true } - jan. 5 -> date { month: "january" day: "five" preserve_order: true } - 5 january 2012 -> date { day: "five" month: "january" year: "twenty twelve" preserve_order: true } - 2012-01-05 -> date { year: "twenty twelve" month: "january" day: "five" } - 2012.01.05 -> date { year: "twenty twelve" month: "january" day: "five" } - 2012/01/05 -> date { year: "twenty twelve" month: "january" day: "five" } - 2012 -> date { year: "twenty twelve" } - - Args: - cardinal: CardinalFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, cardinal: GraphFst, deterministic: bool, lm: bool = False): - super().__init__(name="date", kind="classify", deterministic=deterministic) - - # january - month_graph = pynini.string_file(get_abs_path("data/date/month_name.tsv")).optimize() - # January, JANUARY - month_graph |= pynini.compose(TO_LOWER + pynini.closure(NEMO_CHAR), month_graph) | pynini.compose( - TO_LOWER ** (2, ...), month_graph - ) - - # jan - month_abbr_graph = pynini.string_file(get_abs_path("data/date/month_abbr.tsv")).optimize() - # jan, Jan, JAN - month_abbr_graph = ( - month_abbr_graph - | pynini.compose(TO_LOWER + pynini.closure(NEMO_LOWER, 1), month_abbr_graph).optimize() - | pynini.compose(TO_LOWER ** (2, ...), month_abbr_graph).optimize() - ) + pynini.closure(pynutil.delete("."), 0, 1) - month_graph |= month_abbr_graph.optimize() - - month_numbers_labels = pynini.string_file(get_abs_path("data/date/month_number.tsv")).optimize() - cardinal_graph = cardinal.graph_hundred_component_at_least_one_none_zero_digit - - year_graph = _get_year_graph(cardinal_graph=cardinal_graph, deterministic=deterministic) - - # three_digit_year = (NEMO_DIGIT @ cardinal_graph) + insert_space + (NEMO_DIGIT ** 2) @ cardinal_graph - # year_graph |= three_digit_year - - month_graph = pynutil.insert("month: \"") + month_graph + pynutil.insert("\"") - month_numbers_graph = pynutil.insert("month: \"") + month_numbers_labels + pynutil.insert("\"") - - endings = ["rd", "th", "st", "nd"] - endings += [x.upper() for x in endings] - endings = pynini.union(*endings) - - day_graph = ( - pynutil.insert("day: \"") - + pynini.closure(pynutil.delete("the "), 0, 1) - + ( - ((pynini.union("1", "2") + NEMO_DIGIT) | NEMO_DIGIT | (pynini.accep("3") + pynini.union("0", "1"))) - + pynini.closure(pynutil.delete(endings), 0, 1) - ) - @ cardinal_graph - + pynutil.insert("\"") - ) - - two_digit_year = _get_two_digit_year( - cardinal_graph=cardinal_graph, single_digits_graph=cardinal.single_digits_graph - ) - two_digit_year = pynutil.insert("year: \"") + two_digit_year + pynutil.insert("\"") - - # if lm: - # two_digit_year = pynini.compose(pynini.difference(NEMO_DIGIT, "0") + NEMO_DIGIT ** (3), two_digit_year) - # year_graph = pynini.compose(pynini.difference(NEMO_DIGIT, "0") + NEMO_DIGIT ** (2), year_graph) - # year_graph |= pynini.compose(pynini.difference(NEMO_DIGIT, "0") + NEMO_DIGIT ** (4, ...), year_graph) - - graph_year = pynutil.insert(" year: \"") + pynutil.delete(" ") + year_graph + pynutil.insert("\"") - graph_year |= ( - pynutil.insert(" year: \"") - + pynini.accep(",") - + pynini.closure(pynini.accep(" "), 0, 1) - + year_graph - + pynutil.insert("\"") - ) - optional_graph_year = pynini.closure(graph_year, 0, 1) - - year_graph = pynutil.insert("year: \"") + year_graph + pynutil.insert("\"") - - graph_mdy = month_graph + ( - (delete_extra_space + day_graph) - | (pynini.accep(" ") + day_graph) - | graph_year - | (delete_extra_space + day_graph + graph_year) - ) - - graph_mdy |= ( - month_graph - + pynini.cross("-", " ") - + day_graph - + pynini.closure(((pynini.cross("-", " ") + NEMO_SIGMA) @ graph_year), 0, 1) - ) - - for x in ["-", "/", "."]: - delete_sep = pynutil.delete(x) - graph_mdy |= ( - month_numbers_graph - + delete_sep - + insert_space - + pynini.closure(pynutil.delete("0"), 0, 1) - + day_graph - + delete_sep - + insert_space - + (year_graph | two_digit_year) - ) - - graph_dmy = day_graph + delete_extra_space + month_graph + optional_graph_year - day_ex_month = (NEMO_DIGIT ** 2 - pynini.project(month_numbers_graph, "input")) @ day_graph - for x in ["-", "/", "."]: - delete_sep = pynutil.delete(x) - graph_dmy |= ( - day_ex_month - + delete_sep - + insert_space - + month_numbers_graph - + delete_sep - + insert_space - + (year_graph | two_digit_year) - ) - - graph_ymd = pynini.accep("") - for x in ["-", "/", "."]: - delete_sep = pynutil.delete(x) - graph_ymd |= ( - (year_graph | two_digit_year) - + delete_sep - + insert_space - + month_numbers_graph - + delete_sep - + insert_space - + pynini.closure(pynutil.delete("0"), 0, 1) - + day_graph - ) - - final_graph = graph_mdy | graph_dmy - - if not deterministic or lm: - final_graph += pynini.closure(pynutil.insert(" preserve_order: true"), 0, 1) - m_sep_d = ( - month_numbers_graph - + pynutil.delete(pynini.union("-", "/")) - + insert_space - + pynini.closure(pynutil.delete("0"), 0, 1) - + day_graph - ) - final_graph |= m_sep_d - else: - final_graph += pynutil.insert(" preserve_order: true") - - final_graph |= graph_ymd | year_graph - - if not deterministic or lm: - ymd_to_mdy_graph = None - ymd_to_dmy_graph = None - mdy_to_dmy_graph = None - md_to_dm_graph = None - - for month in [x[0] for x in load_labels(get_abs_path("data/date/month_name.tsv"))]: - for day in [x[0] for x in load_labels(get_abs_path("data/date/day.tsv"))]: - ymd_to_mdy_curr = ( - pynutil.insert("month: \"" + month + "\" day: \"" + day + "\" ") - + pynini.accep('year:') - + NEMO_SIGMA - + pynutil.delete(" month: \"" + month + "\" day: \"" + day + "\"") - ) - - # YY-MM-DD -> MM-DD-YY - ymd_to_mdy_curr = pynini.compose(graph_ymd, ymd_to_mdy_curr) - ymd_to_mdy_graph = ( - ymd_to_mdy_curr - if ymd_to_mdy_graph is None - else pynini.union(ymd_to_mdy_curr, ymd_to_mdy_graph) - ) - - ymd_to_dmy_curr = ( - pynutil.insert("day: \"" + day + "\" month: \"" + month + "\" ") - + pynini.accep('year:') - + NEMO_SIGMA - + pynutil.delete(" month: \"" + month + "\" day: \"" + day + "\"") - ) - - # YY-MM-DD -> MM-DD-YY - ymd_to_dmy_curr = pynini.compose(graph_ymd, ymd_to_dmy_curr).optimize() - ymd_to_dmy_graph = ( - ymd_to_dmy_curr - if ymd_to_dmy_graph is None - else pynini.union(ymd_to_dmy_curr, ymd_to_dmy_graph) - ) - - mdy_to_dmy_curr = ( - pynutil.insert("day: \"" + day + "\" month: \"" + month + "\" ") - + pynutil.delete("month: \"" + month + "\" day: \"" + day + "\" ") - + pynini.accep('year:') - + NEMO_SIGMA - ).optimize() - # MM-DD-YY -> verbalize as MM-DD-YY (February fourth 1991) or DD-MM-YY (the fourth of February 1991) - mdy_to_dmy_curr = pynini.compose(graph_mdy, mdy_to_dmy_curr).optimize() - mdy_to_dmy_graph = ( - mdy_to_dmy_curr - if mdy_to_dmy_graph is None - else pynini.union(mdy_to_dmy_curr, mdy_to_dmy_graph).optimize() - ).optimize() - - md_to_dm_curr = pynutil.insert("day: \"" + day + "\" month: \"" + month + "\"") + pynutil.delete( - "month: \"" + month + "\" day: \"" + day + "\"" - ) - md_to_dm_curr = pynini.compose(m_sep_d, md_to_dm_curr).optimize() - - md_to_dm_graph = ( - md_to_dm_curr - if md_to_dm_graph is None - else pynini.union(md_to_dm_curr, md_to_dm_graph).optimize() - ).optimize() - - final_graph |= mdy_to_dmy_graph | md_to_dm_graph | ymd_to_mdy_graph | ymd_to_dmy_graph - - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/en/taggers/decimal.py b/nemo_text_processing/text_normalization/en/taggers/decimal.py deleted file mode 100644 index 2486b5f8c1ce..000000000000 --- a/nemo_text_processing/text_normalization/en/taggers/decimal.py +++ /dev/null @@ -1,129 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_SIGMA, TO_UPPER, GraphFst, get_abs_path -from pynini.lib import pynutil - -delete_space = pynutil.delete(" ") -quantities = pynini.string_file(get_abs_path("data/number/thousand.tsv")) -quantities_abbr = pynini.string_file(get_abs_path("data/number/quantity_abbr.tsv")) -quantities_abbr |= TO_UPPER @ quantities_abbr - - -def get_quantity( - decimal: 'pynini.FstLike', cardinal_up_to_hundred: 'pynini.FstLike', include_abbr: bool -) -> 'pynini.FstLike': - """ - Returns FST that transforms either a cardinal or decimal followed by a quantity into a numeral, - e.g. 1 million -> integer_part: "one" quantity: "million" - e.g. 1.5 million -> integer_part: "one" fractional_part: "five" quantity: "million" - - Args: - decimal: decimal FST - cardinal_up_to_hundred: cardinal FST - """ - quantity_wo_thousand = pynini.project(quantities, "input") - pynini.union("k", "K", "thousand") - if include_abbr: - quantity_wo_thousand |= pynini.project(quantities_abbr, "input") - pynini.union("k", "K", "thousand") - res = ( - pynutil.insert("integer_part: \"") - + cardinal_up_to_hundred - + pynutil.insert("\"") - + pynini.closure(pynutil.delete(" "), 0, 1) - + pynutil.insert(" quantity: \"") - + (quantity_wo_thousand @ (quantities | quantities_abbr)) - + pynutil.insert("\"") - ) - if include_abbr: - quantity = quantities | quantities_abbr - else: - quantity = quantities - res |= ( - decimal - + pynini.closure(pynutil.delete(" "), 0, 1) - + pynutil.insert("quantity: \"") - + quantity - + pynutil.insert("\"") - ) - return res - - -class DecimalFst(GraphFst): - """ - Finite state transducer for classifying decimal, e.g. - -12.5006 billion -> decimal { negative: "true" integer_part: "12" fractional_part: "five o o six" quantity: "billion" } - 1 billion -> decimal { integer_part: "one" quantity: "billion" } - - cardinal: CardinalFst - """ - - def __init__(self, cardinal: GraphFst, deterministic: bool): - super().__init__(name="decimal", kind="classify", deterministic=deterministic) - - cardinal_graph = cardinal.graph_with_and - cardinal_graph_hundred_component_at_least_one_none_zero_digit = ( - cardinal.graph_hundred_component_at_least_one_none_zero_digit - ) - - self.graph = cardinal.single_digits_graph.optimize() - - if not deterministic: - self.graph = self.graph | cardinal_graph - - point = pynutil.delete(".") - optional_graph_negative = pynini.closure(pynutil.insert("negative: ") + pynini.cross("-", "\"true\" "), 0, 1) - - self.graph_fractional = pynutil.insert("fractional_part: \"") + self.graph + pynutil.insert("\"") - self.graph_integer = pynutil.insert("integer_part: \"") + cardinal_graph + pynutil.insert("\"") - final_graph_wo_sign = ( - pynini.closure(self.graph_integer + pynutil.insert(" "), 0, 1) - + point - + pynutil.insert(" ") - + self.graph_fractional - ) - - quantity_w_abbr = get_quantity( - final_graph_wo_sign, cardinal_graph_hundred_component_at_least_one_none_zero_digit, include_abbr=True - ) - quantity_wo_abbr = get_quantity( - final_graph_wo_sign, cardinal_graph_hundred_component_at_least_one_none_zero_digit, include_abbr=False - ) - self.final_graph_wo_negative_w_abbr = final_graph_wo_sign | quantity_w_abbr - self.final_graph_wo_negative = final_graph_wo_sign | quantity_wo_abbr - - # reduce options for non_deterministic and allow either "oh" or "zero", but not combination - if not deterministic: - no_oh_zero = pynini.difference( - NEMO_SIGMA, - (NEMO_SIGMA + "oh" + NEMO_SIGMA + "zero" + NEMO_SIGMA) - | (NEMO_SIGMA + "zero" + NEMO_SIGMA + "oh" + NEMO_SIGMA), - ).optimize() - no_zero_oh = pynini.difference( - NEMO_SIGMA, NEMO_SIGMA + pynini.accep("zero") + NEMO_SIGMA + pynini.accep("oh") + NEMO_SIGMA - ).optimize() - - self.final_graph_wo_negative |= pynini.compose( - self.final_graph_wo_negative, - pynini.cdrewrite( - pynini.cross("integer_part: \"zero\"", "integer_part: \"oh\""), NEMO_SIGMA, NEMO_SIGMA, NEMO_SIGMA - ), - ) - self.final_graph_wo_negative = pynini.compose(self.final_graph_wo_negative, no_oh_zero).optimize() - self.final_graph_wo_negative = pynini.compose(self.final_graph_wo_negative, no_zero_oh).optimize() - - final_graph = optional_graph_negative + self.final_graph_wo_negative - - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/en/taggers/electronic.py b/nemo_text_processing/text_normalization/en/taggers/electronic.py deleted file mode 100644 index 243c0653f6a1..000000000000 --- a/nemo_text_processing/text_normalization/en/taggers/electronic.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_ALPHA, - NEMO_DIGIT, - NEMO_SIGMA, - GraphFst, - get_abs_path, - insert_space, -) -from pynini.lib import pynutil - - -class ElectronicFst(GraphFst): - """ - Finite state transducer for classifying electronic: as URLs, email addresses, etc. - e.g. cdf1@abc.edu -> tokens { electronic { username: "cdf1" domain: "abc.edu" } } - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="electronic", kind="classify", deterministic=deterministic) - - accepted_symbols = pynini.project(pynini.string_file(get_abs_path("data/electronic/symbol.tsv")), "input") - accepted_common_domains = pynini.project( - pynini.string_file(get_abs_path("data/electronic/domain.tsv")), "input" - ) - all_accepted_symbols = NEMO_ALPHA + pynini.closure(NEMO_ALPHA | NEMO_DIGIT | accepted_symbols) - graph_symbols = pynini.string_file(get_abs_path("data/electronic/symbol.tsv")).optimize() - - username = ( - pynutil.insert("username: \"") + all_accepted_symbols + pynutil.insert("\"") + pynini.cross('@', ' ') - ) - domain_graph = all_accepted_symbols + pynini.accep('.') + all_accepted_symbols + NEMO_ALPHA - protocol_symbols = pynini.closure((graph_symbols | pynini.cross(":", "semicolon")) + pynutil.insert(" ")) - protocol_start = (pynini.cross("https", "HTTPS ") | pynini.cross("http", "HTTP ")) + ( - pynini.accep("://") @ protocol_symbols - ) - protocol_file_start = pynini.accep("file") + insert_space + (pynini.accep(":///") @ protocol_symbols) - - protocol_end = pynini.cross("www", "WWW ") + pynini.accep(".") @ protocol_symbols - protocol = protocol_file_start | protocol_start | protocol_end | (protocol_start + protocol_end) - - domain_graph = ( - pynutil.insert("domain: \"") - + pynini.difference(domain_graph, pynini.project(protocol, "input") + NEMO_SIGMA) - + pynutil.insert("\"") - ) - domain_common_graph = ( - pynutil.insert("domain: \"") - + pynini.difference( - all_accepted_symbols - + accepted_common_domains - + pynini.closure(accepted_symbols + pynini.closure(NEMO_ALPHA | NEMO_DIGIT | accepted_symbols), 0, 1), - pynini.project(protocol, "input") + NEMO_SIGMA, - ) - + pynutil.insert("\"") - ) - - protocol = pynutil.insert("protocol: \"") + protocol + pynutil.insert("\"") - # email - graph = username + domain_graph - # abc.com, abc.com/123-sm - graph |= domain_common_graph - # www.abc.com/sdafsdf, or https://www.abc.com/asdfad or www.abc.abc/asdfad - graph |= protocol + pynutil.insert(" ") + domain_graph - - final_graph = self.add_tokens(graph) - - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/en/taggers/fraction.py b/nemo_text_processing/text_normalization/en/taggers/fraction.py deleted file mode 100644 index ac6877c22950..000000000000 --- a/nemo_text_processing/text_normalization/en/taggers/fraction.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst, get_abs_path -from pynini.lib import pynutil - - -class FractionFst(GraphFst): - """ - Finite state transducer for classifying fraction - "23 4/5" -> - tokens { fraction { integer: "twenty three" numerator: "four" denominator: "five" } } - "23 4/5th" -> - tokens { fraction { integer: "twenty three" numerator: "four" denominator: "five" } } - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, cardinal, deterministic: bool = True): - super().__init__(name="fraction", kind="classify", deterministic=deterministic) - cardinal_graph = cardinal.graph - - integer = pynutil.insert("integer_part: \"") + cardinal_graph + pynutil.insert("\"") - numerator = ( - pynutil.insert("numerator: \"") + cardinal_graph + (pynini.cross("/", "\" ") | pynini.cross(" / ", "\" ")) - ) - - endings = ["rd", "th", "st", "nd"] - endings += [x.upper() for x in endings] - optional_end = pynini.closure(pynini.cross(pynini.union(*endings), ""), 0, 1) - - denominator = pynutil.insert("denominator: \"") + cardinal_graph + optional_end + pynutil.insert("\"") - - graph = pynini.closure(integer + pynini.accep(" "), 0, 1) + (numerator + denominator) - graph |= pynini.closure(integer + (pynini.accep(" ") | pynutil.insert(" ")), 0, 1) + pynini.compose( - pynini.string_file(get_abs_path("data/number/fraction.tsv")), (numerator + denominator) - ) - - self.graph = graph - final_graph = self.add_tokens(self.graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/en/taggers/measure.py b/nemo_text_processing/text_normalization/en/taggers/measure.py deleted file mode 100644 index 3861f9168a07..000000000000 --- a/nemo_text_processing/text_normalization/en/taggers/measure.py +++ /dev/null @@ -1,304 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_ALPHA, - NEMO_DIGIT, - NEMO_NON_BREAKING_SPACE, - NEMO_SIGMA, - NEMO_SPACE, - NEMO_UPPER, - SINGULAR_TO_PLURAL, - TO_LOWER, - GraphFst, - convert_space, - delete_space, - delete_zero_or_one_space, - insert_space, -) -from nemo_text_processing.text_normalization.en.taggers.ordinal import OrdinalFst as OrdinalTagger -from nemo_text_processing.text_normalization.en.taggers.whitelist import get_formats -from nemo_text_processing.text_normalization.en.utils import get_abs_path, load_labels -from nemo_text_processing.text_normalization.en.verbalizers.ordinal import OrdinalFst as OrdinalVerbalizer -from pynini.examples import plurals -from pynini.lib import pynutil - - -class MeasureFst(GraphFst): - """ - Finite state transducer for classifying measure, suppletive aware, e.g. - -12kg -> measure { negative: "true" cardinal { integer: "twelve" } units: "kilograms" } - 1kg -> measure { cardinal { integer: "one" } units: "kilogram" } - .5kg -> measure { decimal { fractional_part: "five" } units: "kilograms" } - - Args: - cardinal: CardinalFst - decimal: DecimalFst - fraction: FractionFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, cardinal: GraphFst, decimal: GraphFst, fraction: GraphFst, deterministic: bool = True): - super().__init__(name="measure", kind="classify", deterministic=deterministic) - cardinal_graph = cardinal.graph_with_and | self.get_range(cardinal.graph_with_and) - - graph_unit = pynini.string_file(get_abs_path("data/measure/unit.tsv")) - if not deterministic: - graph_unit |= pynini.string_file(get_abs_path("data/measure/unit_alternatives.tsv")) - - graph_unit |= pynini.compose( - pynini.closure(TO_LOWER, 1) + (NEMO_ALPHA | TO_LOWER) + pynini.closure(NEMO_ALPHA | TO_LOWER), graph_unit - ).optimize() - - graph_unit_plural = convert_space(graph_unit @ SINGULAR_TO_PLURAL) - graph_unit = convert_space(graph_unit) - - optional_graph_negative = pynini.closure(pynutil.insert("negative: ") + pynini.cross("-", "\"true\" "), 0, 1) - - graph_unit2 = ( - pynini.cross("/", "per") + delete_zero_or_one_space + pynutil.insert(NEMO_NON_BREAKING_SPACE) + graph_unit - ) - - optional_graph_unit2 = pynini.closure( - delete_zero_or_one_space + pynutil.insert(NEMO_NON_BREAKING_SPACE) + graph_unit2, 0, 1, - ) - - unit_plural = ( - pynutil.insert("units: \"") - + (graph_unit_plural + optional_graph_unit2 | graph_unit2) - + pynutil.insert("\"") - ) - - unit_singular = ( - pynutil.insert("units: \"") + (graph_unit + optional_graph_unit2 | graph_unit2) + pynutil.insert("\"") - ) - - subgraph_decimal = ( - pynutil.insert("decimal { ") - + optional_graph_negative - + decimal.final_graph_wo_negative - + delete_space - + pynutil.insert(" } ") - + unit_plural - ) - - # support radio FM/AM - subgraph_decimal |= ( - pynutil.insert("decimal { ") - + decimal.final_graph_wo_negative - + delete_space - + pynutil.insert(" } ") - + pynutil.insert("units: \"") - + pynini.union("AM", "FM") - + pynutil.insert("\"") - ) - - subgraph_cardinal = ( - pynutil.insert("cardinal { ") - + optional_graph_negative - + pynutil.insert("integer: \"") - + ((NEMO_SIGMA - "1") @ cardinal_graph) - + delete_space - + pynutil.insert("\"") - + pynutil.insert(" } ") - + unit_plural - ) - - subgraph_cardinal |= ( - pynutil.insert("cardinal { ") - + optional_graph_negative - + pynutil.insert("integer: \"") - + pynini.cross("1", "one") - + delete_space - + pynutil.insert("\"") - + pynutil.insert(" } ") - + unit_singular - ) - - unit_graph = ( - pynutil.insert("cardinal { integer: \"-\" } units: \"") - + pynini.cross(pynini.union("/", "per"), "per") - + delete_zero_or_one_space - + pynutil.insert(NEMO_NON_BREAKING_SPACE) - + graph_unit - + pynutil.insert("\" preserve_order: true") - ) - - decimal_dash_alpha = ( - pynutil.insert("decimal { ") - + decimal.final_graph_wo_negative - + pynini.cross('-', '') - + pynutil.insert(" } units: \"") - + pynini.closure(NEMO_ALPHA, 1) - + pynutil.insert("\"") - ) - - decimal_times = ( - pynutil.insert("decimal { ") - + decimal.final_graph_wo_negative - + pynutil.insert(" } units: \"") - + pynini.cross(pynini.union('x', "X"), 'x') - + pynutil.insert("\"") - ) - - alpha_dash_decimal = ( - pynutil.insert("units: \"") - + pynini.closure(NEMO_ALPHA, 1) - + pynini.accep('-') - + pynutil.insert("\"") - + pynutil.insert(" decimal { ") - + decimal.final_graph_wo_negative - + pynutil.insert(" } preserve_order: true") - ) - - subgraph_fraction = ( - pynutil.insert("fraction { ") + fraction.graph + delete_space + pynutil.insert(" } ") + unit_plural - ) - - address = self.get_address_graph(cardinal) - address = ( - pynutil.insert("units: \"address\" cardinal { integer: \"") - + address - + pynutil.insert("\" } preserve_order: true") - ) - - math_operations = pynini.string_file(get_abs_path("data/measure/math_operation.tsv")) - delimiter = pynini.accep(" ") | pynutil.insert(" ") - - math = ( - (cardinal_graph | NEMO_ALPHA) - + delimiter - + math_operations - + (delimiter | NEMO_ALPHA) - + cardinal_graph - + delimiter - + pynini.cross("=", "equals") - + delimiter - + (cardinal_graph | NEMO_ALPHA) - ) - - math |= ( - (cardinal_graph | NEMO_ALPHA) - + delimiter - + pynini.cross("=", "equals") - + delimiter - + (cardinal_graph | NEMO_ALPHA) - + delimiter - + math_operations - + delimiter - + cardinal_graph - ) - - math = ( - pynutil.insert("units: \"math\" cardinal { integer: \"") - + math - + pynutil.insert("\" } preserve_order: true") - ) - final_graph = ( - subgraph_decimal - | subgraph_cardinal - | unit_graph - | decimal_dash_alpha - | decimal_times - | alpha_dash_decimal - | subgraph_fraction - | address - | math - ) - - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() - - def get_range(self, cardinal: GraphFst): - """ - Returns range forms for measure tagger, e.g. 2-3, 2x3, 2*2 - - Args: - cardinal: cardinal GraphFst - """ - range_graph = cardinal + pynini.cross(pynini.union("-", " - "), " to ") + cardinal - - for x in [" x ", "x"]: - range_graph |= cardinal + pynini.cross(x, " by ") + cardinal - if not self.deterministic: - range_graph |= cardinal + pynini.cross(x, " times ") + cardinal - - for x in ["*", " * "]: - range_graph |= cardinal + pynini.cross(x, " times ") + cardinal - return range_graph.optimize() - - def get_address_graph(self, cardinal): - """ - Finite state transducer for classifying serial. - The serial is a combination of digits, letters and dashes, e.g.: - 2788 San Tomas Expy, Santa Clara, CA 95051 -> - units: "address" cardinal - { integer: "two seven eight eight San Tomas Expressway Santa Clara California nine five zero five one" } - preserve_order: true - """ - ordinal_verbalizer = OrdinalVerbalizer().graph - ordinal_tagger = OrdinalTagger(cardinal=cardinal).graph - ordinal_num = pynini.compose( - pynutil.insert("integer: \"") + ordinal_tagger + pynutil.insert("\""), ordinal_verbalizer - ) - - address_num = NEMO_DIGIT ** (1, 2) @ cardinal.graph_hundred_component_at_least_one_none_zero_digit - address_num += insert_space + NEMO_DIGIT ** 2 @ ( - pynini.closure(pynini.cross("0", "zero "), 0, 1) - + cardinal.graph_hundred_component_at_least_one_none_zero_digit - ) - # to handle the rest of the numbers - address_num = pynini.compose(NEMO_DIGIT ** (3, 4), address_num) - address_num = plurals._priority_union(address_num, cardinal.graph, NEMO_SIGMA) - - direction = ( - pynini.cross("E", "East") - | pynini.cross("S", "South") - | pynini.cross("W", "West") - | pynini.cross("N", "North") - ) + pynini.closure(pynutil.delete("."), 0, 1) - - direction = pynini.closure(pynini.accep(NEMO_SPACE) + direction, 0, 1) - address_words = get_formats(get_abs_path("data/address/address_word.tsv")) - address_words = ( - pynini.accep(NEMO_SPACE) - + (pynini.closure(ordinal_num, 0, 1) | NEMO_UPPER + pynini.closure(NEMO_ALPHA, 1)) - + NEMO_SPACE - + pynini.closure(NEMO_UPPER + pynini.closure(NEMO_ALPHA) + NEMO_SPACE) - + address_words - ) - - city = pynini.closure(NEMO_ALPHA | pynini.accep(NEMO_SPACE), 1) - city = pynini.closure(pynini.accep(",") + pynini.accep(NEMO_SPACE) + city, 0, 1) - - states = load_labels(get_abs_path("data/address/state.tsv")) - - additional_options = [] - for x, y in states: - additional_options.append((x, f"{y[0]}.{y[1:]}")) - states.extend(additional_options) - state_graph = pynini.string_map(states) - state = pynini.invert(state_graph) - state = pynini.closure(pynini.accep(",") + pynini.accep(NEMO_SPACE) + state, 0, 1) - - zip_code = pynini.compose(NEMO_DIGIT ** 5, cardinal.single_digits_graph) - zip_code = pynini.closure(pynini.closure(pynini.accep(","), 0, 1) + pynini.accep(NEMO_SPACE) + zip_code, 0, 1,) - - address = address_num + direction + address_words + pynini.closure(city + state + zip_code, 0, 1) - - address |= address_num + direction + address_words + pynini.closure(pynini.cross(".", ""), 0, 1) - - return address diff --git a/nemo_text_processing/text_normalization/en/taggers/money.py b/nemo_text_processing/text_normalization/en/taggers/money.py deleted file mode 100644 index 43e26bda7b4d..000000000000 --- a/nemo_text_processing/text_normalization/en/taggers/money.py +++ /dev/null @@ -1,192 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_ALPHA, - NEMO_DIGIT, - NEMO_SIGMA, - SINGULAR_TO_PLURAL, - GraphFst, - convert_space, - insert_space, -) -from nemo_text_processing.text_normalization.en.utils import get_abs_path, load_labels -from pynini.lib import pynutil - -min_singular = pynini.string_file(get_abs_path("data/money/currency_minor_singular.tsv")) -min_plural = pynini.string_file(get_abs_path("data/money/currency_minor_plural.tsv")) -maj_singular = pynini.string_file((get_abs_path("data/money/currency_major.tsv"))) - - -class MoneyFst(GraphFst): - """ - Finite state transducer for classifying money, suppletive aware, e.g. - $12.05 -> money { integer_part: "twelve" currency_maj: "dollars" fractional_part: "five" currency_min: "cents" preserve_order: true } - $12.0500 -> money { integer_part: "twelve" currency_maj: "dollars" fractional_part: "five" currency_min: "cents" preserve_order: true } - $1 -> money { currency_maj: "dollar" integer_part: "one" } - $1.00 -> money { currency_maj: "dollar" integer_part: "one" } - $0.05 -> money { fractional_part: "five" currency_min: "cents" preserve_order: true } - $1 million -> money { currency_maj: "dollars" integer_part: "one" quantity: "million" } - $1.2 million -> money { currency_maj: "dollars" integer_part: "one" fractional_part: "two" quantity: "million" } - $1.2320 -> money { currency_maj: "dollars" integer_part: "one" fractional_part: "two three two" } - - Args: - cardinal: CardinalFst - decimal: DecimalFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, cardinal: GraphFst, decimal: GraphFst, deterministic: bool = True): - super().__init__(name="money", kind="classify", deterministic=deterministic) - cardinal_graph = cardinal.graph_with_and - graph_decimal_final = decimal.final_graph_wo_negative_w_abbr - - maj_singular_labels = load_labels(get_abs_path("data/money/currency_major.tsv")) - maj_unit_plural = convert_space(maj_singular @ SINGULAR_TO_PLURAL) - maj_unit_singular = convert_space(maj_singular) - - graph_maj_singular = pynutil.insert("currency_maj: \"") + maj_unit_singular + pynutil.insert("\"") - graph_maj_plural = pynutil.insert("currency_maj: \"") + maj_unit_plural + pynutil.insert("\"") - - optional_delete_fractional_zeros = pynini.closure( - pynutil.delete(".") + pynini.closure(pynutil.delete("0"), 1), 0, 1 - ) - - graph_integer_one = pynutil.insert("integer_part: \"") + pynini.cross("1", "one") + pynutil.insert("\"") - # only for decimals where third decimal after comma is non-zero or with quantity - decimal_delete_last_zeros = ( - pynini.closure(NEMO_DIGIT | pynutil.delete(",")) - + pynini.accep(".") - + pynini.closure(NEMO_DIGIT, 2) - + (NEMO_DIGIT - "0") - + pynini.closure(pynutil.delete("0")) - ) - decimal_with_quantity = NEMO_SIGMA + NEMO_ALPHA - - graph_decimal = ( - graph_maj_plural + insert_space + (decimal_delete_last_zeros | decimal_with_quantity) @ graph_decimal_final - ) - - graph_integer = ( - pynutil.insert("integer_part: \"") + ((NEMO_SIGMA - "1") @ cardinal_graph) + pynutil.insert("\"") - ) - - graph_integer_only = graph_maj_singular + insert_space + graph_integer_one - graph_integer_only |= graph_maj_plural + insert_space + graph_integer - - final_graph = (graph_integer_only + optional_delete_fractional_zeros) | graph_decimal - - # remove trailing zeros of non zero number in the first 2 digits and fill up to 2 digits - # e.g. 2000 -> 20, 0200->02, 01 -> 01, 10 -> 10 - # not accepted: 002, 00, 0, - two_digits_fractional_part = ( - pynini.closure(NEMO_DIGIT) + (NEMO_DIGIT - "0") + pynini.closure(pynutil.delete("0")) - ) @ ( - (pynutil.delete("0") + (NEMO_DIGIT - "0")) - | ((NEMO_DIGIT - "0") + pynutil.insert("0")) - | ((NEMO_DIGIT - "0") + NEMO_DIGIT) - ) - - graph_min_singular = pynutil.insert(" currency_min: \"") + min_singular + pynutil.insert("\"") - graph_min_plural = pynutil.insert(" currency_min: \"") + min_plural + pynutil.insert("\"") - # format ** dollars ** cent - decimal_graph_with_minor = None - integer_graph_reordered = None - decimal_default_reordered = None - for curr_symbol, _ in maj_singular_labels: - preserve_order = pynutil.insert(" preserve_order: true") - integer_plus_maj = graph_integer + insert_space + pynutil.insert(curr_symbol) @ graph_maj_plural - integer_plus_maj |= graph_integer_one + insert_space + pynutil.insert(curr_symbol) @ graph_maj_singular - - integer_plus_maj_with_comma = pynini.compose( - NEMO_DIGIT - "0" + pynini.closure(NEMO_DIGIT | pynutil.delete(",")), integer_plus_maj - ) - integer_plus_maj = pynini.compose(pynini.closure(NEMO_DIGIT) - "0", integer_plus_maj) - integer_plus_maj |= integer_plus_maj_with_comma - - graph_fractional_one = two_digits_fractional_part @ pynini.cross("1", "one") - graph_fractional_one = pynutil.insert("fractional_part: \"") + graph_fractional_one + pynutil.insert("\"") - graph_fractional = ( - two_digits_fractional_part - @ (pynini.closure(NEMO_DIGIT, 1, 2) - "1") - @ cardinal.graph_hundred_component_at_least_one_none_zero_digit - ) - graph_fractional = pynutil.insert("fractional_part: \"") + graph_fractional + pynutil.insert("\"") - - fractional_plus_min = graph_fractional + insert_space + pynutil.insert(curr_symbol) @ graph_min_plural - fractional_plus_min |= ( - graph_fractional_one + insert_space + pynutil.insert(curr_symbol) @ graph_min_singular - ) - - decimal_graph_with_minor_curr = integer_plus_maj + pynini.cross(".", " ") + fractional_plus_min - - if not deterministic: - decimal_graph_with_minor_curr |= pynutil.add_weight( - integer_plus_maj - + pynini.cross(".", " ") - + pynutil.insert("fractional_part: \"") - + two_digits_fractional_part @ cardinal.graph_hundred_component_at_least_one_none_zero_digit - + pynutil.insert("\""), - weight=0.0001, - ) - default_fraction_graph = (decimal_delete_last_zeros | decimal_with_quantity) @ graph_decimal_final - decimal_graph_with_minor_curr |= ( - pynini.closure(pynutil.delete("0"), 0, 1) + pynutil.delete(".") + fractional_plus_min - ) - decimal_graph_with_minor_curr = ( - pynutil.delete(curr_symbol) + decimal_graph_with_minor_curr + preserve_order - ) - - decimal_graph_with_minor = ( - decimal_graph_with_minor_curr - if decimal_graph_with_minor is None - else pynini.union(decimal_graph_with_minor, decimal_graph_with_minor_curr).optimize() - ) - - if not deterministic: - integer_graph_reordered_curr = ( - pynutil.delete(curr_symbol) + integer_plus_maj + preserve_order - ).optimize() - - integer_graph_reordered = ( - integer_graph_reordered_curr - if integer_graph_reordered is None - else pynini.union(integer_graph_reordered, integer_graph_reordered_curr).optimize() - ) - decimal_default_reordered_curr = ( - pynutil.delete(curr_symbol) - + default_fraction_graph - + insert_space - + pynutil.insert(curr_symbol) @ graph_maj_plural - ) - - decimal_default_reordered = ( - decimal_default_reordered_curr - if decimal_default_reordered is None - else pynini.union(decimal_default_reordered, decimal_default_reordered_curr) - ).optimize() - - # weight for SH - final_graph |= pynutil.add_weight(decimal_graph_with_minor, -0.0001) - - if not deterministic: - final_graph |= integer_graph_reordered | decimal_default_reordered - # to handle "$2.00" cases - final_graph |= pynini.compose( - NEMO_SIGMA + pynutil.delete(".") + pynini.closure(pynutil.delete("0"), 1), integer_graph_reordered - ) - final_graph = self.add_tokens(final_graph.optimize()) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/en/taggers/ordinal.py b/nemo_text_processing/text_normalization/en/taggers/ordinal.py deleted file mode 100644 index 1ea56c917011..000000000000 --- a/nemo_text_processing/text_normalization/en/taggers/ordinal.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_DIGIT, GraphFst -from pynini.lib import pynutil - - -class OrdinalFst(GraphFst): - """ - Finite state transducer for classifying ordinal, e.g. - 13th -> ordinal { integer: "thirteen" } - - Args: - cardinal: CardinalFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, cardinal: GraphFst, deterministic: bool = True): - super().__init__(name="ordinal", kind="classify", deterministic=deterministic) - - cardinal_graph = cardinal.graph - cardinal_format = pynini.closure(NEMO_DIGIT | pynini.accep(",")) - st_format = ( - pynini.closure(cardinal_format + (NEMO_DIGIT - "1"), 0, 1) - + pynini.accep("1") - + pynutil.delete(pynini.union("st", "ST")) - ) - nd_format = ( - pynini.closure(cardinal_format + (NEMO_DIGIT - "1"), 0, 1) - + pynini.accep("2") - + pynutil.delete(pynini.union("nd", "ND")) - ) - rd_format = ( - pynini.closure(cardinal_format + (NEMO_DIGIT - "1"), 0, 1) - + pynini.accep("3") - + pynutil.delete(pynini.union("rd", "RD")) - ) - th_format = pynini.closure( - (NEMO_DIGIT - "1" - "2" - "3") - | (cardinal_format + "1" + NEMO_DIGIT) - | (cardinal_format + (NEMO_DIGIT - "1") + (NEMO_DIGIT - "1" - "2" - "3")), - 1, - ) + pynutil.delete(pynini.union("th", "TH")) - self.graph = (st_format | nd_format | rd_format | th_format) @ cardinal_graph - final_graph = pynutil.insert("integer: \"") + self.graph + pynutil.insert("\"") - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/en/taggers/punctuation.py b/nemo_text_processing/text_normalization/en/taggers/punctuation.py deleted file mode 100644 index 769b020ce382..000000000000 --- a/nemo_text_processing/text_normalization/en/taggers/punctuation.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import sys -from unicodedata import category - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_SPACE, NEMO_SIGMA, GraphFst -from nemo_text_processing.text_normalization.en.utils import get_abs_path, load_labels -from pynini.examples import plurals -from pynini.lib import pynutil - - -class PunctuationFst(GraphFst): - """ - Finite state transducer for classifying punctuation - e.g. a, -> tokens { name: "a" } tokens { name: "," } - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="punctuation", kind="classify", deterministic=deterministic) - s = "!#%&\'()*+,-./:;<=>?@^_`{|}~\"" - - punct_symbols_to_exclude = ["[", "]"] - punct_unicode = [ - chr(i) - for i in range(sys.maxunicode) - if category(chr(i)).startswith("P") and chr(i) not in punct_symbols_to_exclude - ] - - whitelist_symbols = load_labels(get_abs_path("data/whitelist/symbol.tsv")) - whitelist_symbols = [x[0] for x in whitelist_symbols] - self.punct_marks = [p for p in punct_unicode + list(s) if p not in whitelist_symbols] - - punct = pynini.union(*self.punct_marks) - punct = pynini.closure(punct, 1) - - emphasis = ( - pynini.accep("<") - + ( - (pynini.closure(NEMO_NOT_SPACE - pynini.union("<", ">"), 1) + pynini.closure(pynini.accep("/"), 0, 1)) - | (pynini.accep("/") + pynini.closure(NEMO_NOT_SPACE - pynini.union("<", ">"), 1)) - ) - + pynini.accep(">") - ) - punct = plurals._priority_union(emphasis, punct, NEMO_SIGMA) - - self.graph = punct - self.fst = (pynutil.insert("name: \"") + self.graph + pynutil.insert("\"")).optimize() diff --git a/nemo_text_processing/text_normalization/en/taggers/range.py b/nemo_text_processing/text_normalization/en/taggers/range.py deleted file mode 100644 index 9c237f9c28c8..000000000000 --- a/nemo_text_processing/text_normalization/en/taggers/range.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_DIGIT, GraphFst, convert_space -from pynini.lib import pynutil - - -class RangeFst(GraphFst): - """ - This class is a composite class of two other class instances - - Args: - time: composed tagger and verbalizer - date: composed tagger and verbalizer - cardinal: tagger - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - lm: whether to use for hybrid LM - """ - - def __init__( - self, time: GraphFst, date: GraphFst, cardinal: GraphFst, deterministic: bool = True, lm: bool = False, - ): - super().__init__(name="range", kind="classify", deterministic=deterministic) - - delete_space = pynini.closure(pynutil.delete(" "), 0, 1) - - approx = pynini.cross("~", "approximately") - - # TIME - time_graph = time + delete_space + pynini.cross("-", " to ") + delete_space + time - self.graph = time_graph | (approx + time) - - cardinal = cardinal.graph_with_and - # YEAR - date_year_four_digit = (NEMO_DIGIT ** 4 + pynini.closure(pynini.accep("s"), 0, 1)) @ date - date_year_two_digit = (NEMO_DIGIT ** 2 + pynini.closure(pynini.accep("s"), 0, 1)) @ date - year_to_year_graph = ( - date_year_four_digit - + delete_space - + pynini.cross("-", " to ") - + delete_space - + (date_year_four_digit | date_year_two_digit | (NEMO_DIGIT ** 2 @ cardinal)) - ) - mid_year_graph = pynini.accep("mid") + pynini.cross("-", " ") + (date_year_four_digit | date_year_two_digit) - - self.graph |= year_to_year_graph - self.graph |= mid_year_graph - - # ADDITION - range_graph = cardinal + pynini.closure(pynini.cross("+", " plus ") + cardinal, 1) - range_graph |= cardinal + pynini.closure(pynini.cross(" + ", " plus ") + cardinal, 1) - range_graph |= approx + cardinal - range_graph |= cardinal + (pynini.cross("...", " ... ") | pynini.accep(" ... ")) + cardinal - - if not deterministic or lm: - # cardinal ---- - cardinal_to_cardinal_graph = ( - cardinal + delete_space + pynini.cross("-", pynini.union(" to ", " minus ")) + delete_space + cardinal - ) - - range_graph |= cardinal_to_cardinal_graph | ( - cardinal + delete_space + pynini.cross(":", " to ") + delete_space + cardinal - ) - - # MULTIPLY - for x in [" x ", "x"]: - range_graph |= cardinal + pynini.closure( - pynini.cross(x, pynini.union(" by ", " times ")) + cardinal, 1 - ) - - for x in ["*", " * "]: - range_graph |= cardinal + pynini.closure(pynini.cross(x, " times ") + cardinal, 1) - - # supports "No. 12" -> "Number 12" - range_graph |= ( - (pynini.cross(pynini.union("NO", "No"), "Number") | pynini.cross("no", "number")) - + pynini.closure(pynini.union(". ", " "), 0, 1) - + cardinal - ) - - for x in ["/", " / "]: - range_graph |= cardinal + pynini.closure(pynini.cross(x, " divided by ") + cardinal, 1) - - self.graph |= range_graph - - self.graph = self.graph.optimize() - graph = pynutil.insert("name: \"") + convert_space(self.graph).optimize() + pynutil.insert("\"") - self.fst = graph.optimize() diff --git a/nemo_text_processing/text_normalization/en/taggers/roman.py b/nemo_text_processing/text_normalization/en/taggers/roman.py deleted file mode 100644 index e12ee4a2b1ed..000000000000 --- a/nemo_text_processing/text_normalization/en/taggers/roman.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_ALPHA, NEMO_SIGMA, GraphFst -from nemo_text_processing.text_normalization.en.utils import get_abs_path, load_labels -from pynini.lib import pynutil - - -class RomanFst(GraphFst): - """ - Finite state transducer for classifying roman numbers: - e.g. "IV" -> tokens { roman { integer: "four" } } - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True, lm: bool = False): - super().__init__(name="roman", kind="classify", deterministic=deterministic) - - roman_dict = load_labels(get_abs_path("data/roman/roman_to_spoken.tsv")) - default_graph = pynini.string_map(roman_dict).optimize() - default_graph = pynutil.insert("integer: \"") + default_graph + pynutil.insert("\"") - ordinal_limit = 19 - - if deterministic: - # exclude "I" - start_idx = 1 - else: - start_idx = 0 - - graph_teens = pynini.string_map([x[0] for x in roman_dict[start_idx:ordinal_limit]]).optimize() - - # roman numerals up to ordinal_limit with a preceding name are converted to ordinal form - names = get_names() - graph = ( - pynutil.insert("key_the_ordinal: \"") - + names - + pynutil.insert("\"") - + pynini.accep(" ") - + graph_teens @ default_graph - ).optimize() - - # single symbol roman numerals with preceding key words (multiple formats) are converted to cardinal form - key_words = [] - for k_word in load_labels(get_abs_path("data/roman/key_word.tsv")): - key_words.append(k_word) - key_words.append([k_word[0][0].upper() + k_word[0][1:]]) - key_words.append([k_word[0].upper()]) - - key_words = pynini.string_map(key_words).optimize() - graph |= ( - pynutil.insert("key_cardinal: \"") + key_words + pynutil.insert("\"") + pynini.accep(" ") + default_graph - ).optimize() - - if deterministic or lm: - # two digit roman numerals up to 49 - roman_to_cardinal = pynini.compose( - pynini.closure(NEMO_ALPHA, 2), - ( - pynutil.insert("default_cardinal: \"default\" ") - + (pynini.string_map([x[0] for x in roman_dict[:50]]).optimize()) @ default_graph - ), - ) - graph |= roman_to_cardinal - elif not lm: - # two or more digit roman numerals - roman_to_cardinal = pynini.compose( - pynini.difference(NEMO_SIGMA, "I"), - ( - pynutil.insert("default_cardinal: \"default\" integer: \"") - + pynini.string_map(roman_dict).optimize() - + pynutil.insert("\"") - ), - ).optimize() - graph |= roman_to_cardinal - - # convert three digit roman or up with suffix to ordinal - roman_to_ordinal = pynini.compose( - pynini.closure(NEMO_ALPHA, 3), - (pynutil.insert("default_ordinal: \"default\" ") + graph_teens @ default_graph + pynutil.delete("th")), - ) - - graph |= roman_to_ordinal - graph = self.add_tokens(graph.optimize()) - - self.fst = graph.optimize() - - -def get_names(): - """ - Returns the graph that matched common male and female names. - """ - male_labels = load_labels(get_abs_path("data/roman/male.tsv")) - female_labels = load_labels(get_abs_path("data/roman/female.tsv")) - male_labels.extend([[x[0].upper()] for x in male_labels]) - female_labels.extend([[x[0].upper()] for x in female_labels]) - names = pynini.string_map(male_labels).optimize() - names |= pynini.string_map(female_labels).optimize() - return names diff --git a/nemo_text_processing/text_normalization/en/taggers/serial.py b/nemo_text_processing/text_normalization/en/taggers/serial.py deleted file mode 100644 index 669fd95a0569..000000000000 --- a/nemo_text_processing/text_normalization/en/taggers/serial.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_ALPHA, - NEMO_DIGIT, - NEMO_NOT_SPACE, - NEMO_SIGMA, - GraphFst, - convert_space, -) -from nemo_text_processing.text_normalization.en.utils import get_abs_path, load_labels -from pynini.examples import plurals -from pynini.lib import pynutil - - -class SerialFst(GraphFst): - """ - This class is a composite class of two other class instances - - Args: - time: composed tagger and verbalizer - date: composed tagger and verbalizer - cardinal: tagger - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - lm: whether to use for hybrid LM - """ - - def __init__(self, cardinal: GraphFst, ordinal: GraphFst, deterministic: bool = True, lm: bool = False): - super().__init__(name="integer", kind="classify", deterministic=deterministic) - - """ - Finite state transducer for classifying serial (handles only cases without delimiters, - values with delimiters are handled by default). - The serial is a combination of digits, letters and dashes, e.g.: - c325b -> tokens { cardinal { integer: "c three two five b" } } - """ - num_graph = pynini.compose(NEMO_DIGIT ** (6, ...), cardinal.single_digits_graph).optimize() - num_graph |= pynini.compose(NEMO_DIGIT ** (1, 5), cardinal.graph).optimize() - # to handle numbers starting with zero - num_graph |= pynini.compose( - pynini.accep("0") + pynini.closure(NEMO_DIGIT), cardinal.single_digits_graph - ).optimize() - # TODO: "#" doesn't work from the file - symbols_graph = pynini.string_file(get_abs_path("data/whitelist/symbol.tsv")).optimize() | pynini.cross( - "#", "hash" - ) - num_graph |= symbols_graph - - if not self.deterministic and not lm: - num_graph |= cardinal.single_digits_graph - # also allow double digits to be pronounced as integer in serial number - num_graph |= pynutil.add_weight( - NEMO_DIGIT ** 2 @ cardinal.graph_hundred_component_at_least_one_none_zero_digit, weight=0.0001 - ) - - # add space between letter and digit/symbol - symbols = [x[0] for x in load_labels(get_abs_path("data/whitelist/symbol.tsv"))] - symbols = pynini.union(*symbols) - digit_symbol = NEMO_DIGIT | symbols - - graph_with_space = pynini.compose( - pynini.cdrewrite(pynutil.insert(" "), NEMO_ALPHA | symbols, digit_symbol, NEMO_SIGMA), - pynini.cdrewrite(pynutil.insert(" "), digit_symbol, NEMO_ALPHA | symbols, NEMO_SIGMA), - ) - - # serial graph with delimiter - delimiter = pynini.accep("-") | pynini.accep("/") | pynini.accep(" ") - if not deterministic: - delimiter |= pynini.cross("-", " dash ") | pynini.cross("/", " slash ") - - alphas = pynini.closure(NEMO_ALPHA, 1) - letter_num = alphas + delimiter + num_graph - num_letter = pynini.closure(num_graph + delimiter, 1) + alphas - next_alpha_or_num = pynini.closure(delimiter + (alphas | num_graph)) - next_alpha_or_num |= pynini.closure( - delimiter - + num_graph - + plurals._priority_union(pynini.accep(" "), pynutil.insert(" "), NEMO_SIGMA).optimize() - + alphas - ) - - serial_graph = letter_num + next_alpha_or_num - serial_graph |= num_letter + next_alpha_or_num - # numbers only with 2+ delimiters - serial_graph |= ( - num_graph + delimiter + num_graph + delimiter + num_graph + pynini.closure(delimiter + num_graph) - ) - # 2+ symbols - serial_graph |= pynini.compose(NEMO_SIGMA + symbols + NEMO_SIGMA, num_graph + delimiter + num_graph) - - # exclude ordinal numbers from serial options - serial_graph = pynini.compose( - pynini.difference(NEMO_SIGMA, pynini.project(ordinal.graph, "input")), serial_graph - ).optimize() - - serial_graph = pynutil.add_weight(serial_graph, 0.0001) - serial_graph |= ( - pynini.closure(NEMO_NOT_SPACE, 1) - + (pynini.cross("^2", " squared") | pynini.cross("^3", " cubed")).optimize() - ) - - # at least one serial graph with alpha numeric value and optional additional serial/num/alpha values - serial_graph = ( - pynini.closure((serial_graph | num_graph | alphas) + delimiter) - + serial_graph - + pynini.closure(delimiter + (serial_graph | num_graph | alphas)) - ) - - serial_graph |= pynini.compose(graph_with_space, serial_graph.optimize()).optimize() - serial_graph = pynini.compose(pynini.closure(NEMO_NOT_SPACE, 2), serial_graph).optimize() - - # this is not to verbolize "/" as "slash" in cases like "import/export" - serial_graph = pynini.compose( - pynini.difference( - NEMO_SIGMA, pynini.closure(NEMO_ALPHA, 1) + pynini.accep("/") + pynini.closure(NEMO_ALPHA, 1) - ), - serial_graph, - ) - self.graph = serial_graph.optimize() - graph = pynutil.insert("name: \"") + convert_space(self.graph).optimize() + pynutil.insert("\"") - self.fst = graph.optimize() diff --git a/nemo_text_processing/text_normalization/en/taggers/telephone.py b/nemo_text_processing/text_normalization/en/taggers/telephone.py deleted file mode 100644 index 1caedffddde5..000000000000 --- a/nemo_text_processing/text_normalization/en/taggers/telephone.py +++ /dev/null @@ -1,133 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_ALPHA, - NEMO_DIGIT, - NEMO_SIGMA, - GraphFst, - delete_extra_space, - delete_space, - insert_space, - plurals, -) -from nemo_text_processing.text_normalization.en.utils import get_abs_path -from pynini.lib import pynutil - - -class TelephoneFst(GraphFst): - """ - Finite state transducer for classifying telephone, and IP, and SSN which includes country code, number part and extension - country code optional: +*** - number part: ***-***-****, or (***) ***-**** - extension optional: 1-9999 - E.g - +1 123-123-5678-1 -> telephone { country_code: "one" number_part: "one two three, one two three, five six seven eight" extension: "one" } - 1-800-GO-U-HAUL -> telephone { country_code: "one" number_part: "one, eight hundred GO U HAUL" } - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="telephone", kind="classify", deterministic=deterministic) - - add_separator = pynutil.insert(", ") # between components - zero = pynini.cross("0", "zero") - if not deterministic: - zero |= pynini.cross("0", pynini.union("o", "oh")) - digit = pynini.invert(pynini.string_file(get_abs_path("data/number/digit.tsv"))).optimize() | zero - - telephone_prompts = pynini.string_file(get_abs_path("data/telephone/telephone_prompt.tsv")) - country_code = ( - pynini.closure(telephone_prompts + delete_extra_space, 0, 1) - + pynini.closure(pynini.cross("+", "plus "), 0, 1) - + pynini.closure(digit + insert_space, 0, 2) - + digit - + pynutil.insert(",") - ) - country_code |= telephone_prompts - country_code = pynutil.insert("country_code: \"") + country_code + pynutil.insert("\"") - country_code = country_code + pynini.closure(pynutil.delete("-"), 0, 1) + delete_space + insert_space - - area_part_default = pynini.closure(digit + insert_space, 2, 2) + digit - area_part = pynini.cross("800", "eight hundred") | pynini.compose( - pynini.difference(NEMO_SIGMA, "800"), area_part_default - ) - - area_part = ( - (area_part + (pynutil.delete("-") | pynutil.delete("."))) - | ( - pynutil.delete("(") - + area_part - + ((pynutil.delete(")") + pynini.closure(pynutil.delete(" "), 0, 1)) | pynutil.delete(")-")) - ) - ) + add_separator - - del_separator = pynini.closure(pynini.union("-", " ", "."), 0, 1) - number_length = ((NEMO_DIGIT + del_separator) | (NEMO_ALPHA + del_separator)) ** 7 - number_words = pynini.closure( - (NEMO_DIGIT @ digit) + (insert_space | (pynini.cross("-", ', '))) - | NEMO_ALPHA - | (NEMO_ALPHA + pynini.cross("-", ' ')) - ) - number_words |= pynini.closure( - (NEMO_DIGIT @ digit) + (insert_space | (pynini.cross(".", ', '))) - | NEMO_ALPHA - | (NEMO_ALPHA + pynini.cross(".", ' ')) - ) - number_words = pynini.compose(number_length, number_words) - number_part = area_part + number_words - number_part = pynutil.insert("number_part: \"") + number_part + pynutil.insert("\"") - extension = ( - pynutil.insert("extension: \"") + pynini.closure(digit + insert_space, 0, 3) + digit + pynutil.insert("\"") - ) - extension = pynini.closure(insert_space + extension, 0, 1) - - graph = plurals._priority_union(country_code + number_part, number_part, NEMO_SIGMA).optimize() - graph = plurals._priority_union(country_code + number_part + extension, graph, NEMO_SIGMA).optimize() - graph = plurals._priority_union(number_part + extension, graph, NEMO_SIGMA).optimize() - - # ip - ip_prompts = pynini.string_file(get_abs_path("data/telephone/ip_prompt.tsv")) - digit_to_str_graph = digit + pynini.closure(pynutil.insert(" ") + digit, 0, 2) - ip_graph = digit_to_str_graph + (pynini.cross(".", " dot ") + digit_to_str_graph) ** 3 - graph |= ( - pynini.closure( - pynutil.insert("country_code: \"") + ip_prompts + pynutil.insert("\"") + delete_extra_space, 0, 1 - ) - + pynutil.insert("number_part: \"") - + ip_graph.optimize() - + pynutil.insert("\"") - ) - # ssn - ssn_prompts = pynini.string_file(get_abs_path("data/telephone/ssn_prompt.tsv")) - three_digit_part = digit + (pynutil.insert(" ") + digit) ** 2 - two_digit_part = digit + pynutil.insert(" ") + digit - four_digit_part = digit + (pynutil.insert(" ") + digit) ** 3 - ssn_separator = pynini.cross("-", ", ") - ssn_graph = three_digit_part + ssn_separator + two_digit_part + ssn_separator + four_digit_part - - graph |= ( - pynini.closure( - pynutil.insert("country_code: \"") + ssn_prompts + pynutil.insert("\"") + delete_extra_space, 0, 1 - ) - + pynutil.insert("number_part: \"") - + ssn_graph.optimize() - + pynutil.insert("\"") - ) - - final_graph = self.add_tokens(graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/en/taggers/time.py b/nemo_text_processing/text_normalization/en/taggers/time.py deleted file mode 100644 index 4020996cc449..000000000000 --- a/nemo_text_processing/text_normalization/en/taggers/time.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_DIGIT, - GraphFst, - convert_space, - delete_space, - insert_space, -) -from nemo_text_processing.text_normalization.en.utils import ( - augment_labels_with_punct_at_end, - get_abs_path, - load_labels, -) -from pynini.lib import pynutil - - -class TimeFst(GraphFst): - """ - Finite state transducer for classifying time, e.g. - 12:30 a.m. est -> time { hours: "twelve" minutes: "thirty" suffix: "a m" zone: "e s t" } - 2.30 a.m. -> time { hours: "two" minutes: "thirty" suffix: "a m" } - 02.30 a.m. -> time { hours: "two" minutes: "thirty" suffix: "a m" } - 2.00 a.m. -> time { hours: "two" suffix: "a m" } - 2 a.m. -> time { hours: "two" suffix: "a m" } - 02:00 -> time { hours: "two" } - 2:00 -> time { hours: "two" } - 10:00:05 a.m. -> time { hours: "ten" minutes: "zero" seconds: "five" suffix: "a m" } - - Args: - cardinal: CardinalFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, cardinal: GraphFst, deterministic: bool = True): - super().__init__(name="time", kind="classify", deterministic=deterministic) - suffix_labels = load_labels(get_abs_path("data/time/suffix.tsv")) - suffix_labels.extend(augment_labels_with_punct_at_end(suffix_labels)) - suffix_graph = pynini.string_map(suffix_labels) - - time_zone_graph = pynini.string_file(get_abs_path("data/time/zone.tsv")) - - # only used for < 1000 thousand -> 0 weight - cardinal = cardinal.graph - - labels_hour = [str(x) for x in range(0, 24)] - labels_minute_single = [str(x) for x in range(1, 10)] - labels_minute_double = [str(x) for x in range(10, 60)] - - delete_leading_zero_to_double_digit = (NEMO_DIGIT + NEMO_DIGIT) | ( - pynini.closure(pynutil.delete("0"), 0, 1) + NEMO_DIGIT - ) - - graph_hour = delete_leading_zero_to_double_digit @ pynini.union(*labels_hour) @ cardinal - - graph_minute_single = pynini.union(*labels_minute_single) @ cardinal - graph_minute_double = pynini.union(*labels_minute_double) @ cardinal - - final_graph_hour = pynutil.insert("hours: \"") + graph_hour + pynutil.insert("\"") - final_graph_minute = ( - pynutil.insert("minutes: \"") - + (pynini.cross("0", "o") + insert_space + graph_minute_single | graph_minute_double) - + pynutil.insert("\"") - ) - final_graph_second = ( - pynutil.insert("seconds: \"") - + (pynini.cross("0", "o") + insert_space + graph_minute_single | graph_minute_double) - + pynutil.insert("\"") - ) - final_suffix = pynutil.insert("suffix: \"") + convert_space(suffix_graph) + pynutil.insert("\"") - final_suffix_optional = pynini.closure(delete_space + insert_space + final_suffix, 0, 1) - final_time_zone_optional = pynini.closure( - delete_space - + insert_space - + pynutil.insert("zone: \"") - + convert_space(time_zone_graph) - + pynutil.insert("\""), - 0, - 1, - ) - - # 2:30 pm, 02:30, 2:00 - graph_hm = ( - final_graph_hour - + pynutil.delete(":") - + (pynutil.delete("00") | insert_space + final_graph_minute) - + final_suffix_optional - + final_time_zone_optional - ) - - # 10:30:05 pm, - graph_hms = ( - final_graph_hour - + pynutil.delete(":") - + (pynini.cross("00", " minutes: \"zero\"") | insert_space + final_graph_minute) - + pynutil.delete(":") - + (pynini.cross("00", " seconds: \"zero\"") | insert_space + final_graph_second) - + final_suffix_optional - + final_time_zone_optional - ) - - # 2.xx pm/am - graph_hm2 = ( - final_graph_hour - + pynutil.delete(".") - + (pynutil.delete("00") | insert_space + final_graph_minute) - + delete_space - + insert_space - + final_suffix - + final_time_zone_optional - ) - # 2 pm est - graph_h = final_graph_hour + delete_space + insert_space + final_suffix + final_time_zone_optional - final_graph = (graph_hm | graph_h | graph_hm2 | graph_hms).optimize() - - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/en/taggers/tokenize_and_classify.py b/nemo_text_processing/text_normalization/en/taggers/tokenize_and_classify.py deleted file mode 100644 index d37b39cfaacd..000000000000 --- a/nemo_text_processing/text_normalization/en/taggers/tokenize_and_classify.py +++ /dev/null @@ -1,215 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import time - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_WHITE_SPACE, - GraphFst, - delete_extra_space, - delete_space, - generator_main, -) -from nemo_text_processing.text_normalization.en.taggers.abbreviation import AbbreviationFst -from nemo_text_processing.text_normalization.en.taggers.cardinal import CardinalFst -from nemo_text_processing.text_normalization.en.taggers.date import DateFst -from nemo_text_processing.text_normalization.en.taggers.decimal import DecimalFst -from nemo_text_processing.text_normalization.en.taggers.electronic import ElectronicFst -from nemo_text_processing.text_normalization.en.taggers.fraction import FractionFst -from nemo_text_processing.text_normalization.en.taggers.measure import MeasureFst -from nemo_text_processing.text_normalization.en.taggers.money import MoneyFst -from nemo_text_processing.text_normalization.en.taggers.ordinal import OrdinalFst -from nemo_text_processing.text_normalization.en.taggers.punctuation import PunctuationFst -from nemo_text_processing.text_normalization.en.taggers.range import RangeFst as RangeFst -from nemo_text_processing.text_normalization.en.taggers.roman import RomanFst -from nemo_text_processing.text_normalization.en.taggers.serial import SerialFst -from nemo_text_processing.text_normalization.en.taggers.telephone import TelephoneFst -from nemo_text_processing.text_normalization.en.taggers.time import TimeFst -from nemo_text_processing.text_normalization.en.taggers.whitelist import WhiteListFst -from nemo_text_processing.text_normalization.en.taggers.word import WordFst -from nemo_text_processing.text_normalization.en.verbalizers.date import DateFst as vDateFst -from nemo_text_processing.text_normalization.en.verbalizers.ordinal import OrdinalFst as vOrdinalFst -from nemo_text_processing.text_normalization.en.verbalizers.time import TimeFst as vTimeFst -from pynini.lib import pynutil - -from nemo.utils import logging - - -class ClassifyFst(GraphFst): - """ - Final class that composes all other classification grammars. This class can process an entire sentence including punctuation. - For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - - Args: - input_case: accepting either "lower_cased" or "cased" input. - deterministic: if True will provide a single transduction option, - for False multiple options (used for audio-based normalization) - cache_dir: path to a dir with .far grammar file. Set to None to avoid using cache. - overwrite_cache: set to True to overwrite .far files - whitelist: path to a file with whitelist replacements - """ - - def __init__( - self, - input_case: str, - deterministic: bool = True, - cache_dir: str = None, - overwrite_cache: bool = False, - whitelist: str = None, - ): - super().__init__(name="tokenize_and_classify", kind="classify", deterministic=deterministic) - - far_file = None - if cache_dir is not None and cache_dir != "None": - os.makedirs(cache_dir, exist_ok=True) - whitelist_file = os.path.basename(whitelist) if whitelist else "" - far_file = os.path.join( - cache_dir, f"en_tn_{deterministic}_deterministic_{input_case}_{whitelist_file}_tokenize.far" - ) - if not overwrite_cache and far_file and os.path.exists(far_file): - self.fst = pynini.Far(far_file, mode="r")["tokenize_and_classify"] - logging.info(f'ClassifyFst.fst was restored from {far_file}.') - else: - logging.info(f"Creating ClassifyFst grammars.") - - start_time = time.time() - cardinal = CardinalFst(deterministic=deterministic) - cardinal_graph = cardinal.fst - logging.debug(f"cardinal: {time.time() - start_time: .2f}s -- {cardinal_graph.num_states()} nodes") - - start_time = time.time() - ordinal = OrdinalFst(cardinal=cardinal, deterministic=deterministic) - ordinal_graph = ordinal.fst - logging.debug(f"ordinal: {time.time() - start_time: .2f}s -- {ordinal_graph.num_states()} nodes") - - start_time = time.time() - decimal = DecimalFst(cardinal=cardinal, deterministic=deterministic) - decimal_graph = decimal.fst - logging.debug(f"decimal: {time.time() - start_time: .2f}s -- {decimal_graph.num_states()} nodes") - - start_time = time.time() - fraction = FractionFst(deterministic=deterministic, cardinal=cardinal) - fraction_graph = fraction.fst - logging.debug(f"fraction: {time.time() - start_time: .2f}s -- {fraction_graph.num_states()} nodes") - - start_time = time.time() - measure = MeasureFst(cardinal=cardinal, decimal=decimal, fraction=fraction, deterministic=deterministic) - measure_graph = measure.fst - logging.debug(f"measure: {time.time() - start_time: .2f}s -- {measure_graph.num_states()} nodes") - - start_time = time.time() - date_graph = DateFst(cardinal=cardinal, deterministic=deterministic).fst - logging.debug(f"date: {time.time() - start_time: .2f}s -- {date_graph.num_states()} nodes") - - start_time = time.time() - time_graph = TimeFst(cardinal=cardinal, deterministic=deterministic).fst - logging.debug(f"time: {time.time() - start_time: .2f}s -- {time_graph.num_states()} nodes") - - start_time = time.time() - telephone_graph = TelephoneFst(deterministic=deterministic).fst - logging.debug(f"telephone: {time.time() - start_time: .2f}s -- {telephone_graph.num_states()} nodes") - - start_time = time.time() - electonic_graph = ElectronicFst(deterministic=deterministic).fst - logging.debug(f"electronic: {time.time() - start_time: .2f}s -- {electonic_graph.num_states()} nodes") - - start_time = time.time() - money_graph = MoneyFst(cardinal=cardinal, decimal=decimal, deterministic=deterministic).fst - logging.debug(f"money: {time.time() - start_time: .2f}s -- {money_graph.num_states()} nodes") - - start_time = time.time() - whitelist_graph = WhiteListFst( - input_case=input_case, deterministic=deterministic, input_file=whitelist - ).fst - logging.debug(f"whitelist: {time.time() - start_time: .2f}s -- {whitelist_graph.num_states()} nodes") - - start_time = time.time() - punctuation = PunctuationFst(deterministic=deterministic) - punct_graph = punctuation.fst - logging.debug(f"punct: {time.time() - start_time: .2f}s -- {punct_graph.num_states()} nodes") - - start_time = time.time() - word_graph = WordFst(punctuation=punctuation, deterministic=deterministic).fst - logging.debug(f"word: {time.time() - start_time: .2f}s -- {word_graph.num_states()} nodes") - - start_time = time.time() - serial_graph = SerialFst(cardinal=cardinal, ordinal=ordinal, deterministic=deterministic).fst - logging.debug(f"serial: {time.time() - start_time: .2f}s -- {serial_graph.num_states()} nodes") - - start_time = time.time() - v_time_graph = vTimeFst(deterministic=deterministic).fst - v_ordinal_graph = vOrdinalFst(deterministic=deterministic) - v_date_graph = vDateFst(ordinal=v_ordinal_graph, deterministic=deterministic).fst - time_final = pynini.compose(time_graph, v_time_graph) - date_final = pynini.compose(date_graph, v_date_graph) - range_graph = RangeFst( - time=time_final, date=date_final, cardinal=cardinal, deterministic=deterministic - ).fst - logging.debug(f"range: {time.time() - start_time: .2f}s -- {range_graph.num_states()} nodes") - - classify = ( - pynutil.add_weight(whitelist_graph, 1.01) - | pynutil.add_weight(time_graph, 1.1) - | pynutil.add_weight(date_graph, 1.09) - | pynutil.add_weight(decimal_graph, 1.1) - | pynutil.add_weight(measure_graph, 1.1) - | pynutil.add_weight(cardinal_graph, 1.1) - | pynutil.add_weight(ordinal_graph, 1.1) - | pynutil.add_weight(money_graph, 1.1) - | pynutil.add_weight(telephone_graph, 1.1) - | pynutil.add_weight(electonic_graph, 1.1) - | pynutil.add_weight(fraction_graph, 1.1) - | pynutil.add_weight(range_graph, 1.1) - | pynutil.add_weight(serial_graph, 1.1001) # should be higher than the rest of the classes - ) - - roman_graph = RomanFst(deterministic=deterministic).fst - classify |= pynutil.add_weight(roman_graph, 1.1) - - if not deterministic: - abbreviation_graph = AbbreviationFst(deterministic=deterministic).fst - classify |= pynutil.add_weight(abbreviation_graph, 100) - - punct = pynutil.insert("tokens { ") + pynutil.add_weight(punct_graph, weight=2.1) + pynutil.insert(" }") - punct = pynini.closure( - pynini.compose(pynini.closure(NEMO_WHITE_SPACE, 1), delete_extra_space) - | (pynutil.insert(" ") + punct), - 1, - ) - - classify |= pynutil.add_weight(word_graph, 100) - token = pynutil.insert("tokens { ") + classify + pynutil.insert(" }") - token_plus_punct = ( - pynini.closure(punct + pynutil.insert(" ")) + token + pynini.closure(pynutil.insert(" ") + punct) - ) - - graph = token_plus_punct + pynini.closure( - ( - pynini.compose(pynini.closure(NEMO_WHITE_SPACE, 1), delete_extra_space) - | (pynutil.insert(" ") + punct + pynutil.insert(" ")) - ) - + token_plus_punct - ) - - graph = delete_space + graph + delete_space - graph |= punct - - self.fst = graph.optimize() - - if far_file: - generator_main(far_file, {"tokenize_and_classify": self.fst}) - logging.info(f"ClassifyFst grammars are saved to {far_file}.") diff --git a/nemo_text_processing/text_normalization/en/taggers/tokenize_and_classify_lm.py b/nemo_text_processing/text_normalization/en/taggers/tokenize_and_classify_lm.py deleted file mode 100644 index fa48c3784998..000000000000 --- a/nemo_text_processing/text_normalization/en/taggers/tokenize_and_classify_lm.py +++ /dev/null @@ -1,228 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_CHAR, - NEMO_DIGIT, - NEMO_NOT_SPACE, - NEMO_SIGMA, - NEMO_WHITE_SPACE, - GraphFst, - delete_extra_space, - delete_space, - generator_main, -) -from nemo_text_processing.text_normalization.en.taggers.cardinal import CardinalFst -from nemo_text_processing.text_normalization.en.taggers.date import DateFst -from nemo_text_processing.text_normalization.en.taggers.decimal import DecimalFst -from nemo_text_processing.text_normalization.en.taggers.electronic import ElectronicFst -from nemo_text_processing.text_normalization.en.taggers.fraction import FractionFst -from nemo_text_processing.text_normalization.en.taggers.measure import MeasureFst -from nemo_text_processing.text_normalization.en.taggers.money import MoneyFst -from nemo_text_processing.text_normalization.en.taggers.ordinal import OrdinalFst -from nemo_text_processing.text_normalization.en.taggers.punctuation import PunctuationFst -from nemo_text_processing.text_normalization.en.taggers.range import RangeFst as RangeFst -from nemo_text_processing.text_normalization.en.taggers.roman import RomanFst -from nemo_text_processing.text_normalization.en.taggers.serial import SerialFst -from nemo_text_processing.text_normalization.en.taggers.telephone import TelephoneFst -from nemo_text_processing.text_normalization.en.taggers.time import TimeFst -from nemo_text_processing.text_normalization.en.taggers.whitelist import WhiteListFst -from nemo_text_processing.text_normalization.en.taggers.word import WordFst -from nemo_text_processing.text_normalization.en.verbalizers.cardinal import CardinalFst as vCardinal -from nemo_text_processing.text_normalization.en.verbalizers.date import DateFst as vDate -from nemo_text_processing.text_normalization.en.verbalizers.decimal import DecimalFst as vDecimal -from nemo_text_processing.text_normalization.en.verbalizers.electronic import ElectronicFst as vElectronic -from nemo_text_processing.text_normalization.en.verbalizers.fraction import FractionFst as vFraction -from nemo_text_processing.text_normalization.en.verbalizers.measure import MeasureFst as vMeasure -from nemo_text_processing.text_normalization.en.verbalizers.money import MoneyFst as vMoney -from nemo_text_processing.text_normalization.en.verbalizers.ordinal import OrdinalFst as vOrdinal -from nemo_text_processing.text_normalization.en.verbalizers.roman import RomanFst as vRoman -from nemo_text_processing.text_normalization.en.verbalizers.telephone import TelephoneFst as vTelephone -from nemo_text_processing.text_normalization.en.verbalizers.time import TimeFst as vTime -from nemo_text_processing.text_normalization.en.verbalizers.word import WordFst as vWord -from pynini.examples import plurals -from pynini.lib import pynutil - -from nemo.utils import logging - - -class ClassifyFst(GraphFst): - """ - Final class that composes all other classification grammars. This class can process an entire sentence including punctuation. - For deployment, this grammar will be compiled and exported to OpenFst Finite State Archive (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - - Args: - input_case: accepting either "lower_cased" or "cased" input. - deterministic: if True will provide a single transduction option, - for False multiple options (used for audio-based normalization) - cache_dir: path to a dir with .far grammar file. Set to None to avoid using cache. - overwrite_cache: set to True to overwrite .far files - whitelist: path to a file with whitelist replacements - """ - - def __init__( - self, - input_case: str, - deterministic: bool = True, - cache_dir: str = None, - overwrite_cache: bool = True, - whitelist: str = None, - ): - super().__init__(name="tokenize_and_classify", kind="classify", deterministic=deterministic) - - far_file = None - if cache_dir is not None and cache_dir != 'None': - os.makedirs(cache_dir, exist_ok=True) - whitelist_file = os.path.basename(whitelist) if whitelist else "" - far_file = os.path.join( - cache_dir, f"_{input_case}_en_tn_{deterministic}_deterministic{whitelist_file}_lm.far" - ) - if not overwrite_cache and far_file and os.path.exists(far_file): - self.fst = pynini.Far(far_file, mode='r')['tokenize_and_classify'] - no_digits = pynini.closure(pynini.difference(NEMO_CHAR, NEMO_DIGIT)) - self.fst_no_digits = pynini.compose(self.fst, no_digits).optimize() - logging.info(f'ClassifyFst.fst was restored from {far_file}.') - else: - logging.info(f'Creating ClassifyFst grammars. This might take some time...') - # TAGGERS - cardinal = CardinalFst(deterministic=True, lm=True) - cardinal_tagger = cardinal - cardinal_graph = cardinal.fst - - ordinal = OrdinalFst(cardinal=cardinal, deterministic=True) - ordinal_graph = ordinal.fst - - decimal = DecimalFst(cardinal=cardinal, deterministic=True) - decimal_graph = decimal.fst - fraction = FractionFst(deterministic=True, cardinal=cardinal) - fraction_graph = fraction.fst - - measure = MeasureFst(cardinal=cardinal, decimal=decimal, fraction=fraction, deterministic=True) - measure_graph = measure.fst - date = DateFst(cardinal=cardinal, deterministic=True, lm=True) - date_graph = date.fst - punctuation = PunctuationFst(deterministic=True) - punct_graph = punctuation.graph - word_graph = WordFst(punctuation=punctuation, deterministic=deterministic).graph - time_graph = TimeFst(cardinal=cardinal, deterministic=True).fst - telephone_graph = TelephoneFst(deterministic=True).fst - electronic_graph = ElectronicFst(deterministic=True).fst - money_graph = MoneyFst(cardinal=cardinal, decimal=decimal, deterministic=False).fst - whitelist = WhiteListFst(input_case=input_case, deterministic=False, input_file=whitelist) - whitelist_graph = whitelist.graph - serial_graph = SerialFst(cardinal=cardinal, ordinal=ordinal, deterministic=deterministic, lm=True).fst - - # VERBALIZERS - cardinal = vCardinal(deterministic=True) - v_cardinal_graph = cardinal.fst - decimal = vDecimal(cardinal=cardinal, deterministic=True) - v_decimal_graph = decimal.fst - ordinal = vOrdinal(deterministic=True) - v_ordinal_graph = ordinal.fst - fraction = vFraction(deterministic=True, lm=True) - v_fraction_graph = fraction.fst - v_telephone_graph = vTelephone(deterministic=True).fst - v_electronic_graph = vElectronic(deterministic=True).fst - measure = vMeasure(decimal=decimal, cardinal=cardinal, fraction=fraction, deterministic=False) - v_measure_graph = measure.fst - v_time_graph = vTime(deterministic=True).fst - v_date_graph = vDate(ordinal=ordinal, deterministic=deterministic, lm=True).fst - v_money_graph = vMoney(decimal=decimal, deterministic=deterministic).fst - v_roman_graph = vRoman(deterministic=deterministic).fst - v_word_graph = vWord(deterministic=deterministic).fst - - cardinal_or_date_final = plurals._priority_union(date_graph, cardinal_graph, NEMO_SIGMA) - cardinal_or_date_final = pynini.compose(cardinal_or_date_final, (v_cardinal_graph | v_date_graph)) - - time_final = pynini.compose(time_graph, v_time_graph) - ordinal_final = pynini.compose(ordinal_graph, v_ordinal_graph) - sem_w = 1 - word_w = 100 - punct_w = 2 - classify_and_verbalize = ( - pynutil.add_weight(time_final, sem_w) - | pynutil.add_weight(pynini.compose(decimal_graph, v_decimal_graph), sem_w) - | pynutil.add_weight(pynini.compose(measure_graph, v_measure_graph), sem_w) - | pynutil.add_weight(ordinal_final, sem_w) - | pynutil.add_weight(pynini.compose(telephone_graph, v_telephone_graph), sem_w) - | pynutil.add_weight(pynini.compose(electronic_graph, v_electronic_graph), sem_w) - | pynutil.add_weight(pynini.compose(fraction_graph, v_fraction_graph), sem_w) - | pynutil.add_weight(pynini.compose(money_graph, v_money_graph), sem_w) - | pynutil.add_weight(cardinal_or_date_final, sem_w) - | pynutil.add_weight(whitelist_graph, sem_w) - | pynutil.add_weight( - pynini.compose(serial_graph, v_word_graph), 1.1001 - ) # should be higher than the rest of the classes - ).optimize() - - roman_graph = RomanFst(deterministic=deterministic, lm=True).fst - # the weight matches the word_graph weight for "I" cases in long sentences with multiple semiotic tokens - classify_and_verbalize |= pynutil.add_weight(pynini.compose(roman_graph, v_roman_graph), sem_w) - - date_final = pynini.compose(date_graph, v_date_graph) - range_graph = RangeFst( - time=time_final, cardinal=cardinal_tagger, date=date_final, deterministic=deterministic - ).fst - classify_and_verbalize |= pynutil.add_weight(pynini.compose(range_graph, v_word_graph), sem_w) - classify_and_verbalize = pynutil.insert("< ") + classify_and_verbalize + pynutil.insert(" >") - classify_and_verbalize |= pynutil.add_weight(word_graph, word_w) - - punct_only = pynutil.add_weight(punct_graph, weight=punct_w) - punct = pynini.closure( - pynini.compose(pynini.closure(NEMO_WHITE_SPACE, 1), delete_extra_space) - | (pynutil.insert(" ") + punct_only), - 1, - ) - - def get_token_sem_graph(classify_and_verbalize): - token_plus_punct = ( - pynini.closure(punct + pynutil.insert(" ")) - + classify_and_verbalize - + pynini.closure(pynutil.insert(" ") + punct) - ) - - graph = token_plus_punct + pynini.closure( - ( - pynini.compose(pynini.closure(NEMO_WHITE_SPACE, 1), delete_extra_space) - | (pynutil.insert(" ") + punct + pynutil.insert(" ")) - ) - + token_plus_punct - ) - - graph |= punct_only + pynini.closure(punct) - graph = delete_space + graph + delete_space - - remove_extra_spaces = pynini.closure(NEMO_NOT_SPACE, 1) + pynini.closure( - delete_extra_space + pynini.closure(NEMO_NOT_SPACE, 1) - ) - remove_extra_spaces |= ( - pynini.closure(pynutil.delete(" "), 1) - + pynini.closure(NEMO_NOT_SPACE, 1) - + pynini.closure(delete_extra_space + pynini.closure(NEMO_NOT_SPACE, 1)) - ) - - graph = pynini.compose(graph.optimize(), remove_extra_spaces).optimize() - return graph - - self.fst = get_token_sem_graph(classify_and_verbalize) - no_digits = pynini.closure(pynini.difference(NEMO_CHAR, NEMO_DIGIT)) - self.fst_no_digits = pynini.compose(self.fst, no_digits).optimize() - - if far_file: - generator_main(far_file, {"tokenize_and_classify": self.fst}) - logging.info(f'ClassifyFst grammars are saved to {far_file}.') diff --git a/nemo_text_processing/text_normalization/en/taggers/tokenize_and_classify_with_audio.py b/nemo_text_processing/text_normalization/en/taggers/tokenize_and_classify_with_audio.py deleted file mode 100644 index d9adc4c907e7..000000000000 --- a/nemo_text_processing/text_normalization/en/taggers/tokenize_and_classify_with_audio.py +++ /dev/null @@ -1,229 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_CHAR, - NEMO_DIGIT, - NEMO_NOT_SPACE, - NEMO_WHITE_SPACE, - GraphFst, - delete_extra_space, - delete_space, - generator_main, -) -from nemo_text_processing.text_normalization.en.taggers.abbreviation import AbbreviationFst -from nemo_text_processing.text_normalization.en.taggers.cardinal import CardinalFst -from nemo_text_processing.text_normalization.en.taggers.date import DateFst -from nemo_text_processing.text_normalization.en.taggers.decimal import DecimalFst -from nemo_text_processing.text_normalization.en.taggers.electronic import ElectronicFst -from nemo_text_processing.text_normalization.en.taggers.fraction import FractionFst -from nemo_text_processing.text_normalization.en.taggers.measure import MeasureFst -from nemo_text_processing.text_normalization.en.taggers.money import MoneyFst -from nemo_text_processing.text_normalization.en.taggers.ordinal import OrdinalFst -from nemo_text_processing.text_normalization.en.taggers.punctuation import PunctuationFst -from nemo_text_processing.text_normalization.en.taggers.range import RangeFst as RangeFst -from nemo_text_processing.text_normalization.en.taggers.roman import RomanFst -from nemo_text_processing.text_normalization.en.taggers.serial import SerialFst -from nemo_text_processing.text_normalization.en.taggers.telephone import TelephoneFst -from nemo_text_processing.text_normalization.en.taggers.time import TimeFst -from nemo_text_processing.text_normalization.en.taggers.whitelist import WhiteListFst -from nemo_text_processing.text_normalization.en.taggers.word import WordFst -from nemo_text_processing.text_normalization.en.verbalizers.abbreviation import AbbreviationFst as vAbbreviation -from nemo_text_processing.text_normalization.en.verbalizers.cardinal import CardinalFst as vCardinal -from nemo_text_processing.text_normalization.en.verbalizers.date import DateFst as vDate -from nemo_text_processing.text_normalization.en.verbalizers.decimal import DecimalFst as vDecimal -from nemo_text_processing.text_normalization.en.verbalizers.electronic import ElectronicFst as vElectronic -from nemo_text_processing.text_normalization.en.verbalizers.fraction import FractionFst as vFraction -from nemo_text_processing.text_normalization.en.verbalizers.measure import MeasureFst as vMeasure -from nemo_text_processing.text_normalization.en.verbalizers.money import MoneyFst as vMoney -from nemo_text_processing.text_normalization.en.verbalizers.ordinal import OrdinalFst as vOrdinal -from nemo_text_processing.text_normalization.en.verbalizers.roman import RomanFst as vRoman -from nemo_text_processing.text_normalization.en.verbalizers.telephone import TelephoneFst as vTelephone -from nemo_text_processing.text_normalization.en.verbalizers.time import TimeFst as vTime -from nemo_text_processing.text_normalization.en.verbalizers.word import WordFst as vWord -from pynini.lib import pynutil - -from nemo.utils import logging - - -class ClassifyFst(GraphFst): - """ - Final class that composes all other classification grammars. This class can process an entire sentence including punctuation. - For deployment, this grammar will be compiled and exported to OpenFst Finite State Archive (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - - Args: - input_case: accepting either "lower_cased" or "cased" input. - deterministic: if True will provide a single transduction option, - for False multiple options (used for audio-based normalization) - cache_dir: path to a dir with .far grammar file. Set to None to avoid using cache. - overwrite_cache: set to True to overwrite .far files - whitelist: path to a file with whitelist replacements - """ - - def __init__( - self, - input_case: str, - deterministic: bool = True, - cache_dir: str = None, - overwrite_cache: bool = True, - whitelist: str = None, - ): - super().__init__(name="tokenize_and_classify", kind="classify", deterministic=deterministic) - - far_file = None - if cache_dir is not None and cache_dir != 'None': - os.makedirs(cache_dir, exist_ok=True) - whitelist_file = os.path.basename(whitelist) if whitelist else "" - far_file = os.path.join( - cache_dir, f"_{input_case}_en_tn_{deterministic}_deterministic{whitelist_file}.far" - ) - if not overwrite_cache and far_file and os.path.exists(far_file): - self.fst = pynini.Far(far_file, mode='r')['tokenize_and_classify'] - no_digits = pynini.closure(pynini.difference(NEMO_CHAR, NEMO_DIGIT)) - self.fst_no_digits = pynini.compose(self.fst, no_digits).optimize() - logging.info(f'ClassifyFst.fst was restored from {far_file}.') - else: - logging.info(f'Creating ClassifyFst grammars. This might take some time...') - # TAGGERS - cardinal = CardinalFst(deterministic=deterministic) - cardinal_graph = cardinal.fst - - ordinal = OrdinalFst(cardinal=cardinal, deterministic=deterministic) - deterministic_ordinal = OrdinalFst(cardinal=cardinal, deterministic=True) - ordinal_graph = ordinal.fst - - decimal = DecimalFst(cardinal=cardinal, deterministic=deterministic) - decimal_graph = decimal.fst - fraction = FractionFst(deterministic=deterministic, cardinal=cardinal) - fraction_graph = fraction.fst - - measure = MeasureFst(cardinal=cardinal, decimal=decimal, fraction=fraction, deterministic=deterministic) - measure_graph = measure.fst - date_graph = DateFst(cardinal=cardinal, deterministic=deterministic).fst - punctuation = PunctuationFst(deterministic=True) - punct_graph = punctuation.graph - word_graph = WordFst(punctuation=punctuation, deterministic=deterministic).graph - time_graph = TimeFst(cardinal=cardinal, deterministic=deterministic).fst - telephone_graph = TelephoneFst(deterministic=deterministic).fst - electronic_graph = ElectronicFst(deterministic=deterministic).fst - money_graph = MoneyFst(cardinal=cardinal, decimal=decimal, deterministic=deterministic).fst - whitelist = WhiteListFst(input_case=input_case, deterministic=deterministic, input_file=whitelist) - whitelist_graph = whitelist.graph - serial_graph = SerialFst(cardinal=cardinal, ordinal=deterministic_ordinal, deterministic=deterministic).fst - - # VERBALIZERS - cardinal = vCardinal(deterministic=deterministic) - v_cardinal_graph = cardinal.fst - decimal = vDecimal(cardinal=cardinal, deterministic=deterministic) - v_decimal_graph = decimal.fst - ordinal = vOrdinal(deterministic=deterministic) - v_ordinal_graph = ordinal.fst - fraction = vFraction(deterministic=deterministic) - v_fraction_graph = fraction.fst - v_telephone_graph = vTelephone(deterministic=deterministic).fst - v_electronic_graph = vElectronic(deterministic=deterministic).fst - measure = vMeasure(decimal=decimal, cardinal=cardinal, fraction=fraction, deterministic=deterministic) - v_measure_graph = measure.fst - v_time_graph = vTime(deterministic=deterministic).fst - v_date_graph = vDate(ordinal=ordinal, deterministic=deterministic).fst - v_money_graph = vMoney(decimal=decimal, deterministic=deterministic).fst - v_roman_graph = vRoman(deterministic=deterministic).fst - v_abbreviation = vAbbreviation(deterministic=deterministic).fst - - det_v_time_graph = vTime(deterministic=True).fst - det_v_date_graph = vDate(ordinal=vOrdinal(deterministic=True), deterministic=True).fst - time_final = pynini.compose(time_graph, det_v_time_graph) - date_final = pynini.compose(date_graph, det_v_date_graph) - range_graph = RangeFst( - time=time_final, date=date_final, cardinal=CardinalFst(deterministic=True), deterministic=deterministic - ).fst - v_word_graph = vWord(deterministic=deterministic).fst - - sem_w = 1 - word_w = 100 - punct_w = 2 - classify_and_verbalize = ( - pynutil.add_weight(whitelist_graph, sem_w) - | pynutil.add_weight(pynini.compose(time_graph, v_time_graph), sem_w) - | pynutil.add_weight(pynini.compose(decimal_graph, v_decimal_graph), sem_w) - | pynutil.add_weight(pynini.compose(measure_graph, v_measure_graph), sem_w) - | pynutil.add_weight(pynini.compose(cardinal_graph, v_cardinal_graph), sem_w) - | pynutil.add_weight(pynini.compose(ordinal_graph, v_ordinal_graph), sem_w) - | pynutil.add_weight(pynini.compose(telephone_graph, v_telephone_graph), sem_w) - | pynutil.add_weight(pynini.compose(electronic_graph, v_electronic_graph), sem_w) - | pynutil.add_weight(pynini.compose(fraction_graph, v_fraction_graph), sem_w) - | pynutil.add_weight(pynini.compose(money_graph, v_money_graph), sem_w) - | pynutil.add_weight(word_graph, word_w) - | pynutil.add_weight(pynini.compose(date_graph, v_date_graph), sem_w - 0.01) - | pynutil.add_weight(pynini.compose(range_graph, v_word_graph), sem_w) - | pynutil.add_weight( - pynini.compose(serial_graph, v_word_graph), 1.1001 - ) # should be higher than the rest of the classes - ).optimize() - - if not deterministic: - roman_graph = RomanFst(deterministic=deterministic).fst - # the weight matches the word_graph weight for "I" cases in long sentences with multiple semiotic tokens - classify_and_verbalize |= pynutil.add_weight(pynini.compose(roman_graph, v_roman_graph), word_w) - - abbreviation_graph = AbbreviationFst(whitelist=whitelist, deterministic=deterministic).fst - classify_and_verbalize |= pynutil.add_weight( - pynini.compose(abbreviation_graph, v_abbreviation), word_w - ) - - punct_only = pynutil.add_weight(punct_graph, weight=punct_w) - punct = pynini.closure( - pynini.compose(pynini.closure(NEMO_WHITE_SPACE, 1), delete_extra_space) - | (pynutil.insert(" ") + punct_only), - 1, - ) - - token_plus_punct = ( - pynini.closure(punct + pynutil.insert(" ")) - + classify_and_verbalize - + pynini.closure(pynutil.insert(" ") + punct) - ) - - graph = token_plus_punct + pynini.closure( - ( - pynini.compose(pynini.closure(NEMO_WHITE_SPACE, 1), delete_extra_space) - | (pynutil.insert(" ") + punct + pynutil.insert(" ")) - ) - + token_plus_punct - ) - - graph |= punct_only + pynini.closure(punct) - graph = delete_space + graph + delete_space - - remove_extra_spaces = pynini.closure(NEMO_NOT_SPACE, 1) + pynini.closure( - delete_extra_space + pynini.closure(NEMO_NOT_SPACE, 1) - ) - remove_extra_spaces |= ( - pynini.closure(pynutil.delete(" "), 1) - + pynini.closure(NEMO_NOT_SPACE, 1) - + pynini.closure(delete_extra_space + pynini.closure(NEMO_NOT_SPACE, 1)) - ) - - graph = pynini.compose(graph.optimize(), remove_extra_spaces).optimize() - self.fst = graph - no_digits = pynini.closure(pynini.difference(NEMO_CHAR, NEMO_DIGIT)) - self.fst_no_digits = pynini.compose(graph, no_digits).optimize() - - if far_file: - generator_main(far_file, {"tokenize_and_classify": self.fst}) - logging.info(f'ClassifyFst grammars are saved to {far_file}.') diff --git a/nemo_text_processing/text_normalization/en/taggers/whitelist.py b/nemo_text_processing/text_normalization/en/taggers/whitelist.py deleted file mode 100644 index 01d102da9a3c..000000000000 --- a/nemo_text_processing/text_normalization/en/taggers/whitelist.py +++ /dev/null @@ -1,150 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_CHAR, - NEMO_NOT_SPACE, - NEMO_SIGMA, - NEMO_UPPER, - SINGULAR_TO_PLURAL, - GraphFst, - convert_space, -) -from nemo_text_processing.text_normalization.en.taggers.roman import get_names -from nemo_text_processing.text_normalization.en.utils import ( - augment_labels_with_punct_at_end, - get_abs_path, - load_labels, -) -from pynini.lib import pynutil - - -class WhiteListFst(GraphFst): - """ - Finite state transducer for classifying whitelist, e.g. - misses -> tokens { name: "mrs" } - for non-deterministic case: "Dr. Abc" -> - tokens { name: "drive" } tokens { name: "Abc" } - tokens { name: "doctor" } tokens { name: "Abc" } - tokens { name: "Dr." } tokens { name: "Abc" } - This class has highest priority among all classifier grammars. Whitelisted tokens are defined and loaded from "data/whitelist.tsv". - - Args: - input_case: accepting either "lower_cased" or "cased" input. - deterministic: if True will provide a single transduction option, - for False multiple options (used for audio-based normalization) - input_file: path to a file with whitelist replacements - """ - - def __init__(self, input_case: str, deterministic: bool = True, input_file: str = None): - super().__init__(name="whitelist", kind="classify", deterministic=deterministic) - - def _get_whitelist_graph(input_case, file, keep_punct_add_end: bool = False): - whitelist = load_labels(file) - if input_case == "lower_cased": - whitelist = [[x.lower(), y] for x, y in whitelist] - else: - whitelist = [[x, y] for x, y in whitelist] - - if keep_punct_add_end: - whitelist.extend(augment_labels_with_punct_at_end(whitelist)) - - graph = pynini.string_map(whitelist) - return graph - - graph = _get_whitelist_graph(input_case, get_abs_path("data/whitelist/tts.tsv")) - graph |= pynini.compose( - pynini.difference(NEMO_SIGMA, pynini.accep("/")).optimize(), - _get_whitelist_graph(input_case, get_abs_path("data/whitelist/symbol.tsv")), - ).optimize() - - if deterministic: - names = get_names() - graph |= ( - pynini.cross(pynini.union("st", "St", "ST"), "Saint") - + pynini.closure(pynutil.delete(".")) - + pynini.accep(" ") - + names - ) - else: - graph |= _get_whitelist_graph( - input_case, get_abs_path("data/whitelist/alternatives.tsv"), keep_punct_add_end=True - ) - - for x in [".", ". "]: - graph |= ( - NEMO_UPPER - + pynini.closure(pynutil.delete(x) + NEMO_UPPER, 2) - + pynini.closure(pynutil.delete("."), 0, 1) - ) - - if not deterministic: - multiple_forms_whitelist_graph = get_formats(get_abs_path("data/whitelist/alternatives_all_format.tsv")) - graph |= multiple_forms_whitelist_graph - - graph_unit = pynini.string_file(get_abs_path("data/measure/unit.tsv")) | pynini.string_file( - get_abs_path("data/measure/unit_alternatives.tsv") - ) - graph_unit_plural = graph_unit @ SINGULAR_TO_PLURAL - units_graph = pynini.compose(NEMO_CHAR ** (3, ...), convert_space(graph_unit | graph_unit_plural)) - graph |= units_graph - - # convert to states only if comma is present before the abbreviation to avoid converting all caps words, - # e.g. "IN", "OH", "OK" - # TODO or only exclude above? - states = load_labels(get_abs_path("data/address/state.tsv")) - additional_options = [] - for x, y in states: - if input_case == "lower_cased": - x = x.lower() - additional_options.append((x, f"{y[0]}.{y[1:]}")) - if not deterministic: - additional_options.append((x, f"{y[0]}.{y[1:]}.")) - - states.extend(additional_options) - state_graph = pynini.string_map(states) - graph |= pynini.closure(NEMO_NOT_SPACE, 1) + pynini.union(", ", ",") + pynini.invert(state_graph).optimize() - - if input_file: - whitelist_provided = _get_whitelist_graph(input_case, input_file) - if not deterministic: - graph |= whitelist_provided - else: - graph = whitelist_provided - - self.graph = (convert_space(graph)).optimize() - - self.fst = (pynutil.insert("name: \"") + self.graph + pynutil.insert("\"")).optimize() - - -def get_formats(input_f, input_case="cased", is_default=True): - """ - Adds various abbreviation format options to the list of acceptable input forms - """ - multiple_formats = load_labels(input_f) - additional_options = [] - for x, y in multiple_formats: - if input_case == "lower_cased": - x = x.lower() - additional_options.append((f"{x}.", y)) # default "dr" -> doctor, this includes period "dr." -> doctor - additional_options.append((f"{x[0].upper() + x[1:]}", f"{y[0].upper() + y[1:]}")) # "Dr" -> Doctor - additional_options.append((f"{x[0].upper() + x[1:]}.", f"{y[0].upper() + y[1:]}")) # "Dr." -> Doctor - multiple_formats.extend(additional_options) - - if not is_default: - multiple_formats = [(x, f"|raw_start|{x}|raw_end||norm_start|{y}|norm_end|") for (x, y) in multiple_formats] - - multiple_formats = pynini.string_map(multiple_formats) - return multiple_formats diff --git a/nemo_text_processing/text_normalization/en/taggers/word.py b/nemo_text_processing/text_normalization/en/taggers/word.py deleted file mode 100644 index fa6a965aab2e..000000000000 --- a/nemo_text_processing/text_normalization/en/taggers/word.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - MIN_NEG_WEIGHT, - NEMO_ALPHA, - NEMO_DIGIT, - NEMO_NOT_SPACE, - NEMO_SIGMA, - GraphFst, - convert_space, - get_abs_path, -) -from nemo_text_processing.text_normalization.en.taggers.punctuation import PunctuationFst -from pynini.examples import plurals -from pynini.lib import pynutil - - -class WordFst(GraphFst): - """ - Finite state transducer for classifying word. Considers sentence boundary exceptions. - e.g. sleep -> tokens { name: "sleep" } - - Args: - punctuation: PunctuationFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, punctuation: GraphFst, deterministic: bool = True): - super().__init__(name="word", kind="classify", deterministic=deterministic) - - punct = PunctuationFst().graph - default_graph = pynini.closure(pynini.difference(NEMO_NOT_SPACE, punct.project("input")), 1) - symbols_to_exclude = (pynini.union("$", "€", "₩", "£", "¥", "#", "%") | NEMO_DIGIT).optimize() - graph = pynini.closure(pynini.difference(NEMO_NOT_SPACE, symbols_to_exclude), 1) - graph = pynutil.add_weight(graph, MIN_NEG_WEIGHT) | default_graph - - # leave phones of format [HH AH0 L OW1] untouched - phoneme_unit = pynini.closure(NEMO_ALPHA, 1) + pynini.closure(NEMO_DIGIT) - phoneme = ( - pynini.accep(pynini.escape("[")) - + pynini.closure(phoneme_unit + pynini.accep(" ")) - + phoneme_unit - + pynini.accep(pynini.escape("]")) - ) - - # leave IPA phones of format [ˈdoʊv] untouched, single words and sentences with punctuation marks allowed - punct_marks = pynini.union(*punctuation.punct_marks).optimize() - stress = pynini.union("ˈ", "'", "ˌ") - ipa_phoneme_unit = pynini.string_file(get_abs_path("data/whitelist/ipa_symbols.tsv")) - # word in ipa form - ipa_phonemes = ( - pynini.closure(stress, 0, 1) - + pynini.closure(ipa_phoneme_unit, 1) - + pynini.closure(stress | ipa_phoneme_unit) - ) - # allow sentences of words in IPA format separated with spaces or punct marks - delim = (punct_marks | pynini.accep(" ")) ** (1, ...) - ipa_phonemes = ipa_phonemes + pynini.closure(delim + ipa_phonemes) + pynini.closure(delim, 0, 1) - ipa_phonemes = (pynini.accep(pynini.escape("[")) + ipa_phonemes + pynini.accep(pynini.escape("]"))).optimize() - - if not deterministic: - phoneme = ( - pynini.accep(pynini.escape("[")) - + pynini.closure(pynini.accep(" "), 0, 1) - + pynini.closure(phoneme_unit + pynini.accep(" ")) - + phoneme_unit - + pynini.closure(pynini.accep(" "), 0, 1) - + pynini.accep(pynini.escape("]")) - ).optimize() - ipa_phonemes = ( - pynini.accep(pynini.escape("[")) + ipa_phonemes + pynini.accep(pynini.escape("]")) - ).optimize() - - phoneme |= ipa_phonemes - self.graph = plurals._priority_union(convert_space(phoneme.optimize()), graph, NEMO_SIGMA) - self.fst = (pynutil.insert("name: \"") + self.graph + pynutil.insert("\"")).optimize() diff --git a/nemo_text_processing/text_normalization/en/utils.py b/nemo_text_processing/text_normalization/en/utils.py deleted file mode 100644 index 31d9ec635a15..000000000000 --- a/nemo_text_processing/text_normalization/en/utils.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import csv -import os - - -def get_abs_path(rel_path): - """ - Get absolute path - - Args: - rel_path: relative path to this file - - Returns absolute path - """ - return os.path.dirname(os.path.abspath(__file__)) + '/' + rel_path - - -def load_labels(abs_path): - """ - loads relative path file as dictionary - - Args: - abs_path: absolute path - - Returns dictionary of mappings - """ - with open(abs_path, encoding="utf-8") as label_tsv: - labels = list(csv.reader(label_tsv, delimiter="\t")) - return labels - - -def augment_labels_with_punct_at_end(labels): - """ - augments labels: if key ends on a punctuation that value does not have, add a new label - where the value maintains the punctuation - - Args: - labels : input labels - Returns: - additional labels - """ - res = [] - for label in labels: - if len(label) > 1: - if label[0][-1] == "." and label[1][-1] != ".": - res.append([label[0], label[1] + "."] + label[2:]) - return res diff --git a/nemo_text_processing/text_normalization/en/verbalizers/__init__.py b/nemo_text_processing/text_normalization/en/verbalizers/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/text_normalization/en/verbalizers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/en/verbalizers/abbreviation.py b/nemo_text_processing/text_normalization/en/verbalizers/abbreviation.py deleted file mode 100644 index 191792431c18..000000000000 --- a/nemo_text_processing/text_normalization/en/verbalizers/abbreviation.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst -from pynini.lib import pynutil - - -class AbbreviationFst(GraphFst): - """ - Finite state transducer for verbalizing abbreviations - e.g. tokens { abbreviation { value: "A B C" } } -> "ABC" - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="abbreviation", kind="verbalize", deterministic=deterministic) - - graph = pynutil.delete("value: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/en/verbalizers/cardinal.py b/nemo_text_processing/text_normalization/en/verbalizers/cardinal.py deleted file mode 100644 index 99531a0962d9..000000000000 --- a/nemo_text_processing/text_normalization/en/verbalizers/cardinal.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class CardinalFst(GraphFst): - """ - Finite state transducer for verbalizing cardinal, e.g. - cardinal { negative: "true" integer: "23" } -> minus twenty three - - Args: - deterministic: if True will provide a single transduction option, - for False multiple options (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="cardinal", kind="verbalize", deterministic=deterministic) - - self.optional_sign = pynini.cross("negative: \"true\"", "minus ") - if not deterministic: - self.optional_sign |= pynini.cross("negative: \"true\"", "negative ") - self.optional_sign = pynini.closure(self.optional_sign + delete_space, 0, 1) - - integer = pynini.closure(NEMO_NOT_QUOTE) - - self.integer = delete_space + pynutil.delete("\"") + integer + pynutil.delete("\"") - integer = pynutil.delete("integer:") + self.integer - - self.numbers = self.optional_sign + integer - delete_tokens = self.delete_tokens(self.numbers) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/en/verbalizers/date.py b/nemo_text_processing/text_normalization/en/verbalizers/date.py deleted file mode 100644 index 191d0106343d..000000000000 --- a/nemo_text_processing/text_normalization/en/verbalizers/date.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_NOT_QUOTE, - NEMO_SIGMA, - GraphFst, - delete_extra_space, - delete_space, -) -from pynini.examples import plurals -from pynini.lib import pynutil - - -class DateFst(GraphFst): - """ - Finite state transducer for verbalizing date, e.g. - date { month: "february" day: "five" year: "twenty twelve" preserve_order: true } -> february fifth twenty twelve - date { day: "five" month: "february" year: "twenty twelve" preserve_order: true } -> the fifth of february twenty twelve - - Args: - ordinal: OrdinalFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, ordinal: GraphFst, deterministic: bool = True, lm: bool = False): - super().__init__(name="date", kind="verbalize", deterministic=deterministic) - - month = pynini.closure(NEMO_NOT_QUOTE, 1) - day_cardinal = ( - pynutil.delete("day:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - day = day_cardinal @ ordinal.suffix - - month = pynutil.delete("month:") + delete_space + pynutil.delete("\"") + month + pynutil.delete("\"") - - year = ( - pynutil.delete("year:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + delete_space - + pynutil.delete("\"") - ) - - # month (day) year - graph_mdy = ( - month + pynini.closure(delete_extra_space + day, 0, 1) + pynini.closure(delete_extra_space + year, 0, 1) - ) - # may 5 -> may five - if not deterministic and not lm: - graph_mdy |= ( - month - + pynini.closure(delete_extra_space + day_cardinal, 0, 1) - + pynini.closure(delete_extra_space + year, 0, 1) - ) - - # day month year - graph_dmy = ( - pynutil.insert("the ") - + day - + delete_extra_space - + pynutil.insert("of ") - + month - + pynini.closure(delete_extra_space + year, 0, 1) - ) - - optional_preserve_order = pynini.closure( - pynutil.delete("preserve_order:") + delete_space + pynutil.delete("true") + delete_space - | pynutil.delete("field_order:") - + delete_space - + pynutil.delete("\"") - + NEMO_NOT_QUOTE - + pynutil.delete("\"") - + delete_space - ) - - final_graph = ( - (plurals._priority_union(graph_mdy, pynutil.add_weight(graph_dmy, 0.0001), NEMO_SIGMA) | year) - + delete_space - + optional_preserve_order - ) - delete_tokens = self.delete_tokens(final_graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/en/verbalizers/decimal.py b/nemo_text_processing/text_normalization/en/verbalizers/decimal.py deleted file mode 100644 index 787bcea63536..000000000000 --- a/nemo_text_processing/text_normalization/en/verbalizers/decimal.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space, insert_space -from pynini.lib import pynutil - - -class DecimalFst(GraphFst): - """ - Finite state transducer for verbalizing decimal, e.g. - decimal { negative: "true" integer_part: "twelve" fractional_part: "five o o six" quantity: "billion" } -> minus twelve point five o o six billion - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, cardinal, deterministic: bool = True): - super().__init__(name="decimal", kind="verbalize", deterministic=deterministic) - self.optional_sign = pynini.cross("negative: \"true\"", "minus ") - if not deterministic: - self.optional_sign |= pynini.cross("negative: \"true\"", "negative ") - self.optional_sign = pynini.closure(self.optional_sign + delete_space, 0, 1) - self.integer = pynutil.delete("integer_part:") + cardinal.integer - self.optional_integer = pynini.closure(self.integer + delete_space + insert_space, 0, 1) - self.fractional_default = ( - pynutil.delete("fractional_part:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - - self.fractional = pynutil.insert("point ") + self.fractional_default - - self.quantity = ( - delete_space - + insert_space - + pynutil.delete("quantity:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - self.optional_quantity = pynini.closure(self.quantity, 0, 1) - - graph = self.optional_sign + ( - self.integer - | (self.integer + self.quantity) - | (self.optional_integer + self.fractional + self.optional_quantity) - ) - - self.numbers = graph - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/en/verbalizers/electronic.py b/nemo_text_processing/text_normalization/en/verbalizers/electronic.py deleted file mode 100644 index 884f125b0913..000000000000 --- a/nemo_text_processing/text_normalization/en/verbalizers/electronic.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_NOT_QUOTE, - NEMO_NOT_SPACE, - NEMO_SIGMA, - TO_UPPER, - GraphFst, - delete_extra_space, - delete_space, - insert_space, -) -from nemo_text_processing.text_normalization.en.utils import get_abs_path -from pynini.examples import plurals -from pynini.lib import pynutil - - -class ElectronicFst(GraphFst): - """ - Finite state transducer for verbalizing electronic - e.g. tokens { electronic { username: "cdf1" domain: "abc.edu" } } -> c d f one at a b c dot e d u - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="electronic", kind="verbalize", deterministic=deterministic) - graph_digit_no_zero = pynini.invert(pynini.string_file(get_abs_path("data/number/digit.tsv"))).optimize() - graph_zero = pynini.cross("0", "zero") - - if not deterministic: - graph_zero |= pynini.cross("0", "o") | pynini.cross("0", "oh") - - graph_digit = graph_digit_no_zero | graph_zero - graph_symbols = pynini.string_file(get_abs_path("data/electronic/symbol.tsv")).optimize() - - default_chars_symbols = pynini.cdrewrite( - pynutil.insert(" ") + (graph_symbols | graph_digit) + pynutil.insert(" "), "", "", NEMO_SIGMA - ) - default_chars_symbols = pynini.compose( - pynini.closure(NEMO_NOT_SPACE), default_chars_symbols.optimize() - ).optimize() - - user_name = ( - pynutil.delete("username:") - + delete_space - + pynutil.delete("\"") - + default_chars_symbols - + pynutil.delete("\"") - ) - - domain_common = pynini.string_file(get_abs_path("data/electronic/domain.tsv")) - - domain = ( - default_chars_symbols - + insert_space - + plurals._priority_union( - domain_common, pynutil.add_weight(pynini.cross(".", "dot"), weight=0.0001), NEMO_SIGMA - ) - + pynini.closure( - insert_space + (pynini.cdrewrite(TO_UPPER, "", "", NEMO_SIGMA) @ default_chars_symbols), 0, 1 - ) - ) - domain = ( - pynutil.delete("domain:") - + delete_space - + pynutil.delete("\"") - + domain - + delete_space - + pynutil.delete("\"") - ).optimize() - - protocol = pynutil.delete("protocol: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - graph = ( - pynini.closure(protocol + delete_space, 0, 1) - + pynini.closure(user_name + delete_space + pynutil.insert(" at ") + delete_space, 0, 1) - + domain - + delete_space - ).optimize() @ pynini.cdrewrite(delete_extra_space, "", "", NEMO_SIGMA) - - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/en/verbalizers/fraction.py b/nemo_text_processing/text_normalization/en/verbalizers/fraction.py deleted file mode 100644 index d0c5dc2b1648..000000000000 --- a/nemo_text_processing/text_normalization/en/verbalizers/fraction.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, NEMO_SIGMA, GraphFst, insert_space -from nemo_text_processing.text_normalization.en.verbalizers.ordinal import OrdinalFst -from pynini.examples import plurals -from pynini.lib import pynutil - - -class FractionFst(GraphFst): - """ - Finite state transducer for verbalizing fraction - e.g. tokens { fraction { integer: "twenty three" numerator: "four" denominator: "five" } } -> - twenty three and four fifth - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True, lm: bool = False): - super().__init__(name="fraction", kind="verbalize", deterministic=deterministic) - suffix = OrdinalFst().suffix - - integer = pynutil.delete("integer_part: \"") + pynini.closure(NEMO_NOT_QUOTE) + pynutil.delete("\" ") - denominator_one = pynini.cross("denominator: \"one\"", "over one") - denominator_half = pynini.cross("denominator: \"two\"", "half") - denominator_quarter = pynini.cross("denominator: \"four\"", "quarter") - - denominator_rest = ( - pynutil.delete("denominator: \"") + pynini.closure(NEMO_NOT_QUOTE) @ suffix + pynutil.delete("\"") - ) - - denominators = plurals._priority_union( - denominator_one, - plurals._priority_union( - denominator_half, - plurals._priority_union(denominator_quarter, denominator_rest, NEMO_SIGMA), - NEMO_SIGMA, - ), - NEMO_SIGMA, - ).optimize() - if not deterministic: - denominators |= pynutil.delete("denominator: \"") + (pynini.accep("four") @ suffix) + pynutil.delete("\"") - - numerator_one = pynutil.delete("numerator: \"") + pynini.accep("one") + pynutil.delete("\" ") - numerator_one = numerator_one + insert_space + denominators - numerator_rest = ( - pynutil.delete("numerator: \"") - + (pynini.closure(NEMO_NOT_QUOTE) - pynini.accep("one")) - + pynutil.delete("\" ") - ) - numerator_rest = numerator_rest + insert_space + denominators - numerator_rest @= pynini.cdrewrite( - plurals._priority_union(pynini.cross("half", "halves"), pynutil.insert("s"), NEMO_SIGMA), - "", - "[EOS]", - NEMO_SIGMA, - ) - - graph = numerator_one | numerator_rest - - conjunction = pynutil.insert("and ") - if not deterministic and not lm: - conjunction = pynini.closure(conjunction, 0, 1) - - integer = pynini.closure(integer + insert_space + conjunction, 0, 1) - - graph = integer + graph - graph @= pynini.cdrewrite( - pynini.cross("and one half", "and a half") | pynini.cross("over ones", "over one"), "", "[EOS]", NEMO_SIGMA - ) - - self.graph = graph - delete_tokens = self.delete_tokens(self.graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/en/verbalizers/measure.py b/nemo_text_processing/text_normalization/en/verbalizers/measure.py deleted file mode 100644 index e4a23b3ff6f4..000000000000 --- a/nemo_text_processing/text_normalization/en/verbalizers/measure.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space, insert_space -from pynini.lib import pynutil - - -class MeasureFst(GraphFst): - """ - Finite state transducer for verbalizing measure, e.g. - measure { negative: "true" cardinal { integer: "twelve" } units: "kilograms" } -> minus twelve kilograms - measure { decimal { integer_part: "twelve" fractional_part: "five" } units: "kilograms" } -> twelve point five kilograms - tokens { measure { units: "covid" decimal { integer_part: "nineteen" fractional_part: "five" } } } -> covid nineteen point five - - Args: - decimal: DecimalFst - cardinal: CardinalFst - fraction: FractionFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, decimal: GraphFst, cardinal: GraphFst, fraction: GraphFst, deterministic: bool = True): - super().__init__(name="measure", kind="verbalize", deterministic=deterministic) - optional_sign = cardinal.optional_sign - unit = ( - pynutil.delete("units: \"") - + pynini.difference(pynini.closure(NEMO_NOT_QUOTE, 1), pynini.union("address", "math")) - + pynutil.delete("\"") - + delete_space - ) - - if not deterministic: - unit |= pynini.compose(unit, pynini.cross(pynini.union("inch", "inches"), "\"")) - - graph_decimal = ( - pynutil.delete("decimal {") - + delete_space - + optional_sign - + delete_space - + decimal.numbers - + delete_space - + pynutil.delete("}") - ) - graph_cardinal = ( - pynutil.delete("cardinal {") - + delete_space - + optional_sign - + delete_space - + cardinal.numbers - + delete_space - + pynutil.delete("}") - ) - - graph_fraction = ( - pynutil.delete("fraction {") + delete_space + fraction.graph + delete_space + pynutil.delete("}") - ) - - graph = (graph_cardinal | graph_decimal | graph_fraction) + delete_space + insert_space + unit - - # SH adds "preserve_order: true" by default - preserve_order = pynutil.delete("preserve_order:") + delete_space + pynutil.delete("true") + delete_space - graph |= unit + insert_space + (graph_cardinal | graph_decimal) + delete_space + pynini.closure(preserve_order) - # for only unit - graph |= ( - pynutil.delete("cardinal { integer: \"-\"") - + delete_space - + pynutil.delete("}") - + delete_space - + unit - + pynini.closure(preserve_order) - ) - address = ( - pynutil.delete("units: \"address\" ") - + delete_space - + graph_cardinal - + delete_space - + pynini.closure(preserve_order) - ) - math = ( - pynutil.delete("units: \"math\" ") - + delete_space - + graph_cardinal - + delete_space - + pynini.closure(preserve_order) - ) - graph |= address | math - - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/en/verbalizers/money.py b/nemo_text_processing/text_normalization/en/verbalizers/money.py deleted file mode 100644 index b3cbc4a31494..000000000000 --- a/nemo_text_processing/text_normalization/en/verbalizers/money.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_NOT_QUOTE, - GraphFst, - delete_extra_space, - delete_preserve_order, -) -from pynini.lib import pynutil - - -class MoneyFst(GraphFst): - """ - Finite state transducer for verbalizing money, e.g. - money { integer_part: "twelve" fractional_part: "o five" currency: "dollars" } -> twelve o five dollars - - Args: - decimal: DecimalFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, decimal: GraphFst, deterministic: bool = True): - super().__init__(name="money", kind="verbalize", deterministic=deterministic) - keep_space = pynini.accep(" ") - maj = pynutil.delete("currency_maj: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - min = pynutil.delete("currency_min: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - - fractional_part = ( - pynutil.delete("fractional_part: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - ) - - integer_part = decimal.integer - - # *** currency_maj - graph_integer = integer_part + keep_space + maj - - # *** currency_maj + (***) | ((and) *** current_min) - fractional = fractional_part + delete_extra_space + min - - if not deterministic: - fractional |= pynutil.insert("and ") + fractional - - graph_integer_with_minor = integer_part + keep_space + maj + keep_space + fractional + delete_preserve_order - - # *** point *** currency_maj - graph_decimal = decimal.numbers + keep_space + maj - - # *** current_min - graph_minor = fractional_part + delete_extra_space + min + delete_preserve_order - - graph = graph_integer | graph_integer_with_minor | graph_decimal | graph_minor - - if not deterministic: - graph |= graph_integer + delete_preserve_order - - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/en/verbalizers/ordinal.py b/nemo_text_processing/text_normalization/en/verbalizers/ordinal.py deleted file mode 100644 index c64579ae5b46..000000000000 --- a/nemo_text_processing/text_normalization/en/verbalizers/ordinal.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, NEMO_SIGMA, GraphFst, delete_space -from nemo_text_processing.text_normalization.en.utils import get_abs_path -from pynini.lib import pynutil - - -class OrdinalFst(GraphFst): - """ - Finite state transducer for verbalizing ordinal, e.g. - ordinal { integer: "thirteen" } } -> thirteenth - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="ordinal", kind="verbalize", deterministic=deterministic) - - graph_digit = pynini.string_file(get_abs_path("data/ordinal/digit.tsv")).invert() - graph_teens = pynini.string_file(get_abs_path("data/ordinal/teen.tsv")).invert() - - graph = ( - pynutil.delete("integer:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - convert_rest = pynutil.insert("th") - - suffix = pynini.cdrewrite( - graph_digit | graph_teens | pynini.cross("ty", "tieth") | convert_rest, "", "[EOS]", NEMO_SIGMA, - ).optimize() - self.graph = pynini.compose(graph, suffix) - self.suffix = suffix - delete_tokens = self.delete_tokens(self.graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/en/verbalizers/post_processing.py b/nemo_text_processing/text_normalization/en/verbalizers/post_processing.py deleted file mode 100644 index f3da3c97f2e3..000000000000 --- a/nemo_text_processing/text_normalization/en/verbalizers/post_processing.py +++ /dev/null @@ -1,182 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - MIN_NEG_WEIGHT, - NEMO_ALPHA, - NEMO_CHAR, - NEMO_SIGMA, - NEMO_SPACE, - generator_main, -) -from nemo_text_processing.text_normalization.en.taggers.punctuation import PunctuationFst -from pynini.lib import pynutil - -from nemo.utils import logging - - -class PostProcessingFst: - """ - Finite state transducer that post-processing an entire sentence after verbalization is complete, e.g. - removes extra spaces around punctuation marks " ( one hundred and twenty three ) " -> "(one hundred and twenty three)" - - Args: - cache_dir: path to a dir with .far grammar file. Set to None to avoid using cache. - overwrite_cache: set to True to overwrite .far files - """ - - def __init__(self, cache_dir: str = None, overwrite_cache: bool = False): - - far_file = None - if cache_dir is not None and cache_dir != "None": - os.makedirs(cache_dir, exist_ok=True) - far_file = os.path.join(cache_dir, "en_tn_post_processing.far") - if not overwrite_cache and far_file and os.path.exists(far_file): - self.fst = pynini.Far(far_file, mode="r")["post_process_graph"] - logging.info(f'Post processing graph was restored from {far_file}.') - else: - self.set_punct_dict() - self.fst = self.get_punct_postprocess_graph() - - if far_file: - generator_main(far_file, {"post_process_graph": self.fst}) - - def set_punct_dict(self): - self.punct_marks = { - "'": [ - "'", - '´', - 'ʹ', - 'ʻ', - 'ʼ', - 'ʽ', - 'ʾ', - 'ˈ', - 'ˊ', - 'ˋ', - '˴', - 'ʹ', - '΄', - '՚', - '՝', - 'י', - '׳', - 'ߴ', - 'ߵ', - 'ᑊ', - 'ᛌ', - '᾽', - '᾿', - '`', - '´', - '῾', - '‘', - '’', - '‛', - '′', - '‵', - 'ꞌ', - ''', - '`', - '𖽑', - '𖽒', - ], - } - - def get_punct_postprocess_graph(self): - """ - Returns graph to post process punctuation marks. - - {``} quotes are converted to {"}. Note, if there are spaces around single quote {'}, they will be kept. - By default, a space is added after a punctuation mark, and spaces are removed before punctuation marks. - """ - punct_marks_all = PunctuationFst().punct_marks - - # no_space_before_punct assume no space before them - quotes = ["'", "\"", "``", "«"] - dashes = ["-", "—"] - brackets = ["<", "{", "("] - open_close_single_quotes = [ - ("`", "`"), - ] - - open_close_double_quotes = [('"', '"'), ("``", "``"), ("“", "”")] - open_close_symbols = open_close_single_quotes + open_close_double_quotes - allow_space_before_punct = ["&"] + quotes + dashes + brackets + [k[0] for k in open_close_symbols] - - no_space_before_punct = [m for m in punct_marks_all if m not in allow_space_before_punct] - no_space_before_punct = pynini.union(*no_space_before_punct) - no_space_after_punct = pynini.union(*brackets) - delete_space = pynutil.delete(" ") - delete_space_optional = pynini.closure(delete_space, 0, 1) - - # non_punct allows space - # delete space before no_space_before_punct marks, if present - non_punct = pynini.difference(NEMO_CHAR, no_space_before_punct).optimize() - graph = ( - pynini.closure(non_punct) - + pynini.closure( - no_space_before_punct | pynutil.add_weight(delete_space + no_space_before_punct, MIN_NEG_WEIGHT) - ) - + pynini.closure(non_punct) - ) - graph = pynini.closure(graph).optimize() - graph = pynini.compose( - graph, pynini.cdrewrite(pynini.cross("``", '"'), "", "", NEMO_SIGMA).optimize() - ).optimize() - - # remove space after no_space_after_punct (even if there are no matching closing brackets) - no_space_after_punct = pynini.cdrewrite(delete_space, no_space_after_punct, NEMO_SIGMA, NEMO_SIGMA).optimize() - graph = pynini.compose(graph, no_space_after_punct).optimize() - - # remove space around text in quotes - single_quote = pynutil.add_weight(pynini.accep("`"), MIN_NEG_WEIGHT) - double_quotes = pynutil.add_weight(pynini.accep('"'), MIN_NEG_WEIGHT) - quotes_graph = ( - single_quote + delete_space_optional + NEMO_ALPHA + NEMO_SIGMA + delete_space_optional + single_quote - ).optimize() - - # this is to make sure multiple quotes are tagged from right to left without skipping any quotes in the left - not_alpha = pynini.difference(NEMO_CHAR, NEMO_ALPHA).optimize() | pynutil.add_weight( - NEMO_SPACE, MIN_NEG_WEIGHT - ) - end = pynini.closure(pynutil.add_weight(not_alpha, MIN_NEG_WEIGHT)) - quotes_graph |= ( - double_quotes - + delete_space_optional - + NEMO_ALPHA - + NEMO_SIGMA - + delete_space_optional - + double_quotes - + end - ) - - quotes_graph = pynutil.add_weight(quotes_graph, MIN_NEG_WEIGHT) - quotes_graph = NEMO_SIGMA + pynini.closure(NEMO_SIGMA + quotes_graph + NEMO_SIGMA) - - graph = pynini.compose(graph, quotes_graph).optimize() - - # remove space between a word and a single quote followed by s - remove_space_around_single_quote = pynini.cdrewrite( - delete_space_optional + pynini.union(*self.punct_marks["'"]) + delete_space, - NEMO_ALPHA, - pynini.union("s ", "s[EOS]"), - NEMO_SIGMA, - ) - - graph = pynini.compose(graph, remove_space_around_single_quote).optimize() - return graph diff --git a/nemo_text_processing/text_normalization/en/verbalizers/roman.py b/nemo_text_processing/text_normalization/en/verbalizers/roman.py deleted file mode 100644 index 43faebe769c1..000000000000 --- a/nemo_text_processing/text_normalization/en/verbalizers/roman.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst -from nemo_text_processing.text_normalization.en.verbalizers.ordinal import OrdinalFst -from pynini.lib import pynutil - - -class RomanFst(GraphFst): - """ - Finite state transducer for verbalizing roman numerals - e.g. tokens { roman { integer: "one" } } -> one - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="roman", kind="verbalize", deterministic=deterministic) - suffix = OrdinalFst().suffix - - cardinal = pynini.closure(NEMO_NOT_QUOTE) - ordinal = pynini.compose(cardinal, suffix) - - graph = ( - pynutil.delete("key_cardinal: \"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - + pynini.accep(" ") - + pynutil.delete("integer: \"") - + cardinal - + pynutil.delete("\"") - ).optimize() - - graph |= ( - pynutil.delete("default_cardinal: \"default\" integer: \"") + cardinal + pynutil.delete("\"") - ).optimize() - - graph |= ( - pynutil.delete("default_ordinal: \"default\" integer: \"") + ordinal + pynutil.delete("\"") - ).optimize() - - graph |= ( - pynutil.delete("key_the_ordinal: \"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - + pynini.accep(" ") - + pynutil.delete("integer: \"") - + pynini.closure(pynutil.insert("the "), 0, 1) - + ordinal - + pynutil.delete("\"") - ).optimize() - - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/en/verbalizers/telephone.py b/nemo_text_processing/text_normalization/en/verbalizers/telephone.py deleted file mode 100644 index 4af7bbb8771a..000000000000 --- a/nemo_text_processing/text_normalization/en/verbalizers/telephone.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space, insert_space -from pynini.lib import pynutil - - -class TelephoneFst(GraphFst): - """ - Finite state transducer for verbalizing telephone numbers, e.g. - telephone { country_code: "one" number_part: "one two three, one two three, five six seven eight" extension: "one" } - -> one, one two three, one two three, five six seven eight, one - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="telephone", kind="verbalize", deterministic=deterministic) - - optional_country_code = pynini.closure( - pynutil.delete("country_code: \"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - + delete_space - + insert_space, - 0, - 1, - ) - - number_part = ( - pynutil.delete("number_part: \"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynini.closure(pynutil.add_weight(pynutil.delete(" "), -0.0001), 0, 1) - + pynutil.delete("\"") - ) - - optional_extension = pynini.closure( - delete_space - + insert_space - + pynutil.delete("extension: \"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\""), - 0, - 1, - ) - - graph = optional_country_code + number_part + optional_extension - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/en/verbalizers/time.py b/nemo_text_processing/text_normalization/en/verbalizers/time.py deleted file mode 100644 index 518c7dfa2186..000000000000 --- a/nemo_text_processing/text_normalization/en/verbalizers/time.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_NOT_QUOTE, - NEMO_SIGMA, - GraphFst, - delete_space, - insert_space, -) -from pynini.lib import pynutil - - -class TimeFst(GraphFst): - """ - Finite state transducer for verbalizing time, e.g. - time { hours: "twelve" minutes: "thirty" suffix: "a m" zone: "e s t" } -> twelve thirty a m e s t - time { hours: "twelve" } -> twelve o'clock - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="time", kind="verbalize", deterministic=deterministic) - hour = ( - pynutil.delete("hours:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - minute = ( - pynutil.delete("minutes:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - suffix = ( - pynutil.delete("suffix:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - optional_suffix = pynini.closure(delete_space + insert_space + suffix, 0, 1) - zone = ( - pynutil.delete("zone:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - optional_zone = pynini.closure(delete_space + insert_space + zone, 0, 1) - second = ( - pynutil.delete("seconds:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - graph_hms = ( - hour - + pynutil.insert(" hours ") - + delete_space - + minute - + pynutil.insert(" minutes and ") - + delete_space - + second - + pynutil.insert(" seconds") - + optional_suffix - + optional_zone - ) - graph_hms @= pynini.cdrewrite( - pynutil.delete("o ") - | pynini.cross("one minutes", "one minute") - | pynini.cross("one seconds", "one second") - | pynini.cross("one hours", "one hour"), - pynini.union(" ", "[BOS]"), - "", - NEMO_SIGMA, - ) - graph = hour + delete_space + insert_space + minute + optional_suffix + optional_zone - graph |= hour + insert_space + pynutil.insert("o'clock") + optional_zone - graph |= hour + delete_space + insert_space + suffix + optional_zone - graph |= graph_hms - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/en/verbalizers/verbalize.py b/nemo_text_processing/text_normalization/en/verbalizers/verbalize.py deleted file mode 100644 index cd3b1404aa05..000000000000 --- a/nemo_text_processing/text_normalization/en/verbalizers/verbalize.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst -from nemo_text_processing.text_normalization.en.verbalizers.abbreviation import AbbreviationFst -from nemo_text_processing.text_normalization.en.verbalizers.cardinal import CardinalFst -from nemo_text_processing.text_normalization.en.verbalizers.date import DateFst -from nemo_text_processing.text_normalization.en.verbalizers.decimal import DecimalFst -from nemo_text_processing.text_normalization.en.verbalizers.electronic import ElectronicFst -from nemo_text_processing.text_normalization.en.verbalizers.fraction import FractionFst -from nemo_text_processing.text_normalization.en.verbalizers.measure import MeasureFst -from nemo_text_processing.text_normalization.en.verbalizers.money import MoneyFst -from nemo_text_processing.text_normalization.en.verbalizers.ordinal import OrdinalFst -from nemo_text_processing.text_normalization.en.verbalizers.roman import RomanFst -from nemo_text_processing.text_normalization.en.verbalizers.telephone import TelephoneFst -from nemo_text_processing.text_normalization.en.verbalizers.time import TimeFst -from nemo_text_processing.text_normalization.en.verbalizers.whitelist import WhiteListFst - - -class VerbalizeFst(GraphFst): - """ - Composes other verbalizer grammars. - For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - - Args: - deterministic: if True will provide a single transduction option, - for False multiple options (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="verbalize", kind="verbalize", deterministic=deterministic) - cardinal = CardinalFst(deterministic=deterministic) - cardinal_graph = cardinal.fst - decimal = DecimalFst(cardinal=cardinal, deterministic=deterministic) - decimal_graph = decimal.fst - ordinal = OrdinalFst(deterministic=deterministic) - ordinal_graph = ordinal.fst - fraction = FractionFst(deterministic=deterministic) - fraction_graph = fraction.fst - telephone_graph = TelephoneFst(deterministic=deterministic).fst - electronic_graph = ElectronicFst(deterministic=deterministic).fst - measure = MeasureFst(decimal=decimal, cardinal=cardinal, fraction=fraction, deterministic=deterministic) - measure_graph = measure.fst - time_graph = TimeFst(deterministic=deterministic).fst - date_graph = DateFst(ordinal=ordinal, deterministic=deterministic).fst - money_graph = MoneyFst(decimal=decimal, deterministic=deterministic).fst - whitelist_graph = WhiteListFst(deterministic=deterministic).fst - - graph = ( - time_graph - | date_graph - | money_graph - | measure_graph - | ordinal_graph - | decimal_graph - | cardinal_graph - | telephone_graph - | electronic_graph - | fraction_graph - | whitelist_graph - ) - - roman_graph = RomanFst(deterministic=deterministic).fst - graph |= roman_graph - - if not deterministic: - abbreviation_graph = AbbreviationFst(deterministic=deterministic).fst - graph |= abbreviation_graph - - self.fst = graph diff --git a/nemo_text_processing/text_normalization/en/verbalizers/verbalize_final.py b/nemo_text_processing/text_normalization/en/verbalizers/verbalize_final.py deleted file mode 100644 index cbb61156ba47..000000000000 --- a/nemo_text_processing/text_normalization/en/verbalizers/verbalize_final.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - GraphFst, - delete_extra_space, - delete_space, - generator_main, -) -from nemo_text_processing.text_normalization.en.verbalizers.verbalize import VerbalizeFst -from nemo_text_processing.text_normalization.en.verbalizers.word import WordFst -from pynini.lib import pynutil - -from nemo.utils import logging - - -class VerbalizeFinalFst(GraphFst): - """ - Finite state transducer that verbalizes an entire sentence, e.g. - tokens { name: "its" } tokens { time { hours: "twelve" minutes: "thirty" } } tokens { name: "now" } tokens { name: "." } -> its twelve thirty now . - - Args: - deterministic: if True will provide a single transduction option, - for False multiple options (used for audio-based normalization) - cache_dir: path to a dir with .far grammar file. Set to None to avoid using cache. - overwrite_cache: set to True to overwrite .far files - """ - - def __init__(self, deterministic: bool = True, cache_dir: str = None, overwrite_cache: bool = False): - super().__init__(name="verbalize_final", kind="verbalize", deterministic=deterministic) - - far_file = None - if cache_dir is not None and cache_dir != "None": - os.makedirs(cache_dir, exist_ok=True) - far_file = os.path.join(cache_dir, f"en_tn_{deterministic}_deterministic_verbalizer.far") - if not overwrite_cache and far_file and os.path.exists(far_file): - self.fst = pynini.Far(far_file, mode="r")["verbalize"] - logging.info(f'VerbalizeFinalFst graph was restored from {far_file}.') - else: - verbalize = VerbalizeFst(deterministic=deterministic).fst - word = WordFst(deterministic=deterministic).fst - types = verbalize | word - - if deterministic: - graph = ( - pynutil.delete("tokens") - + delete_space - + pynutil.delete("{") - + delete_space - + types - + delete_space - + pynutil.delete("}") - ) - else: - graph = delete_space + types + delete_space - - graph = delete_space + pynini.closure(graph + delete_extra_space) + graph + delete_space - - self.fst = graph.optimize() - if far_file: - generator_main(far_file, {"verbalize": self.fst}) - logging.info(f"VerbalizeFinalFst grammars are saved to {far_file}.") diff --git a/nemo_text_processing/text_normalization/en/verbalizers/whitelist.py b/nemo_text_processing/text_normalization/en/verbalizers/whitelist.py deleted file mode 100644 index 96aa2075b2e1..000000000000 --- a/nemo_text_processing/text_normalization/en/verbalizers/whitelist.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_CHAR, NEMO_SIGMA, GraphFst, delete_space -from pynini.lib import pynutil - - -class WhiteListFst(GraphFst): - """ - Finite state transducer for verbalizing whitelist - e.g. tokens { name: "misses" } } -> misses - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="whitelist", kind="verbalize", deterministic=deterministic) - graph = ( - pynutil.delete("name:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_CHAR - " ", 1) - + pynutil.delete("\"") - ) - graph = graph @ pynini.cdrewrite(pynini.cross(u"\u00A0", " "), "", "", NEMO_SIGMA) - self.fst = graph.optimize() diff --git a/nemo_text_processing/text_normalization/en/verbalizers/word.py b/nemo_text_processing/text_normalization/en/verbalizers/word.py deleted file mode 100644 index e124f42ff16e..000000000000 --- a/nemo_text_processing/text_normalization/en/verbalizers/word.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_CHAR, NEMO_SIGMA, GraphFst, delete_space -from pynini.lib import pynutil - - -class WordFst(GraphFst): - """ - Finite state transducer for verbalizing word - e.g. tokens { name: "sleep" } -> sleep - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="word", kind="verbalize", deterministic=deterministic) - chars = pynini.closure(NEMO_CHAR - " ", 1) - char = pynutil.delete("name:") + delete_space + pynutil.delete("\"") + chars + pynutil.delete("\"") - graph = char @ pynini.cdrewrite(pynini.cross(u"\u00A0", " "), "", "", NEMO_SIGMA) - - self.fst = graph.optimize() diff --git a/nemo_text_processing/text_normalization/es/README.md b/nemo_text_processing/text_normalization/es/README.md deleted file mode 100644 index ec946a614da8..000000000000 --- a/nemo_text_processing/text_normalization/es/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Localization Note - -Depending on locale, Spanish number strings will vary in formatting. In the EU and South American countries, it is common to use a period (".") or space to delineate groupings of three -digits. e.g. - `1.000.000` -> "un millón" - `1 000 000` -> "un millón" - -and commas (",") to seperate cardinal and decimal strings. e.g. - - `1,00` -> "uno coma cero cero" - -While Central and Northern America will use commas (",") to delineate groupings of three digits, e.g. - `1,000,000` -> "un millón" - -and periods (".") to seperate cardinal and decimal strings. e.g. - - `1.00` -> "uno coma cero cero" - -As inclusion of both forms will create inherrent ambiguity for verbalization, this module defaults to the former formatting (periods for cardinal delineation and commas for decimals). - -To toggle the alternate formatting, you may edit the `LOCALIZATION` variable in `nemo_text_processing.text_normalization.es.__init__` with the value of `'am'`. This will perform necessary -adjustments to all affected classes. \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/__init__.py b/nemo_text_processing/text_normalization/es/data/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/es/data/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/es/data/dates/__init__.py b/nemo_text_processing/text_normalization/es/data/dates/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/es/data/dates/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/es/data/dates/months.tsv b/nemo_text_processing/text_normalization/es/data/dates/months.tsv deleted file mode 100644 index 7cfdf329aa5b..000000000000 --- a/nemo_text_processing/text_normalization/es/data/dates/months.tsv +++ /dev/null @@ -1,12 +0,0 @@ -1 enero -2 febrero -3 marzo -4 abril -5 mayo -6 junio -7 julio -8 agosto -9 septiembre -10 octubre -11 noviembre -12 diciembre \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/electronic/__init__.py b/nemo_text_processing/text_normalization/es/data/electronic/__init__.py deleted file mode 100644 index 2db92b257416..000000000000 --- a/nemo_text_processing/text_normalization/es/data/electronic/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/es/data/electronic/domain.tsv b/nemo_text_processing/text_normalization/es/data/electronic/domain.tsv deleted file mode 100644 index 9d71632ea9ce..000000000000 --- a/nemo_text_processing/text_normalization/es/data/electronic/domain.tsv +++ /dev/null @@ -1,14 +0,0 @@ -.com punto com -.org punto org -.gov punto gov -.uk punto uk -.fr punto fr -.net punto net -.br punto br -.in punto in -.ru punto ru -.de punto de -.it punto it -.es punto es -.mx punto mx -.us punto us \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/electronic/server_name.tsv b/nemo_text_processing/text_normalization/es/data/electronic/server_name.tsv deleted file mode 100644 index 9a0c75629486..000000000000 --- a/nemo_text_processing/text_normalization/es/data/electronic/server_name.tsv +++ /dev/null @@ -1,11 +0,0 @@ -gmail -nvidia -outlook -hotmail -yahoo -live -yandex -orange -wanadoo -web -comcast \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/electronic/symbols.tsv b/nemo_text_processing/text_normalization/es/data/electronic/symbols.tsv deleted file mode 100644 index c7790c3014c6..000000000000 --- a/nemo_text_processing/text_normalization/es/data/electronic/symbols.tsv +++ /dev/null @@ -1,22 +0,0 @@ -. punto -- guión -_ barra baja -! signo de exclamación -# almohadilla -$ signo de dólar -% signo de porcentaje -& et -' comilla -* asterisco -+ signo más -/ barra -= signo igual -? signo de interrogación -^ acento circunflej -` acento grave -{ llave izquierda -| pleca -} llave derecha -~ tilde -, coma -: dos puntos \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/fractions/__init__.py b/nemo_text_processing/text_normalization/es/data/fractions/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/es/data/fractions/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/es/data/fractions/ordinal_exceptions.tsv b/nemo_text_processing/text_normalization/es/data/fractions/ordinal_exceptions.tsv deleted file mode 100644 index 32cc3930a0e6..000000000000 --- a/nemo_text_processing/text_normalization/es/data/fractions/ordinal_exceptions.tsv +++ /dev/null @@ -1,2 +0,0 @@ -segundo medio -tercero tercio \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/fractions/powers_of_ten.tsv b/nemo_text_processing/text_normalization/es/data/fractions/powers_of_ten.tsv deleted file mode 100644 index 021dc0a101e0..000000000000 --- a/nemo_text_processing/text_normalization/es/data/fractions/powers_of_ten.tsv +++ /dev/null @@ -1,9 +0,0 @@ -millón millonésimo -millones millonésimo -billón billonésimo -billones billonésimo -trillón trillonésimo -trillones trillonésimo -mil millones mil millonésimo -mil billones mil billonésimo -mil trillones mil trillonésimo \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/fractions/powers_of_ten_fem.tsv b/nemo_text_processing/text_normalization/es/data/fractions/powers_of_ten_fem.tsv deleted file mode 100644 index 9e1a4ca4ce9f..000000000000 --- a/nemo_text_processing/text_normalization/es/data/fractions/powers_of_ten_fem.tsv +++ /dev/null @@ -1,6 +0,0 @@ -décimo décima -centésimo centésima -milésimo milésima -millonésimo millonésima -billonésimo billonésima -trillonésimo trillonésima \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/measures/__init__.py b/nemo_text_processing/text_normalization/es/data/measures/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/es/data/measures/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/es/data/measures/measurements.tsv b/nemo_text_processing/text_normalization/es/data/measures/measurements.tsv deleted file mode 100644 index 13b00f4ca227..000000000000 --- a/nemo_text_processing/text_normalization/es/data/measures/measurements.tsv +++ /dev/null @@ -1,17 +0,0 @@ -cm centímetro -g gramo -h hora -kg kilogramo -km kilómetro -km² kilómetro cuadrado -l litro -m metro -m² metro cuadrado -m³ metro cubico -mph milla por hora -ml mililitro -mm milímetro -ms milisegundo -min minuto -% por ciento -s segundo \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/measures/measurements_plural_fem.tsv b/nemo_text_processing/text_normalization/es/data/measures/measurements_plural_fem.tsv deleted file mode 100644 index e8158d7e9f8c..000000000000 --- a/nemo_text_processing/text_normalization/es/data/measures/measurements_plural_fem.tsv +++ /dev/null @@ -1,3 +0,0 @@ -hora horas -milla por hora millas por hora -milla millas \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/measures/measurements_plural_masc.tsv b/nemo_text_processing/text_normalization/es/data/measures/measurements_plural_masc.tsv deleted file mode 100644 index 8376ad2e5acb..000000000000 --- a/nemo_text_processing/text_normalization/es/data/measures/measurements_plural_masc.tsv +++ /dev/null @@ -1,15 +0,0 @@ -centímetro centímetros -gramo gramos -kilogramo kilogramos -kilómetro kilómetros -kilómetro cuadrado kilómetros cuadrados -litro litros -metro metros -metro cuadrado metros cuadrados -metro cubico metros cubicos -mililitro mililitros -milímetro milímetros -milisegundo milisegundos -minuto minutos -segundo segundos -por ciento por ciento \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/money/__init__.py b/nemo_text_processing/text_normalization/es/data/money/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/es/data/money/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/es/data/money/currency_major.tsv b/nemo_text_processing/text_normalization/es/data/money/currency_major.tsv deleted file mode 100644 index 5950aab5ed91..000000000000 --- a/nemo_text_processing/text_normalization/es/data/money/currency_major.tsv +++ /dev/null @@ -1,29 +0,0 @@ -€ euro -$ dólar -£ libra -₩ won -nzd dólar de Nueva Zelanda -rs rupia -chf franco suizo -fr franco -dkk corona danesa -kr corona -fim marco finlandes -mk marco -aed dírham -dh dírham -dhs dírham -¥ yen -czk corona checa -kč corona checa -crc colón costarricense -₡ colón -hkd dólar de Hong Kong -awg florín -nok corona noruega -sek corona sueca -R$ real -BRL real -ESP peseta -₧ peseta -Ptas peseta \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/money/currency_minor.tsv b/nemo_text_processing/text_normalization/es/data/money/currency_minor.tsv deleted file mode 100644 index 2d559e1f9125..000000000000 --- a/nemo_text_processing/text_normalization/es/data/money/currency_minor.tsv +++ /dev/null @@ -1,4 +0,0 @@ -$ centavo -€ céntimo -£ penique -¥ centavo \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/money/currency_plural_fem.tsv b/nemo_text_processing/text_normalization/es/data/money/currency_plural_fem.tsv deleted file mode 100644 index c412456e03eb..000000000000 --- a/nemo_text_processing/text_normalization/es/data/money/currency_plural_fem.tsv +++ /dev/null @@ -1,8 +0,0 @@ -rupia rupias -libra libras -corona danesa coronas danesas -corona coronas -corona checa coronas checas -corona noruega coronas noruegas -corona sueca coronas suecas -peseta pesetas \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/money/currency_plural_masc.tsv b/nemo_text_processing/text_normalization/es/data/money/currency_plural_masc.tsv deleted file mode 100644 index 08c25eeab1b2..000000000000 --- a/nemo_text_processing/text_normalization/es/data/money/currency_plural_masc.tsv +++ /dev/null @@ -1,19 +0,0 @@ -euro euros -dólar dólares -dólar estadounidense dólares estadounidenses -won wones -dólar de Nueva Zelanda dólares de Nueva Zelanda -franco suizo francos suizos -franco francos -marco finlandes marcos finlandeses -marco marcos -dírham dírhams -dírham dírhams -dírham dírhams -yen yenes -colón costarricense colónes costarricenses -colón colónes -dólar de Hong Kong dólares de Hong Kong -centavo centavos -céntimo céntimos -penique peniques \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/numbers/__init__.py b/nemo_text_processing/text_normalization/es/data/numbers/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/es/data/numbers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/es/data/numbers/digit.tsv b/nemo_text_processing/text_normalization/es/data/numbers/digit.tsv deleted file mode 100644 index 462803637ed5..000000000000 --- a/nemo_text_processing/text_normalization/es/data/numbers/digit.tsv +++ /dev/null @@ -1,9 +0,0 @@ -un 1 -dos 2 -tres 3 -cuatro 4 -cinco 5 -seis 6 -siete 7 -ocho 8 -nueve 9 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/numbers/hundreds.tsv b/nemo_text_processing/text_normalization/es/data/numbers/hundreds.tsv deleted file mode 100644 index bf651cd0a978..000000000000 --- a/nemo_text_processing/text_normalization/es/data/numbers/hundreds.tsv +++ /dev/null @@ -1,8 +0,0 @@ -doscientos 2 -trescientos 3 -cuatrocientos 4 -quinientos 5 -seiscientos 6 -setecientos 7 -ochocientos 8 -novecientos 9 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/numbers/quantities.tsv b/nemo_text_processing/text_normalization/es/data/numbers/quantities.tsv deleted file mode 100644 index 58bce3e5dcff..000000000000 --- a/nemo_text_processing/text_normalization/es/data/numbers/quantities.tsv +++ /dev/null @@ -1,6 +0,0 @@ -millón -millones -billón -billones -trillón -trillones \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/numbers/teen.tsv b/nemo_text_processing/text_normalization/es/data/numbers/teen.tsv deleted file mode 100644 index 370526ea57c3..000000000000 --- a/nemo_text_processing/text_normalization/es/data/numbers/teen.tsv +++ /dev/null @@ -1,10 +0,0 @@ -diez 10 -once 11 -doce 12 -trece 13 -catorce 14 -quince 15 -dieciséis 16 -diecisiete 17 -dieciocho 18 -diecinueve 19 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/numbers/ties.tsv b/nemo_text_processing/text_normalization/es/data/numbers/ties.tsv deleted file mode 100644 index 51b21081b3e3..000000000000 --- a/nemo_text_processing/text_normalization/es/data/numbers/ties.tsv +++ /dev/null @@ -1,7 +0,0 @@ -treinta 3 -cuarenta 4 -cincuenta 5 -sesenta 6 -setenta 7 -ochenta 8 -noventa 9 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/numbers/twenties.tsv b/nemo_text_processing/text_normalization/es/data/numbers/twenties.tsv deleted file mode 100644 index c378a3ad9817..000000000000 --- a/nemo_text_processing/text_normalization/es/data/numbers/twenties.tsv +++ /dev/null @@ -1,10 +0,0 @@ -veinte 20 -veintiún 21 -veintidós 22 -veintitrés 23 -veinticuatro 24 -veinticinco 25 -veintiséis 26 -veintisiete 27 -veintiocho 28 -veintinueve 29 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/numbers/zero.tsv b/nemo_text_processing/text_normalization/es/data/numbers/zero.tsv deleted file mode 100644 index 945785283a51..000000000000 --- a/nemo_text_processing/text_normalization/es/data/numbers/zero.tsv +++ /dev/null @@ -1 +0,0 @@ -cero 0 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/ordinals/__init__.py b/nemo_text_processing/text_normalization/es/data/ordinals/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/es/data/ordinals/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/es/data/ordinals/digit.tsv b/nemo_text_processing/text_normalization/es/data/ordinals/digit.tsv deleted file mode 100644 index 08d86ac928b4..000000000000 --- a/nemo_text_processing/text_normalization/es/data/ordinals/digit.tsv +++ /dev/null @@ -1,11 +0,0 @@ -primero uno -primera una -primero un -segundo dos -tercero tres -cuarto cuatro -quinto cinco -sexto seis -séptimo siete -octavo ocho -noveno nueve \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/ordinals/hundreds.tsv b/nemo_text_processing/text_normalization/es/data/ordinals/hundreds.tsv deleted file mode 100644 index 65867fd84eca..000000000000 --- a/nemo_text_processing/text_normalization/es/data/ordinals/hundreds.tsv +++ /dev/null @@ -1,18 +0,0 @@ -centésimo ciento -centésimo cien -ducentésimo doscientos -ducentésima doscientas -tricentésimo trescientos -tricentésima trescientas -cuadringentésimo cuatrocientos -cuadringentésima cuatrocientas -quingentésimo quinientos -quingentésima quinientas -sexcentésimo seiscientos -sexcentésima seiscientas -septingentésimo setecientos -septingentésima setecientas -octingentésimo ochocientos -octingentésima ochocientas -noningentésimo novecientos -noningentésima novecientas \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/ordinals/teen.tsv b/nemo_text_processing/text_normalization/es/data/ordinals/teen.tsv deleted file mode 100644 index 1ffcfb2697d4..000000000000 --- a/nemo_text_processing/text_normalization/es/data/ordinals/teen.tsv +++ /dev/null @@ -1,10 +0,0 @@ -décimo diez -undécimo once -duodécimo doce -decimotercero trece -decimocuarto catorce -decimoquinto quince -decimosexto dieciséis -decimoséptimo diecisiete -decimooctavo dieciocho -decimonoveno diecinueve \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/ordinals/ties.tsv b/nemo_text_processing/text_normalization/es/data/ordinals/ties.tsv deleted file mode 100644 index 21c488ef2ab8..000000000000 --- a/nemo_text_processing/text_normalization/es/data/ordinals/ties.tsv +++ /dev/null @@ -1,8 +0,0 @@ -vigésimo veinte -trigésimo treinta -cuadragésimo cuarenta -quincuagésimo cincuenta -sexagésimo sesenta -septuagésimo setenta -octogésimo ochenta -nonagésimo noventa \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/ordinals/twenties.tsv b/nemo_text_processing/text_normalization/es/data/ordinals/twenties.tsv deleted file mode 100644 index e63a3e5825a3..000000000000 --- a/nemo_text_processing/text_normalization/es/data/ordinals/twenties.tsv +++ /dev/null @@ -1,11 +0,0 @@ -vigesimoprimero veintiuno -vigesimoprimera veintiuna -vigesimoprimero veintiún -vigesimosegundo veintidós -vigesimotercero veintitrés -vigesimocuarto veinticuatro -vigesimoquinto veinticinco -vigesimosexto veintiséis -vigesimoséptimo veintisiete -vigesimooctavo veintiocho -vigesimonoveno veintinueve \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/roman/__init__.py b/nemo_text_processing/text_normalization/es/data/roman/__init__.py deleted file mode 100644 index 2db92b257416..000000000000 --- a/nemo_text_processing/text_normalization/es/data/roman/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/es/data/roman/digit.tsv b/nemo_text_processing/text_normalization/es/data/roman/digit.tsv deleted file mode 100644 index 0610b4a54ff3..000000000000 --- a/nemo_text_processing/text_normalization/es/data/roman/digit.tsv +++ /dev/null @@ -1,9 +0,0 @@ -i 1 -ii 2 -iii 3 -iv 4 -v 5 -vi 6 -vii 7 -viii 8 -ix 9 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/roman/hundreds.tsv b/nemo_text_processing/text_normalization/es/data/roman/hundreds.tsv deleted file mode 100644 index cdbdb6814726..000000000000 --- a/nemo_text_processing/text_normalization/es/data/roman/hundreds.tsv +++ /dev/null @@ -1,9 +0,0 @@ -c 1 -cc 2 -ccc 3 -cd 4 -d 5 -dc 6 -dcc 7 -dccc 8 -cm 9 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/roman/ties.tsv b/nemo_text_processing/text_normalization/es/data/roman/ties.tsv deleted file mode 100644 index ac043aa146bf..000000000000 --- a/nemo_text_processing/text_normalization/es/data/roman/ties.tsv +++ /dev/null @@ -1,9 +0,0 @@ -x 1 -xx 2 -xxx 3 -xl 4 -l 5 -lx 6 -lxx 7 -lxxx 8 -xc 9 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/time/__init__.py b/nemo_text_processing/text_normalization/es/data/time/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/es/data/time/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/es/data/time/afternoon_times.tsv b/nemo_text_processing/text_normalization/es/data/time/afternoon_times.tsv deleted file mode 100644 index 25e75c8b30ec..000000000000 --- a/nemo_text_processing/text_normalization/es/data/time/afternoon_times.tsv +++ /dev/null @@ -1,4 +0,0 @@ -una -dos -tres -cuatro \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/time/alt_minutes.tsv b/nemo_text_processing/text_normalization/es/data/time/alt_minutes.tsv deleted file mode 100644 index 6bdff30993d4..000000000000 --- a/nemo_text_processing/text_normalization/es/data/time/alt_minutes.tsv +++ /dev/null @@ -1,2 +0,0 @@ -quince cuarto -treinta media \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/time/evening_times.tsv b/nemo_text_processing/text_normalization/es/data/time/evening_times.tsv deleted file mode 100644 index c77d4c7f1e25..000000000000 --- a/nemo_text_processing/text_normalization/es/data/time/evening_times.tsv +++ /dev/null @@ -1,7 +0,0 @@ -cinco -seis -siete -ocho -nueve -diez -once \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/time/hour_to_12.tsv b/nemo_text_processing/text_normalization/es/data/time/hour_to_12.tsv deleted file mode 100644 index e681afc6c25c..000000000000 --- a/nemo_text_processing/text_normalization/es/data/time/hour_to_12.tsv +++ /dev/null @@ -1,12 +0,0 @@ -1 12 -2 1 -3 2 -4 3 -5 4 -6 5 -7 6 -8 7 -9 8 -10 9 -11 10 -12 11 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/time/hour_to_24.tsv b/nemo_text_processing/text_normalization/es/data/time/hour_to_24.tsv deleted file mode 100644 index 13f43dc5b9b9..000000000000 --- a/nemo_text_processing/text_normalization/es/data/time/hour_to_24.tsv +++ /dev/null @@ -1,25 +0,0 @@ -1 0 -2 1 -3 2 -4 3 -5 4 -6 5 -7 6 -8 7 -9 8 -10 9 -11 10 -12 11 -13 12 -14 13 -15 14 -16 15 -17 16 -18 17 -19 18 -20 19 -21 20 -22 21 -23 22 -0 23 -1 24 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/time/hour_to_night.tsv b/nemo_text_processing/text_normalization/es/data/time/hour_to_night.tsv deleted file mode 100644 index b7765ac2a3c6..000000000000 --- a/nemo_text_processing/text_normalization/es/data/time/hour_to_night.tsv +++ /dev/null @@ -1,13 +0,0 @@ -12 0 -1 13 -2 14 -3 15 -4 16 -5 17 -6 18 -7 19 -8 20 -9 21 -10 22 -11 23 -12 24 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/time/minute_to.tsv b/nemo_text_processing/text_normalization/es/data/time/minute_to.tsv deleted file mode 100644 index edab4d5b03d6..000000000000 --- a/nemo_text_processing/text_normalization/es/data/time/minute_to.tsv +++ /dev/null @@ -1,59 +0,0 @@ -1 59 -2 58 -3 57 -4 56 -5 55 -6 54 -7 53 -8 52 -9 51 -10 50 -11 49 -12 48 -13 47 -14 46 -15 45 -16 44 -17 43 -18 42 -19 41 -20 40 -21 39 -22 38 -23 37 -24 36 -25 35 -26 34 -27 33 -28 32 -29 31 -30 30 -31 29 -32 28 -33 27 -34 26 -35 25 -36 24 -37 23 -38 22 -39 21 -40 20 -41 19 -42 18 -43 17 -44 16 -45 15 -46 14 -47 13 -48 12 -49 11 -50 10 -51 9 -52 8 -53 7 -54 6 -55 5 -56 4 -57 3 -58 2 -59 1 diff --git a/nemo_text_processing/text_normalization/es/data/time/morning_times.tsv b/nemo_text_processing/text_normalization/es/data/time/morning_times.tsv deleted file mode 100644 index e329184d2bd0..000000000000 --- a/nemo_text_processing/text_normalization/es/data/time/morning_times.tsv +++ /dev/null @@ -1,12 +0,0 @@ -un -una -dos -tres -cuatro -cinco -seis -siete -ocho -nueve -diez -once \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/time/time_suffix.tsv b/nemo_text_processing/text_normalization/es/data/time/time_suffix.tsv deleted file mode 100644 index 9ba6b1ceb170..000000000000 --- a/nemo_text_processing/text_normalization/es/data/time/time_suffix.tsv +++ /dev/null @@ -1,20 +0,0 @@ -p.m. pm -p. m. pm -p.m pm -p m. pm -pm pm -P.M. pm -P.M pm -P M. pm -P. M. pm -PM pm -a.m. am -a.m. am -a.m am -a m. am -am am -A.M. am -A.M am -A M. am -A. M. am -AM am \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/time/time_zone.tsv b/nemo_text_processing/text_normalization/es/data/time/time_zone.tsv deleted file mode 100644 index e310d8f3b921..000000000000 --- a/nemo_text_processing/text_normalization/es/data/time/time_zone.tsv +++ /dev/null @@ -1,7 +0,0 @@ -cst c s t -cet c e t -pst p s t -est e s t -pt p t -et e t -gmt g m t \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/data/whitelist.tsv b/nemo_text_processing/text_normalization/es/data/whitelist.tsv deleted file mode 100644 index ecf6dcfbb2d2..000000000000 --- a/nemo_text_processing/text_normalization/es/data/whitelist.tsv +++ /dev/null @@ -1,42 +0,0 @@ -ud. usted -Ud. usted -uds. ustedes -Uds. ustedes -Vd. vosotros -Vds. vosotros -vd. vosotros -vds. vosotros -D. Don -Da. Donna -Dr. Doctor -Dra. Doctora -dr. doctor -dra. doctora -d. don -da. donna -E este -EE. Estados Unidos -ee. estados unidos -Gob. gobierno -gob. gobierno -esq. esquina -Av. avenida -Avda. avenida -av. avenida -avda. avenida -O oeste -pág. página -p.ej. por ejemplo -Prof. profesor -Profa. profesora -prof. profesor -profa. profesora -S sur -N norte -Sr. señor -sr. señor -Sra. señora -sra. señora -Srta. señorita -srta. señorita -tel. teléfono \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/es/graph_utils.py b/nemo_text_processing/text_normalization/es/graph_utils.py deleted file mode 100644 index 7f0a7a34c939..000000000000 --- a/nemo_text_processing/text_normalization/es/graph_utils.py +++ /dev/null @@ -1,174 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_SIGMA, NEMO_SPACE -from nemo_text_processing.text_normalization.es import LOCALIZATION -from nemo_text_processing.text_normalization.es.utils import get_abs_path, load_labels -from pynini.lib import pynutil - -digits = pynini.project(pynini.string_file(get_abs_path("data/numbers/digit.tsv")), "input") -tens = pynini.project(pynini.string_file(get_abs_path("data/numbers/ties.tsv")), "input") -teens = pynini.project(pynini.string_file(get_abs_path("data/numbers/teen.tsv")), "input") -twenties = pynini.project(pynini.string_file(get_abs_path("data/numbers/twenties.tsv")), "input") -hundreds = pynini.project(pynini.string_file(get_abs_path("data/numbers/hundreds.tsv")), "input") - -accents = pynini.string_map([("á", "a"), ("é", "e"), ("í", "i"), ("ó", "o"), ("ú", "u")]) - -if LOCALIZATION == "am": # Setting localization for central and northern america formatting - cardinal_separator = pynini.string_map([",", NEMO_SPACE]) - decimal_separator = pynini.accep(".") -else: - cardinal_separator = pynini.string_map([".", NEMO_SPACE]) - decimal_separator = pynini.accep(",") - -ones = pynini.union("un", "ún") -fem_ones = pynini.union(pynini.cross("un", "una"), pynini.cross("ún", "una"), pynini.cross("uno", "una")) -one_to_one_hundred = pynini.union(digits, "uno", tens, teens, twenties, tens + pynini.accep(" y ") + digits) -fem_hundreds = hundreds @ pynini.cdrewrite(pynini.cross("ientos", "ientas"), "", "", NEMO_SIGMA) - - -def strip_accent(fst: 'pynini.FstLike') -> 'pynini.FstLike': - """ - Converts all accented vowels to non-accented equivalents - - Args: - fst: Any fst. Composes vowel conversion onto fst's output strings - """ - return fst @ pynini.cdrewrite(accents, "", "", NEMO_SIGMA) - - -def shift_cardinal_gender(fst: 'pynini.FstLike') -> 'pynini.FstLike': - """ - Applies gender conversion rules to a cardinal string. These include: rendering all masculine forms of "uno" (including apocopated forms) as "una" and - Converting all gendered numbers in the hundreds series (200,300,400...) to feminine equivalent (e.g. "doscientos" -> "doscientas"). Conversion only applies - to value place for <1000 and multiple of 1000. (e.g. "doscientos mil doscientos" -> "doscientas mil doscientas".) For place values greater than the thousands, there - is no gender shift as the higher powers of ten ("millones", "billones") are masculine nouns and any conversion would be formally - ungrammatical. - e.g. - "doscientos" -> "doscientas" - "doscientos mil" -> "doscientas mil" - "doscientos millones" -> "doscientos millones" - "doscientos mil millones" -> "doscientos mil millones" - "doscientos millones doscientos mil doscientos" -> "doscientos millones doscientas mil doscientas" - - Args: - fst: Any fst. Composes conversion onto fst's output strings - """ - before_mil = ( - NEMO_SPACE - + (pynini.accep("mil") | pynini.accep("milésimo")) - + pynini.closure(NEMO_SPACE + hundreds, 0, 1) - + pynini.closure(NEMO_SPACE + one_to_one_hundred, 0, 1) - + pynini.union(pynini.accep("[EOS]"), pynini.accep("\""), decimal_separator) - ) - before_double_digits = pynini.closure(NEMO_SPACE + one_to_one_hundred, 0, 1) + pynini.union( - pynini.accep("[EOS]"), pynini.accep("\"") - ) - - fem_allign = pynini.cdrewrite(fem_hundreds, "", before_mil, NEMO_SIGMA) # doscientas mil dosciento - fem_allign @= pynini.cdrewrite(fem_hundreds, "", before_double_digits, NEMO_SIGMA) # doscientas mil doscienta - - fem_allign @= pynini.cdrewrite( - fem_ones, "", pynini.union("[EOS]", "\"", decimal_separator), NEMO_SIGMA - ) # If before a quote or EOS, we know it's the end of a string - - return fst @ fem_allign - - -def shift_number_gender(fst: 'pynini.FstLike') -> 'pynini.FstLike': - """ - Performs gender conversion on all verbalized numbers in output. All values in the hundreds series (200,300,400) are changed to - feminine gender (e.g. "doscientos" -> "doscientas") and all forms of "uno" (including apocopated forms) are converted to "una". - This has no boundary restriction and will perform shift across all values in output string. - e.g. - "doscientos" -> "doscientas" - "doscientos millones" -> "doscientas millones" - "doscientos millones doscientos" -> "doscientas millones doscientas" - - Args: - fst: Any fst. Composes conversion onto fst's output strings - """ - fem_allign = pynini.cdrewrite(fem_hundreds, "", "", NEMO_SIGMA) - fem_allign @= pynini.cdrewrite( - fem_ones, "", pynini.union(NEMO_SPACE, pynini.accep("[EOS]"), pynini.accep("\"")), NEMO_SIGMA - ) # If before a quote or EOS, we know it's the end of a string - - return fst @ fem_allign - - -def strip_cardinal_apocope(fst: 'pynini.FstLike') -> 'pynini.FstLike': - """ - Reverts apocope on cardinal strings in line with formation rules. e.g. "un" -> "uno". Due to cardinal formation rules, this in effect only - affects strings where the final value is a variation of "un". - e.g. - "un" -> "uno" - "veintiún" -> "veintiuno" - - Args: - fst: Any fst. Composes conversion onto fst's output strings - """ - # Since cardinals use apocope by default for large values (e.g. "millón"), this only needs to act on the last instance of one - strip = pynini.cross("un", "uno") | pynini.cross("ún", "uno") - strip = pynini.cdrewrite(strip, "", pynini.union("[EOS]", "\""), NEMO_SIGMA) - return fst @ strip - - -def add_cardinal_apocope_fem(fst: 'pynini.FstLike') -> 'pynini.FstLike': - """ - Adds apocope on cardinal strings in line with stressing rules. e.g. "una" -> "un". This only occurs when "una" precedes a stressed "a" sound in formal speech. This is not predictable - with text string, so is included for non-deterministic cases. - e.g. - "una" -> "un" - "veintiuna" -> "veintiun" - - Args: - fst: Any fst. Composes conversion onto fst's output strings - """ - # Since the stress trigger follows the cardinal string and only affects the preceding sound, this only needs to act on the last instance of one - strip = pynini.cross("una", "un") | pynini.cross("veintiuna", "veintiún") - strip = pynini.cdrewrite(strip, "", pynini.union("[EOS]", "\""), NEMO_SIGMA) - return fst @ strip - - -def roman_to_int(fst: 'pynini.FstLike') -> 'pynini.FstLike': - """ - Alters given fst to convert Roman integers (lower and upper cased) into Arabic numerals. Valid for values up to 1000. - e.g. - "V" -> "5" - "i" -> "1" - - Args: - fst: Any fst. Composes fst onto Roman conversion outputs. - """ - - def _load_roman(file: str): - roman = load_labels(get_abs_path(file)) - roman_numerals = [(x, y) for x, y in roman] + [(x.upper(), y) for x, y in roman] - return pynini.string_map(roman_numerals) - - digit = _load_roman("data/roman/digit.tsv") - ties = _load_roman("data/roman/ties.tsv") - hundreds = _load_roman("data/roman/hundreds.tsv") - - graph = ( - digit - | ties + (digit | pynutil.add_weight(pynutil.insert("0"), 0.01)) - | ( - hundreds - + (ties | pynutil.add_weight(pynutil.insert("0"), 0.01)) - + (digit | pynutil.add_weight(pynutil.insert("0"), 0.01)) - ) - ).optimize() - - return graph @ fst diff --git a/nemo_text_processing/text_normalization/es/taggers/__init__.py b/nemo_text_processing/text_normalization/es/taggers/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/es/taggers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/es/taggers/cardinal.py b/nemo_text_processing/text_normalization/es/taggers/cardinal.py deleted file mode 100644 index c535100f7934..000000000000 --- a/nemo_text_processing/text_normalization/es/taggers/cardinal.py +++ /dev/null @@ -1,176 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_ALPHA, - NEMO_DIGIT, - NEMO_SIGMA, - NEMO_SPACE, - NEMO_WHITE_SPACE, - GraphFst, - delete_space, - insert_space, -) -from nemo_text_processing.text_normalization.es.graph_utils import cardinal_separator -from nemo_text_processing.text_normalization.es.utils import get_abs_path -from pynini.lib import pynutil - -zero = pynini.invert(pynini.string_file(get_abs_path("data/numbers/zero.tsv"))) -digit = pynini.invert(pynini.string_file(get_abs_path("data/numbers/digit.tsv"))) -teen = pynini.invert(pynini.string_file(get_abs_path("data/numbers/teen.tsv"))) -ties = pynini.invert(pynini.string_file(get_abs_path("data/numbers/ties.tsv"))) -twenties = pynini.invert(pynini.string_file(get_abs_path("data/numbers/twenties.tsv"))) -hundreds = pynini.invert(pynini.string_file(get_abs_path("data/numbers/hundreds.tsv"))) - - -def filter_punctuation(fst: 'pynini.FstLike') -> 'pynini.FstLike': - """ - Helper function for parsing number strings. Converts common cardinal strings (groups of three digits delineated by 'cardinal_separator' - see graph_utils) - and converts to a string of digits: - "1 000" -> "1000" - "1.000.000" -> "1000000" - Args: - fst: Any pynini.FstLike object. Function composes fst onto string parser fst - - Returns: - fst: A pynini.FstLike object - """ - exactly_three_digits = NEMO_DIGIT ** 3 # for blocks of three - up_to_three_digits = pynini.closure(NEMO_DIGIT, 1, 3) # for start of string - - cardinal_string = pynini.closure( - NEMO_DIGIT, 1 - ) # For string w/o punctuation (used for page numbers, thousand series) - - cardinal_string |= ( - up_to_three_digits - + pynutil.delete(cardinal_separator) - + pynini.closure(exactly_three_digits + pynutil.delete(cardinal_separator)) - + exactly_three_digits - ) - - return cardinal_string @ fst - - -class CardinalFst(GraphFst): - """ - Finite state transducer for classifying cardinals, e.g. - "1000" -> cardinal { integer: "mil" } - "2.000.000" -> cardinal { integer: "dos millones" } - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="cardinal", kind="classify", deterministic=deterministic) - - # Any single digit - graph_digit = digit - digits_no_one = (NEMO_DIGIT - "1") @ graph_digit - - # Any double digit - graph_tens = teen - graph_tens |= ties + (pynutil.delete('0') | (pynutil.insert(" y ") + graph_digit)) - graph_tens |= twenties - - self.tens = graph_tens.optimize() - - self.two_digit_non_zero = pynini.union( - graph_digit, graph_tens, (pynini.cross("0", NEMO_SPACE) + graph_digit) - ).optimize() - - # Three digit strings - graph_hundreds = hundreds + pynini.union( - pynutil.delete("00"), (insert_space + graph_tens), (pynini.cross("0", NEMO_SPACE) + graph_digit) - ) - graph_hundreds |= pynini.cross("100", "cien") - graph_hundreds |= ( - pynini.cross("1", "ciento") + insert_space + pynini.union(graph_tens, pynutil.delete("0") + graph_digit) - ) - - self.hundreds = graph_hundreds.optimize() - - # For all three digit strings with leading zeroes (graph appends '0's to manage place in string) - graph_hundreds_component = pynini.union(graph_hundreds, pynutil.delete("0") + graph_tens) - - graph_hundreds_component_at_least_one_none_zero_digit = graph_hundreds_component | ( - pynutil.delete("00") + graph_digit - ) - graph_hundreds_component_at_least_one_none_zero_digit_no_one = graph_hundreds_component | ( - pynutil.delete("00") + digits_no_one - ) - - graph_thousands_component_at_least_one_none_zero_digit = pynini.union( - pynutil.delete("000") + graph_hundreds_component_at_least_one_none_zero_digit, - graph_hundreds_component_at_least_one_none_zero_digit_no_one - + pynutil.insert(" mil") - + ((insert_space + graph_hundreds_component_at_least_one_none_zero_digit) | pynutil.delete("000")), - pynini.cross("001", "mil") - + ((insert_space + graph_hundreds_component_at_least_one_none_zero_digit) | pynutil.delete("000")), - ) - - graph_thousands_component_at_least_one_none_zero_digit_no_one = pynini.union( - pynutil.delete("000") + graph_hundreds_component_at_least_one_none_zero_digit_no_one, - graph_hundreds_component_at_least_one_none_zero_digit_no_one - + pynutil.insert(" mil") - + ((insert_space + graph_hundreds_component_at_least_one_none_zero_digit) | pynutil.delete("000")), - pynini.cross("001", "mil") - + ((insert_space + graph_hundreds_component_at_least_one_none_zero_digit) | pynutil.delete("000")), - ) - - graph_million = pynutil.add_weight(pynini.cross("000001", "un millón"), -0.001) - graph_million |= graph_thousands_component_at_least_one_none_zero_digit_no_one + pynutil.insert(" millones") - graph_million |= pynutil.delete("000000") - graph_million += insert_space - - graph_billion = pynutil.add_weight(pynini.cross("000001", "un billón"), -0.001) - graph_billion |= graph_thousands_component_at_least_one_none_zero_digit_no_one + pynutil.insert(" billones") - graph_billion |= pynutil.delete("000000") - graph_billion += insert_space - - graph_trillion = pynutil.add_weight(pynini.cross("000001", "un trillón"), -0.001) - graph_trillion |= graph_thousands_component_at_least_one_none_zero_digit_no_one + pynutil.insert(" trillones") - graph_trillion |= pynutil.delete("000000") - graph_trillion += insert_space - - graph = ( - graph_trillion - + graph_billion - + graph_million - + (graph_thousands_component_at_least_one_none_zero_digit | pynutil.delete("000000")) - ) - - self.graph = ( - ((NEMO_DIGIT - "0") + pynini.closure(NEMO_DIGIT, 0)) - @ pynini.cdrewrite(pynini.closure(pynutil.insert("0")), "[BOS]", "", NEMO_SIGMA) - @ NEMO_DIGIT ** 24 - @ graph - @ pynini.cdrewrite(delete_space, "[BOS]", "", NEMO_SIGMA) - @ pynini.cdrewrite(delete_space, "", "[EOS]", NEMO_SIGMA) - @ pynini.cdrewrite( - pynini.cross(pynini.closure(NEMO_WHITE_SPACE, 2), NEMO_SPACE), NEMO_ALPHA, NEMO_ALPHA, NEMO_SIGMA - ) - ) - self.graph |= zero - - self.graph = filter_punctuation(self.graph).optimize() - - optional_minus_graph = pynini.closure(pynutil.insert("negative: ") + pynini.cross("-", "\"true\" "), 0, 1) - - final_graph = optional_minus_graph + pynutil.insert("integer: \"") + self.graph + pynutil.insert("\"") - - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/es/taggers/date.py b/nemo_text_processing/text_normalization/es/taggers/date.py deleted file mode 100644 index 4f40e83a8031..000000000000 --- a/nemo_text_processing/text_normalization/es/taggers/date.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_DIGIT, NEMO_SPACE, GraphFst, delete_extra_space -from nemo_text_processing.text_normalization.es.utils import get_abs_path -from pynini.lib import pynutil - -articles = pynini.union("de", "del", "el", "del año") -delete_leading_zero = (pynutil.delete("0") | (NEMO_DIGIT - "0")) + NEMO_DIGIT -month_numbers = pynini.string_file(get_abs_path("data/dates/months.tsv")) - - -class DateFst(GraphFst): - """ - Finite state transducer for classifying date, e.g. - "01.04.2010" -> date { day: "un" month: "enero" year: "dos mil diez" preserve_order: true } - "marzo 4 2000" -> date { month: "marzo" day: "cuatro" year: "dos mil" } - "1990-20-01" -> date { year: "mil novecientos noventa" day: "veinte" month: "enero" } - - Args: - cardinal: cardinal GraphFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, cardinal: GraphFst, deterministic: bool): - super().__init__(name="date", kind="classify", deterministic=deterministic) - - number_to_month = month_numbers.optimize() - month_graph = pynini.project(number_to_month, "output") - - numbers = cardinal.graph - optional_leading_zero = delete_leading_zero | NEMO_DIGIT - - # 01, 31, 1 - digit_day = optional_leading_zero @ pynini.union(*[str(x) for x in range(1, 32)]) @ numbers - day = (pynutil.insert("day: \"") + digit_day + pynutil.insert("\"")).optimize() - - digit_month = optional_leading_zero @ pynini.union(*[str(x) for x in range(1, 13)]) - number_to_month = digit_month @ number_to_month - - month_name = (pynutil.insert("month: \"") + month_graph + pynutil.insert("\"")).optimize() - month_number = (pynutil.insert("month: \"") + number_to_month + pynutil.insert("\"")).optimize() - - # prefer cardinal over year - year = (NEMO_DIGIT - "0") + pynini.closure(NEMO_DIGIT, 1, 3) # 90, 990, 1990 - year @= numbers - self.year = year - - year_only = pynutil.insert("year: \"") + year + pynutil.insert("\"") - year_with_articles = ( - pynutil.insert("year: \"") + pynini.closure(articles + NEMO_SPACE, 0, 1) + year + pynutil.insert("\"") - ) - - graph_dmy = ( - day - + pynini.closure(pynutil.delete(" de")) - + NEMO_SPACE - + month_name - + pynini.closure(NEMO_SPACE + year_with_articles, 0, 1) - ) - - graph_mdy = ( # English influences on language - month_name + delete_extra_space + day + pynini.closure(NEMO_SPACE + year_with_articles, 0, 1) - ) - - separators = [".", "-", "/"] - for sep in separators: - year_optional = pynini.closure(pynini.cross(sep, NEMO_SPACE) + year_only, 0, 1) - new_graph = day + pynini.cross(sep, NEMO_SPACE) + month_number + year_optional - graph_dmy |= new_graph - if not deterministic: - new_graph = month_number + pynini.cross(sep, NEMO_SPACE) + day + year_optional - graph_mdy |= new_graph - - dash = "-" - day_optional = pynini.closure(pynini.cross(dash, NEMO_SPACE) + day, 0, 1) - graph_ymd = NEMO_DIGIT ** 4 @ year_only + pynini.cross(dash, NEMO_SPACE) + month_number + day_optional - - final_graph = graph_dmy + pynutil.insert(" preserve_order: true") - final_graph |= graph_ymd - final_graph |= graph_mdy - - self.final_graph = final_graph.optimize() - self.fst = self.add_tokens(self.final_graph).optimize() diff --git a/nemo_text_processing/text_normalization/es/taggers/decimals.py b/nemo_text_processing/text_normalization/es/taggers/decimals.py deleted file mode 100644 index 4d6e6e2f3598..000000000000 --- a/nemo_text_processing/text_normalization/es/taggers/decimals.py +++ /dev/null @@ -1,127 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_DIGIT, - NEMO_SIGMA, - NEMO_SPACE, - GraphFst, - delete_space, - insert_space, -) -from nemo_text_processing.text_normalization.es.graph_utils import ( - cardinal_separator, - decimal_separator, - strip_cardinal_apocope, -) -from nemo_text_processing.text_normalization.es.utils import get_abs_path -from pynini.lib import pynutil - -quantities = pynini.string_file(get_abs_path("data/numbers/quantities.tsv")) -digit = pynini.invert(pynini.string_file(get_abs_path("data/numbers/digit.tsv"))) -zero = pynini.invert(pynini.string_file(get_abs_path("data/numbers/zero.tsv"))) - - -def get_quantity(decimal_graph: 'pynini.FstLike', cardinal_graph: 'pynini.FstLike') -> 'pynini.FstLike': - """ - Returns FST that transforms either a cardinal or decimal followed by a quantity into a numeral, - e.g. 2 millones -> integer_part: "dos" quantity: "millones" - e.g. 2,4 millones -> integer_part: "dos" fractional_part: "quatro" quantity: "millones" - e.g. 2,400 millones -> integer_part: "dos mil cuatrocientos" fractional_part: "quatro" quantity: "millones" - - Args: - decimal_graph: DecimalFST - cardinal_graph: CardinalFST - """ - numbers = pynini.closure(NEMO_DIGIT, 1, 6) @ cardinal_graph - numbers = pynini.cdrewrite(pynutil.delete(cardinal_separator), "", "", NEMO_SIGMA) @ numbers - - res = ( - pynutil.insert("integer_part: \"") - + numbers # The cardinal we're passing only produces 'un' for one, so gender agreement is safe (all quantities are masculine). Limit to 10^6 power. - + pynutil.insert("\"") - + NEMO_SPACE - + pynutil.insert("quantity: \"") - + quantities - + pynutil.insert("\"") - ) - res |= decimal_graph + NEMO_SPACE + pynutil.insert("quantity: \"") + quantities + pynutil.insert("\"") - return res - - -class DecimalFst(GraphFst): - """ - Finite state transducer for classifying decimal, e.g. - -11,4006 billones -> decimal { negative: "true" integer_part: "once" fractional_part: "cuatro cero cero seis" quantity: "billones" preserve_order: true } - 1 billón -> decimal { integer_part: "un" quantity: "billón" preserve_order: true } - Args: - cardinal: CardinalFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, cardinal: GraphFst, deterministic: bool = True): - super().__init__(name="decimal", kind="classify", deterministic=deterministic) - graph_digit = digit | zero - - if not deterministic: - graph = pynini.union(graph_digit, cardinal.hundreds, cardinal.tens) - graph += pynini.closure(insert_space + graph) - - else: - # General pattern seems to be 1-3 digits: map as cardinal, default to digits otherwise \ - graph = pynini.union( - graph_digit, - cardinal.tens, - cardinal.hundreds, - graph_digit + pynini.closure(insert_space + graph_digit, 3), - zero - + pynini.closure(insert_space + zero) - + pynini.closure(insert_space + graph_digit), # For cases such as "1,010" - ) - - # Need to strip apocope everywhere BUT end of string - reverse_apocope = pynini.string_map([("un", "uno"), ("ún", "uno")]) - apply_reverse_apocope = pynini.cdrewrite(reverse_apocope, "", NEMO_SPACE, NEMO_SIGMA) - graph @= apply_reverse_apocope - - # Technically decimals should be space delineated groups of three, e.g. (1,333 333). This removes any possible spaces - strip_formatting = pynini.cdrewrite(delete_space, "", "", NEMO_SIGMA) - graph = strip_formatting @ graph - - self.graph = graph.optimize() - - graph_separator = pynutil.delete(decimal_separator) - optional_graph_negative = pynini.closure(pynutil.insert("negative: ") + pynini.cross("-", "\"true\" "), 0, 1) - - self.graph_fractional = pynutil.insert("fractional_part: \"") + self.graph + pynutil.insert("\"") - - # Integer graph maintains apocope except for ones place - graph_integer = ( - strip_cardinal_apocope(cardinal.graph) - if deterministic - else pynini.union(cardinal.graph, strip_cardinal_apocope(cardinal.graph)) - ) # Gives us forms w/ and w/o apocope - self.graph_integer = pynutil.insert("integer_part: \"") + graph_integer + pynutil.insert("\"") - final_graph_wo_sign = self.graph_integer + graph_separator + insert_space + self.graph_fractional - - self.final_graph_wo_negative = ( - final_graph_wo_sign | get_quantity(final_graph_wo_sign, cardinal.graph).optimize() - ) - final_graph = optional_graph_negative + self.final_graph_wo_negative - - final_graph += pynutil.insert(" preserve_order: true") - final_graph = self.add_tokens(final_graph) - - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/es/taggers/electronic.py b/nemo_text_processing/text_normalization/es/taggers/electronic.py deleted file mode 100644 index 13833bbe03ae..000000000000 --- a/nemo_text_processing/text_normalization/es/taggers/electronic.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_ALPHA, NEMO_DIGIT, GraphFst, insert_space -from nemo_text_processing.text_normalization.es.utils import get_abs_path, load_labels -from pynini.lib import pynutil - -common_domains = [x[0] for x in load_labels(get_abs_path("data/electronic/domain.tsv"))] -symbols = [x[0] for x in load_labels(get_abs_path("data/electronic/symbols.tsv"))] - - -class ElectronicFst(GraphFst): - """ - Finite state transducer for classifying electronic: email addresses - e.g. "abc@hotmail.com" -> electronic { username: "abc" domain: "hotmail.com" preserve_order: true } - e.g. "www.abc.com/123" -> electronic { protocol: "www." domain: "abc.com/123" preserve_order: true } - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="electronic", kind="classify", deterministic=deterministic) - - dot = pynini.accep(".") - accepted_common_domains = pynini.union(*common_domains) - accepted_symbols = pynini.union(*symbols) - dot - accepted_characters = pynini.closure(NEMO_ALPHA | NEMO_DIGIT | accepted_symbols) - acceepted_characters_with_dot = pynini.closure(NEMO_ALPHA | NEMO_DIGIT | accepted_symbols | dot) - - # email - username = ( - pynutil.insert("username: \"") - + acceepted_characters_with_dot - + pynutil.insert("\"") - + pynini.cross('@', ' ') - ) - domain_graph = accepted_characters + dot + accepted_characters - domain_graph = pynutil.insert("domain: \"") + domain_graph + pynutil.insert("\"") - domain_common_graph = ( - pynutil.insert("domain: \"") - + accepted_characters - + accepted_common_domains - + pynini.closure((accepted_symbols | dot) + pynini.closure(accepted_characters, 1), 0, 1) - + pynutil.insert("\"") - ) - graph = (username + domain_graph) | domain_common_graph - - # url - protocol_start = pynini.accep("https://") | pynini.accep("http://") - protocol_end = ( - pynini.accep("www.") - if deterministic - else pynini.accep("www.") | pynini.cross("www.", "doble ve doble ve doble ve.") - ) - protocol = protocol_start | protocol_end | (protocol_start + protocol_end) - protocol = pynutil.insert("protocol: \"") + protocol + pynutil.insert("\"") - graph |= protocol + insert_space + (domain_graph | domain_common_graph) - self.graph = graph - - final_graph = self.add_tokens(self.graph + pynutil.insert(" preserve_order: true")) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/es/taggers/fraction.py b/nemo_text_processing/text_normalization/es/taggers/fraction.py deleted file mode 100644 index c52e8eafc4cd..000000000000 --- a/nemo_text_processing/text_normalization/es/taggers/fraction.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_CHAR, - NEMO_DIGIT, - NEMO_SIGMA, - NEMO_SPACE, - GraphFst, -) -from nemo_text_processing.text_normalization.es.utils import get_abs_path -from pynini.lib import pynutil - -ordinal_exceptions = pynini.string_file(get_abs_path("data/fractions/ordinal_exceptions.tsv")) -higher_powers_of_ten = pynini.string_file(get_abs_path("data/fractions/powers_of_ten.tsv")) - - -class FractionFst(GraphFst): - """ - Finite state transducer for classifying fraction - "23 4/5" -> - tokens { fraction { integer: "veintitrés" numerator: "cuatro" denominator: "quinto" mophosyntactic_features: "ordinal" } } - - Args: - cardinal: CardinalFst - ordinal: OrdinalFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, cardinal: GraphFst, ordinal: GraphFst, deterministic: bool = True): - super().__init__(name="fraction", kind="classify", deterministic=deterministic) - cardinal_graph = cardinal.graph - ordinal_graph = ordinal.graph - - # 2-10 are all ordinals - three_to_ten = pynini.string_map(["2", "3", "4", "5", "6", "7", "8", "9", "10",]) - block_three_to_ten = pynutil.delete(three_to_ten) # To block cardinal productions - if not deterministic: # Multiples of tens are sometimes rendered as ordinals - three_to_ten |= pynini.string_map(["20", "30", "40", "50", "60", "70", "80", "90",]) - graph_three_to_ten = three_to_ten @ ordinal_graph - graph_three_to_ten @= pynini.cdrewrite(ordinal_exceptions, "", "", NEMO_SIGMA) - - # Higher powers of tens (and multiples) are converted to ordinals. - hundreds = pynini.string_map(["100", "200", "300", "400", "500", "600", "700", "800", "900",]) - graph_hundreds = hundreds @ ordinal_graph - - multiples_of_thousand = ordinal.multiples_of_thousand # So we can have X milésimos - - graph_higher_powers_of_ten = ( - pynini.closure(ordinal.one_to_one_thousand + NEMO_SPACE, 0, 1) - + pynini.closure("mil ", 0, 1) - + pynini.closure(ordinal.one_to_one_thousand + NEMO_SPACE, 0, 1) - ) # x millones / x mil millones / x mil z millones - graph_higher_powers_of_ten += higher_powers_of_ten - graph_higher_powers_of_ten = cardinal_graph @ graph_higher_powers_of_ten - graph_higher_powers_of_ten @= pynini.cdrewrite( - pynutil.delete("un "), pynini.accep("[BOS]"), pynini.project(higher_powers_of_ten, "output"), NEMO_SIGMA - ) # we drop 'un' from these ordinals (millionths, not one-millionths) - - graph_higher_powers_of_ten = multiples_of_thousand | graph_hundreds | graph_higher_powers_of_ten - block_higher_powers_of_ten = pynutil.delete( - pynini.project(graph_higher_powers_of_ten, "input") - ) # For cardinal graph - - graph_fractions_ordinals = graph_higher_powers_of_ten | graph_three_to_ten - graph_fractions_ordinals += pynutil.insert( - "\" morphosyntactic_features: \"ordinal\"" - ) # We note the root for processing later - - # Blocking the digits and hundreds from Cardinal graph - graph_fractions_cardinals = pynini.cdrewrite( - block_three_to_ten | block_higher_powers_of_ten, pynini.accep("[BOS]"), pynini.accep("[EOS]"), NEMO_SIGMA - ) - graph_fractions_cardinals @= NEMO_CHAR.plus @ pynini.cdrewrite( - pynutil.delete("0"), pynini.accep("[BOS]"), pynini.accep("[EOS]"), NEMO_SIGMA - ) # Empty characters become '0' for NEMO_CHAR fst, so need to block - graph_fractions_cardinals @= cardinal_graph - graph_fractions_cardinals += pynutil.insert( - "\" morphosyntactic_features: \"add_root\"" - ) # blocking these entries to reduce erroneous possibilities in debugging - - if deterministic: - graph_fractions_cardinals = ( - pynini.closure(NEMO_DIGIT, 1, 2) @ graph_fractions_cardinals - ) # Past hundreds the conventional scheme can be hard to read. For determinism we stop here - - graph_denominator = pynini.union( - graph_fractions_ordinals, - graph_fractions_cardinals, - pynutil.add_weight(cardinal_graph + pynutil.insert("\""), 0.001), - ) # Last form is simply recording the cardinal. Weighting so last resort - - integer = pynutil.insert("integer_part: \"") + cardinal_graph + pynutil.insert("\"") + NEMO_SPACE - numerator = ( - pynutil.insert("numerator: \"") + cardinal_graph + (pynini.cross("/", "\" ") | pynini.cross(" / ", "\" ")) - ) - denominator = pynutil.insert("denominator: \"") + graph_denominator - - self.graph = pynini.closure(integer, 0, 1) + numerator + denominator - - final_graph = self.add_tokens(self.graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/es/taggers/measure.py b/nemo_text_processing/text_normalization/es/taggers/measure.py deleted file mode 100644 index 8335a6b853da..000000000000 --- a/nemo_text_processing/text_normalization/es/taggers/measure.py +++ /dev/null @@ -1,173 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_ALPHA, - NEMO_NON_BREAKING_SPACE, - NEMO_SIGMA, - NEMO_SPACE, - GraphFst, - convert_space, - delete_space, - insert_space, -) -from nemo_text_processing.text_normalization.es.graph_utils import strip_cardinal_apocope -from nemo_text_processing.text_normalization.es.utils import get_abs_path -from pynini.lib import pynutil - -unit = pynini.string_file(get_abs_path("data/measures/measurements.tsv")) -unit_plural_fem = pynini.string_file(get_abs_path("data/measures/measurements_plural_fem.tsv")) -unit_plural_masc = pynini.string_file(get_abs_path("data/measures/measurements_plural_masc.tsv")) - - -class MeasureFst(GraphFst): - """ - Finite state transducer for classifying measure, e.g. - "2,4 g" -> measure { cardinal { integer_part: "dos" fractional_part: "cuatro" units: "gramos" preserve_order: true } } - "1 g" -> measure { cardinal { integer: "un" units: "gramo" preserve_order: true } } - "1 millón g" -> measure { cardinal { integer: "un quantity: "millón" units: "gramos" preserve_order: true } } - e.g. "a-8" —> "a ocho" - e.g. "1,2-a" —> "uno coma dos a" - This class also converts words containing numbers and letters - e.g. "a-8" —> "a ocho" - e.g. "1,2-a" —> "uno coma dos a" - - - Args: - cardinal: CardinalFst - decimal: DecimalFst - fraction: FractionFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, cardinal: GraphFst, decimal: GraphFst, fraction: GraphFst, deterministic: bool = True): - super().__init__(name="measure", kind="classify", deterministic=deterministic) - cardinal_graph = cardinal.graph - - unit_singular = unit - unit_plural = unit_singular @ (unit_plural_fem | unit_plural_masc) - - graph_unit_singular = convert_space(unit_singular) - graph_unit_plural = convert_space(unit_plural) - - optional_graph_negative = pynini.closure("-", 0, 1) - - graph_unit_denominator = ( - pynini.cross("/", "por") + pynutil.insert(NEMO_NON_BREAKING_SPACE) + graph_unit_singular - ) - - optional_unit_denominator = pynini.closure( - pynutil.insert(NEMO_NON_BREAKING_SPACE) + graph_unit_denominator, 0, 1, - ) - - unit_plural = ( - pynutil.insert("units: \"") - + ((graph_unit_plural + optional_unit_denominator) | graph_unit_denominator) - + pynutil.insert("\"") - ) - - unit_singular_graph = ( - pynutil.insert("units: \"") - + ((graph_unit_singular + optional_unit_denominator) | graph_unit_denominator) - + pynutil.insert("\"") - ) - - subgraph_decimal = decimal.fst + insert_space + pynini.closure(NEMO_SPACE, 0, 1) + unit_plural - - subgraph_cardinal = ( - (optional_graph_negative + (NEMO_SIGMA - "1")) @ cardinal.fst - + insert_space - + pynini.closure(delete_space, 0, 1) - + unit_plural - ) - - subgraph_cardinal |= ( - (optional_graph_negative + pynini.accep("1")) @ cardinal.fst - + insert_space - + pynini.closure(delete_space, 0, 1) - + unit_singular_graph - ) - - subgraph_fraction = fraction.fst + insert_space + pynini.closure(delete_space, 0, 1) + unit_singular_graph - - decimal_times = ( - pynutil.insert("decimal { ") - + decimal.final_graph_wo_negative - + pynutil.insert(" } units: \"") - + pynini.union('x', 'X') - + pynutil.insert("\"") - ) - - cardinal_times = ( - pynutil.insert("cardinal { integer: \"") - + strip_cardinal_apocope(cardinal_graph) - + pynutil.insert("\" } units: \"") - + pynini.union('x', 'X') - + pynutil.insert("\"") - ) - - cardinal_dash_alpha = ( - pynutil.insert("cardinal { integer: \"") - + strip_cardinal_apocope(cardinal_graph) - + pynutil.delete('-') - + pynutil.insert("\" } units: \"") - + pynini.closure(NEMO_ALPHA, 1) - + pynutil.insert("\"") - ) - - decimal_dash_alpha = ( - pynutil.insert("decimal { ") - + decimal.final_graph_wo_negative - + pynutil.delete('-') - + pynutil.insert(" } units: \"") - + pynini.closure(NEMO_ALPHA, 1) - + pynutil.insert("\"") - ) - - alpha_dash_cardinal = ( - pynutil.insert("units: \"") - + pynini.closure(NEMO_ALPHA, 1) - + pynutil.delete('-') - + pynutil.insert("\"") - + pynutil.insert(" cardinal { integer: \"") - + cardinal_graph - + pynutil.insert("\" } preserve_order: true") - ) - - alpha_dash_decimal = ( - pynutil.insert("units: \"") - + pynini.closure(NEMO_ALPHA, 1) - + pynutil.delete('-') - + pynutil.insert("\"") - + pynutil.insert(" decimal { ") - + decimal.final_graph_wo_negative - + pynutil.insert(" } preserve_order: true") - ) - - final_graph = ( - subgraph_decimal - | subgraph_cardinal - | cardinal_dash_alpha - | alpha_dash_cardinal - | decimal_dash_alpha - | subgraph_fraction - | decimal_times - | cardinal_times - | alpha_dash_decimal - ) - final_graph += pynutil.insert(" preserve_order: true") - final_graph = self.add_tokens(final_graph) - - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/es/taggers/money.py b/nemo_text_processing/text_normalization/es/taggers/money.py deleted file mode 100644 index 3cb074550097..000000000000 --- a/nemo_text_processing/text_normalization/es/taggers/money.py +++ /dev/null @@ -1,181 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_ALPHA, - NEMO_DIGIT, - NEMO_SIGMA, - NEMO_SPACE, - GraphFst, - delete_space, - insert_space, -) -from nemo_text_processing.text_normalization.es.graph_utils import decimal_separator -from nemo_text_processing.text_normalization.es.utils import get_abs_path, load_labels -from pynini.lib import pynutil - -maj_singular_labels = load_labels(get_abs_path("data/money/currency_major.tsv")) -maj_singular = pynini.string_file((get_abs_path("data/money/currency_major.tsv"))) -min_singular = pynini.string_file(get_abs_path("data/money/currency_minor.tsv")) -fem_plural = pynini.string_file((get_abs_path("data/money/currency_plural_fem.tsv"))) -masc_plural = pynini.string_file((get_abs_path("data/money/currency_plural_masc.tsv"))) - - -class MoneyFst(GraphFst): - """ - Finite state transducer for classifying money, e.g. - "€1" -> money { currency_maj: "euro" integer_part: "un"} - "€1,000" -> money { currency_maj: "euro" integer_part: "un" } - "€1,001" -> money { currency_maj: "euro" integer_part: "un" fractional_part: "cero cero un" } - "£1,4" -> money { integer_part: "una" currency_maj: "libra" fractional_part: "cuarenta" preserve_order: true } - -> money { integer_part: "una" currency_maj: "libra" fractional_part: "cuarenta" currency_min: "penique" preserve_order: true } - "0,01 £" -> money { fractional_part: "un" currency_min: "penique" preserve_order: true } - "0,02 £" -> money { fractional_part: "dos" currency_min: "peniques" preserve_order: true } - "£0,01 million" -> money { currency_maj: "libra" integer_part: "cero" fractional_part: "cero un" quantity: "million" } - - Args: - cardinal: CardinalFst - decimal: DecimalFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, cardinal: GraphFst, decimal: GraphFst, deterministic: bool = True): - super().__init__(name="money", kind="classify", deterministic=deterministic) - cardinal_graph = cardinal.graph - graph_decimal_final = decimal.final_graph_wo_negative - - maj_singular_graph = maj_singular - min_singular_graph = min_singular - maj_plural_graph = maj_singular @ (fem_plural | masc_plural) - min_plural_graph = min_singular @ (fem_plural | masc_plural) - - graph_maj_singular = pynutil.insert("currency_maj: \"") + maj_singular_graph + pynutil.insert("\"") - graph_maj_plural = pynutil.insert("currency_maj: \"") + maj_plural_graph + pynutil.insert("\"") - - graph_integer_one = pynutil.insert("integer_part: \"") + pynini.cross("1", "un") + pynutil.insert("\"") - - decimal_with_quantity = (NEMO_SIGMA + NEMO_ALPHA) @ graph_decimal_final - - graph_decimal_plural = pynini.union( - graph_maj_plural + pynini.closure(delete_space, 0, 1) + insert_space + graph_decimal_final, # $1,05 - graph_decimal_final + pynini.closure(delete_space, 0, 1) + insert_space + graph_maj_plural, # 1,05 $ - ) - graph_decimal_plural = ( - (NEMO_SIGMA - "1") + decimal_separator + NEMO_SIGMA - ) @ graph_decimal_plural # Can't have "un euros" - - graph_decimal_singular = pynini.union( - graph_maj_singular + pynini.closure(delete_space, 0, 1) + insert_space + graph_decimal_final, # $1,05 - graph_decimal_final + pynini.closure(delete_space, 0, 1) + insert_space + graph_maj_singular, # 1,05 $ - ) - graph_decimal_singular = (pynini.accep("1") + decimal_separator + NEMO_SIGMA) @ graph_decimal_singular - - graph_decimal = pynini.union( - graph_decimal_singular, - graph_decimal_plural, - graph_maj_plural + pynini.closure(delete_space, 0, 1) + insert_space + decimal_with_quantity, - ) - - graph_integer = ( - pynutil.insert("integer_part: \"") + ((NEMO_SIGMA - "1") @ cardinal_graph) + pynutil.insert("\"") - ) - - graph_integer_only = pynini.union( - graph_maj_singular + pynini.closure(delete_space, 0, 1) + insert_space + graph_integer_one, - graph_integer_one + pynini.closure(delete_space, 0, 1) + insert_space + graph_maj_singular, - ) - graph_integer_only |= pynini.union( - graph_maj_plural + pynini.closure(delete_space, 0, 1) + insert_space + graph_integer, - graph_integer + pynini.closure(delete_space, 0, 1) + insert_space + graph_maj_plural, - ) - - graph = graph_integer_only | graph_decimal - - # remove trailing zeros of non zero number in the first 2 digits and fill up to 2 digits - # e.g. 2000 -> 20, 0200->02, 01 -> 01, 10 -> 10 - # not accepted: 002, 00, 0, - two_digits_fractional_part = ( - pynini.closure(NEMO_DIGIT) + (NEMO_DIGIT - "0") + pynini.closure(pynutil.delete("0")) - ) @ ( - (pynutil.delete("0") + (NEMO_DIGIT - "0")) - | ((NEMO_DIGIT - "0") + pynutil.insert("0")) - | ((NEMO_DIGIT - "0") + NEMO_DIGIT) - ) - - graph_min_singular = pynutil.insert("currency_min: \"") + min_singular_graph + pynutil.insert("\"") - graph_min_plural = pynutil.insert("currency_min: \"") + min_plural_graph + pynutil.insert("\"") - - # format ** euro ** cent - decimal_graph_with_minor = None - for curr_symbol, _ in maj_singular_labels: - preserve_order = pynutil.insert(" preserve_order: true") - - integer_plus_maj = pynini.union( - graph_integer + insert_space + pynutil.insert(curr_symbol) @ graph_maj_plural, - graph_integer_one + insert_space + pynutil.insert(curr_symbol) @ graph_maj_singular, - ) - # non zero integer part - integer_plus_maj = (pynini.closure(NEMO_DIGIT) - "0") @ integer_plus_maj - - graph_fractional_one = ( - pynutil.insert("fractional_part: \"") - + two_digits_fractional_part @ pynini.cross("1", "un") - + pynutil.insert("\"") - ) - - graph_fractional = ( - two_digits_fractional_part @ (pynini.closure(NEMO_DIGIT, 1, 2) - "1") @ cardinal.two_digit_non_zero - ) - graph_fractional = pynutil.insert("fractional_part: \"") + graph_fractional + pynutil.insert("\"") - - fractional_plus_min = pynini.union( - graph_fractional + insert_space + pynutil.insert(curr_symbol) @ graph_min_plural, - graph_fractional_one + insert_space + pynutil.insert(curr_symbol) @ graph_min_singular, - ) - - decimal_graph_with_minor_curr = ( - integer_plus_maj + pynini.cross(decimal_separator, NEMO_SPACE) + fractional_plus_min - ) - decimal_graph_with_minor_curr |= pynutil.add_weight( - integer_plus_maj - + pynini.cross(decimal_separator, NEMO_SPACE) - + pynutil.insert("fractional_part: \"") - + two_digits_fractional_part @ cardinal.two_digit_non_zero - + pynutil.insert("\""), - weight=0.0001, - ) - - decimal_graph_with_minor_curr |= pynutil.delete("0,") + fractional_plus_min - decimal_graph_with_minor_curr = pynini.union( - pynutil.delete(curr_symbol) - + pynini.closure(delete_space, 0, 1) - + decimal_graph_with_minor_curr - + preserve_order, - decimal_graph_with_minor_curr - + preserve_order - + pynini.closure(delete_space, 0, 1) - + pynutil.delete(curr_symbol), - ) - - decimal_graph_with_minor = ( - decimal_graph_with_minor_curr - if decimal_graph_with_minor is None - else pynini.union(decimal_graph_with_minor, decimal_graph_with_minor_curr) - ) - - final_graph = graph | pynutil.add_weight(decimal_graph_with_minor, -0.001) - - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/es/taggers/ordinal.py b/nemo_text_processing/text_normalization/es/taggers/ordinal.py deleted file mode 100644 index a22fd191fc0b..000000000000 --- a/nemo_text_processing/text_normalization/es/taggers/ordinal.py +++ /dev/null @@ -1,175 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_CHAR, - NEMO_SIGMA, - NEMO_SPACE, - GraphFst, - delete_space, -) -from nemo_text_processing.text_normalization.es.graph_utils import roman_to_int, strip_accent -from nemo_text_processing.text_normalization.es.utils import get_abs_path -from pynini.lib import pynutil - -digit = pynini.invert(pynini.string_file(get_abs_path("data/ordinals/digit.tsv"))) -teens = pynini.invert(pynini.string_file(get_abs_path("data/ordinals/teen.tsv"))) -twenties = pynini.invert(pynini.string_file(get_abs_path("data/ordinals/twenties.tsv"))) -ties = pynini.invert(pynini.string_file(get_abs_path("data/ordinals/ties.tsv"))) -hundreds = pynini.invert(pynini.string_file(get_abs_path("data/ordinals/hundreds.tsv"))) - - -def get_one_to_one_thousand(cardinal: 'pynini.FstLike') -> 'pynini.FstLike': - """ - Produces an acceptor for verbalizations of all numbers from 1 to 1000. Needed for ordinals and fractions. - - Args: - cardinal: CardinalFst - - Returns: - fst: A pynini.FstLike object - """ - numbers = pynini.string_map([str(_) for _ in range(1, 1000)]) @ cardinal - return pynini.project(numbers, "output").optimize() - - -class OrdinalFst(GraphFst): - """ - Finite state transducer for classifying ordinal - "21.º" -> ordinal { integer: "vigésimo primero" morphosyntactic_features: "gender_masc" } - This class converts ordinal up to the millionth (millonésimo) order (exclusive). - - This FST also records the ending of the ordinal (called "morphosyntactic_features"): - either as gender_masc, gender_fem, or apocope. Also introduces plural feature for non-deterministic graphs. - - Args: - cardinal: CardinalFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, cardinal: GraphFst, deterministic: bool = True): - super().__init__(name="ordinal", kind="classify") - cardinal_graph = cardinal.graph - - graph_digit = digit.optimize() - graph_teens = teens.optimize() - graph_ties = ties.optimize() - graph_twenties = twenties.optimize() - graph_hundreds = hundreds.optimize() - - if not deterministic: - # Some alternative derivations - graph_ties = graph_ties | pynini.cross("sesenta", "setuagésimo") - - graph_teens = graph_teens | pynini.cross("once", "decimoprimero") - graph_teens |= pynini.cross("doce", "decimosegundo") - - graph_digit = graph_digit | pynini.cross("nueve", "nono") - graph_digit |= pynini.cross("siete", "sétimo") - - graph_tens_component = ( - graph_teens - | (graph_ties + pynini.closure(pynini.cross(" y ", NEMO_SPACE) + graph_digit, 0, 1)) - | graph_twenties - ) - - graph_hundred_component = pynini.union( - graph_hundreds + pynini.closure(NEMO_SPACE + pynini.union(graph_tens_component, graph_digit), 0, 1), - graph_tens_component, - graph_digit, - ) - - # Need to go up to thousands for fractions - self.one_to_one_thousand = get_one_to_one_thousand(cardinal_graph) - - thousands = pynini.cross("mil", "milésimo") - - graph_thousands = ( - strip_accent(self.one_to_one_thousand) + NEMO_SPACE + thousands - ) # Cardinals become prefix for thousands series. Snce accent on the powers of ten we strip accent from leading words - graph_thousands @= pynini.cdrewrite(delete_space, "", "milésimo", NEMO_SIGMA) # merge as a prefix - graph_thousands |= thousands - - self.multiples_of_thousand = (cardinal_graph @ graph_thousands).optimize() - - if ( - not deterministic - ): # Formally the words preceding the power of ten should be a prefix, but some maintain word boundaries. - graph_thousands |= (self.one_to_one_thousand @ graph_hundred_component) + NEMO_SPACE + thousands - - graph_thousands += pynini.closure(NEMO_SPACE + graph_hundred_component, 0, 1) - - ordinal_graph = graph_thousands | graph_hundred_component - ordinal_graph = cardinal_graph @ ordinal_graph - - if not deterministic: - # The 10's and 20's series can also be two words - split_words = pynini.cross("decimo", "décimo ") | pynini.cross("vigesimo", "vigésimo ") - split_words = pynini.cdrewrite(split_words, "", NEMO_CHAR, NEMO_SIGMA) - ordinal_graph |= ordinal_graph @ split_words - - # If "octavo" is preceeded by the "o" within string, it needs deletion - ordinal_graph @= pynini.cdrewrite(pynutil.delete("o"), "", "octavo", NEMO_SIGMA) - - self.graph = ordinal_graph.optimize() - - masc = pynini.accep("gender_masc") - fem = pynini.accep("gender_fem") - apocope = pynini.accep("apocope") - - delete_period = pynini.closure(pynutil.delete("."), 0, 1) # Sometimes the period is omitted f - - accept_masc = delete_period + pynini.cross("º", masc) - accep_fem = delete_period + pynini.cross("ª", fem) - accep_apocope = delete_period + pynini.cross("ᵉʳ", apocope) - - # Managing Romanization - graph_roman = pynutil.insert("integer: \"") + roman_to_int(ordinal_graph) + pynutil.insert("\"") - if not deterministic: - # Introduce plural - plural = pynini.closure(pynutil.insert("/plural"), 0, 1) - accept_masc += plural - accep_fem += plural - - # Romanizations have no morphology marker, so in non-deterministic case we provide option for all - insert_morphology = pynutil.insert(pynini.union(masc, fem)) + plural - insert_morphology |= pynutil.insert(apocope) - insert_morphology = ( - pynutil.insert(" morphosyntactic_features: \"") + insert_morphology + pynutil.insert("\"") - ) - - graph_roman += insert_morphology - - else: - # We insert both genders as default - graph_roman += pynutil.insert(" morphosyntactic_features: \"gender_masc\"") | pynutil.insert( - " morphosyntactic_features: \"gender_fem\"" - ) - - # Rest of graph - convert_abbreviation = accept_masc | accep_fem | accep_apocope - - graph = ( - pynutil.insert("integer: \"") - + ordinal_graph - + pynutil.insert("\"") - + pynutil.insert(" morphosyntactic_features: \"") - + convert_abbreviation - + pynutil.insert("\"") - ) - graph = pynini.union(graph, graph_roman) - - final_graph = self.add_tokens(graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/es/taggers/telephone.py b/nemo_text_processing/text_normalization/es/taggers/telephone.py deleted file mode 100644 index f97207c9696b..000000000000 --- a/nemo_text_processing/text_normalization/es/taggers/telephone.py +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_SIGMA, GraphFst, insert_space -from nemo_text_processing.text_normalization.es.graph_utils import ones -from nemo_text_processing.text_normalization.es.utils import get_abs_path -from pynini.lib import pynutil - -graph_digit = pynini.string_file(get_abs_path("data/numbers/digit.tsv")) -graph_ties = pynini.string_file(get_abs_path("data/numbers/ties.tsv")) -graph_teen = pynini.string_file(get_abs_path("data/numbers/teen.tsv")) -graph_twenties = pynini.string_file(get_abs_path("data/numbers/twenties.tsv")) - - -class TelephoneFst(GraphFst): - """ - Finite state transducer for classifying telephone numbers, e.g. - 123-123-5678 -> { number_part: "uno dos tres uno dos tres cinco seis siete ocho" }. - In Spanish, digits are generally read individually, or as 2-digit numbers, - eg. "123" = "uno dos tres", - "1234" = "doce treinta y cuatro". - This will verbalize sequences of 10 (3+3+4 e.g. 123-456-7890). - 9 (3+3+3 e.g. 123-456-789) and 8 (4+4 e.g. 1234-5678) digits. - - (we ignore more complicated cases such as "doscientos y dos" or "tres nueves"). - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="telephone", kind="classify") - - # create `single_digits` and `double_digits` graphs as these will be - # the building blocks of possible telephone numbers - single_digits = pynini.invert(graph_digit).optimize() | pynini.cross("0", "cero") - - double_digits = pynini.union( - graph_twenties, - graph_teen, - (graph_ties + pynutil.delete("0")), - (graph_ties + insert_space + pynutil.insert("y") + insert_space + graph_digit), - ) - double_digits = pynini.invert(double_digits) - - # define `ten_digit_graph`, `nine_digit_graph`, `eight_digit_graph` - # which produces telephone numbers spoken (1) only with single digits, - # or (2) spoken with double digits (and sometimes single digits) - - # 10-digit option (1): all single digits - ten_digit_graph = ( - pynini.closure(single_digits + insert_space, 3, 3) - + pynutil.delete("-") - + pynini.closure(single_digits + insert_space, 3, 3) - + pynutil.delete("-") - + pynini.closure(single_digits + insert_space, 3, 3) - + single_digits - ) - - # 9-digit option (1): all single digits - nine_digit_graph = ( - pynini.closure(single_digits + insert_space, 3, 3) - + pynutil.delete("-") - + pynini.closure(single_digits + insert_space, 3, 3) - + pynutil.delete("-") - + pynini.closure(single_digits + insert_space, 2, 2) - + single_digits - ) - - # 8-digit option (1): all single digits - eight_digit_graph = ( - pynini.closure(single_digits + insert_space, 4, 4) - + pynutil.delete("-") - + pynini.closure(single_digits + insert_space, 3, 3) - + single_digits - ) - - if not deterministic: - # 10-digit option (2): (1+2) + (1+2) + (2+2) digits - ten_digit_graph |= ( - single_digits - + insert_space - + double_digits - + insert_space - + pynutil.delete("-") - + single_digits - + insert_space - + double_digits - + insert_space - + pynutil.delete("-") - + double_digits - + insert_space - + double_digits - ) - - # 9-digit option (2): (1+2) + (1+2) + (1+2) digits - nine_digit_graph |= ( - single_digits - + insert_space - + double_digits - + insert_space - + pynutil.delete("-") - + single_digits - + insert_space - + double_digits - + insert_space - + pynutil.delete("-") - + single_digits - + insert_space - + double_digits - ) - - # 8-digit option (2): (2+2) + (2+2) digits - eight_digit_graph |= ( - double_digits - + insert_space - + double_digits - + insert_space - + pynutil.delete("-") - + double_digits - + insert_space - + double_digits - ) - - number_part = pynini.union(ten_digit_graph, nine_digit_graph, eight_digit_graph) - number_part @= pynini.cdrewrite(pynini.cross(ones, "uno"), "", "", NEMO_SIGMA) - - number_part = pynutil.insert("number_part: \"") + number_part + pynutil.insert("\"") - - graph = number_part - final_graph = self.add_tokens(graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/es/taggers/time.py b/nemo_text_processing/text_normalization/es/taggers/time.py deleted file mode 100644 index 06c9fd452ce3..000000000000 --- a/nemo_text_processing/text_normalization/es/taggers/time.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_DIGIT, - NEMO_SIGMA, - GraphFst, - delete_space, - insert_space, -) -from nemo_text_processing.text_normalization.es.utils import get_abs_path -from pynini.lib import pynutil - -time_zone_graph = pynini.string_file(get_abs_path("data/time/time_zone.tsv")) -suffix = pynini.string_file(get_abs_path("data/time/time_suffix.tsv")) - - -class TimeFst(GraphFst): - """ - Finite state transducer for classifying time, e.g. - "02:15 est" -> time { hours: "dos" minutes: "quince" zone: "e s t"} - "2 h" -> time { hours: "dos" } - "9 h" -> time { hours: "nueve" } - "02:15:10 h" -> time { hours: "dos" minutes: "quince" seconds: "diez"} - - Args: - cardinal: CardinalFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, cardinal: GraphFst, deterministic: bool = True): - super().__init__(name="time", kind="classify", deterministic=deterministic) - - delete_time_delimiter = pynutil.delete(pynini.union(".", ":")) - - one = pynini.string_map([("un", "una"), ("ún", "una")]) - change_one = pynini.cdrewrite(one, "", "", NEMO_SIGMA) - cardinal_graph = cardinal.graph @ change_one - - day_suffix = pynutil.insert("suffix: \"") + suffix + pynutil.insert("\"") - day_suffix = delete_space + insert_space + day_suffix - - delete_hora_suffix = delete_space + insert_space + pynutil.delete("h") - delete_minute_suffix = delete_space + insert_space + pynutil.delete("min") - delete_second_suffix = delete_space + insert_space + pynutil.delete("s") - - labels_hour_24 = [ - str(x) for x in range(0, 25) - ] # Can see both systems. Twelve hour requires am/pm for ambiguity resolution - labels_hour_12 = [str(x) for x in range(1, 13)] - labels_minute_single = [str(x) for x in range(1, 10)] - labels_minute_double = [str(x) for x in range(10, 60)] - - delete_leading_zero_to_double_digit = ( - pynini.closure(pynutil.delete("0") | (NEMO_DIGIT - "0"), 0, 1) + NEMO_DIGIT - ) - - graph_24 = ( - pynini.closure(NEMO_DIGIT, 1, 2) @ delete_leading_zero_to_double_digit @ pynini.union(*labels_hour_24) - ) - graph_12 = ( - pynini.closure(NEMO_DIGIT, 1, 2) @ delete_leading_zero_to_double_digit @ pynini.union(*labels_hour_12) - ) - - graph_hour_24 = graph_24 @ cardinal_graph - graph_hour_12 = graph_12 @ cardinal_graph - - graph_minute_single = delete_leading_zero_to_double_digit @ pynini.union(*labels_minute_single) - graph_minute_double = pynini.union(*labels_minute_double) - - graph_minute = pynini.union(graph_minute_single, graph_minute_double) @ cardinal_graph - - final_graph_hour_only_24 = ( - pynutil.insert("hours: \"") + graph_hour_24 + pynutil.insert("\"") + delete_hora_suffix - ) - final_graph_hour_only_12 = pynutil.insert("hours: \"") + graph_hour_12 + pynutil.insert("\"") + day_suffix - - final_graph_hour_24 = pynutil.insert("hours: \"") + graph_hour_24 + pynutil.insert("\"") - final_graph_hour_12 = pynutil.insert("hours: \"") + graph_hour_12 + pynutil.insert("\"") - - final_graph_minute = pynutil.insert("minutes: \"") + graph_minute + pynutil.insert("\"") - final_graph_second = pynutil.insert("seconds: \"") + graph_minute + pynutil.insert("\"") - final_time_zone_optional = pynini.closure( - delete_space + insert_space + pynutil.insert("zone: \"") + time_zone_graph + pynutil.insert("\""), 0, 1, - ) - - # 02.30 h - graph_hm = ( - final_graph_hour_24 - + delete_time_delimiter - + (pynutil.delete("00") | (insert_space + final_graph_minute)) - + pynini.closure( - delete_time_delimiter + (pynini.cross("00", " seconds: \"0\"") | (insert_space + final_graph_second)), - 0, - 1, - ) # For seconds 2.30.35 h - + pynini.closure(delete_hora_suffix, 0, 1) # 2.30 is valid if unambiguous - + final_time_zone_optional - ) - - # 2 h 30 min - graph_hm |= ( - final_graph_hour_24 - + delete_hora_suffix - + delete_space - + (pynutil.delete("00") | (insert_space + final_graph_minute)) - + delete_minute_suffix - + pynini.closure( - delete_space - + (pynini.cross("00", " seconds: \"0\"") | (insert_space + final_graph_second)) - + delete_second_suffix, - 0, - 1, - ) # For seconds - + final_time_zone_optional - ) - - # 2.30 a. m. (Only for 12 hour clock) - graph_hm |= ( - final_graph_hour_12 - + delete_time_delimiter - + (pynutil.delete("00") | (insert_space + final_graph_minute)) - + pynini.closure( - delete_time_delimiter + (pynini.cross("00", " seconds: \"0\"") | (insert_space + final_graph_second)), - 0, - 1, - ) # For seconds 2.30.35 a. m. - + day_suffix - + final_time_zone_optional - ) - - graph_h = ( - pynini.union(final_graph_hour_only_24, final_graph_hour_only_12) + final_time_zone_optional - ) # Should always have a time indicator, else we'll pass to cardinals - - if not deterministic: - # This includes alternate vocalization (hour menos min, min para hour), here we shift the times and indicate a `style` tag - hour_shift_24 = pynini.invert(pynini.string_file(get_abs_path("data/time/hour_to_24.tsv"))) - hour_shift_12 = pynini.invert(pynini.string_file(get_abs_path("data/time/hour_to_12.tsv"))) - minute_shift = pynini.string_file(get_abs_path("data/time/minute_to.tsv")) - - graph_hour_to_24 = graph_24 @ hour_shift_24 @ cardinal_graph - graph_hour_to_12 = graph_12 @ hour_shift_12 @ cardinal_graph - - graph_minute_to = pynini.union(graph_minute_single, graph_minute_double) @ minute_shift @ cardinal_graph - - final_graph_hour_to_24 = pynutil.insert("hours: \"") + graph_hour_to_24 + pynutil.insert("\"") - final_graph_hour_to_12 = pynutil.insert("hours: \"") + graph_hour_to_12 + pynutil.insert("\"") - - final_graph_minute_to = pynutil.insert("minutes: \"") + graph_minute_to + pynutil.insert("\"") - - graph_menos = pynutil.insert(" style: \"1\"") - graph_para = pynutil.insert(" style: \"2\"") - - final_graph_style = graph_menos | graph_para - - # 02.30 h (omitting seconds since a bit awkward) - graph_hm |= ( - final_graph_hour_to_24 - + delete_time_delimiter - + insert_space - + final_graph_minute_to - + pynini.closure(delete_hora_suffix, 0, 1) # 2.30 is valid if unambiguous - + final_time_zone_optional - + final_graph_style - ) - - # 2 h 30 min - graph_hm |= ( - final_graph_hour_to_24 - + delete_hora_suffix - + delete_space - + insert_space - + final_graph_minute_to - + delete_minute_suffix - + final_time_zone_optional - + final_graph_style - ) - - # 2.30 a. m. (Only for 12 hour clock) - graph_hm |= ( - final_graph_hour_to_12 - + delete_time_delimiter - + insert_space - + final_graph_minute_to - + day_suffix - + final_time_zone_optional - + final_graph_style - ) - - final_graph = graph_hm | graph_h - if deterministic: - final_graph = final_graph + pynutil.insert(" preserve_order: true") - final_graph = final_graph.optimize() - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/es/taggers/tokenize_and_classify.py b/nemo_text_processing/text_normalization/es/taggers/tokenize_and_classify.py deleted file mode 100644 index c754fd1935dc..000000000000 --- a/nemo_text_processing/text_normalization/es/taggers/tokenize_and_classify.py +++ /dev/null @@ -1,150 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_WHITE_SPACE, - GraphFst, - delete_extra_space, - delete_space, - generator_main, -) -from nemo_text_processing.text_normalization.en.taggers.punctuation import PunctuationFst -from nemo_text_processing.text_normalization.es.taggers.cardinal import CardinalFst -from nemo_text_processing.text_normalization.es.taggers.date import DateFst -from nemo_text_processing.text_normalization.es.taggers.decimals import DecimalFst -from nemo_text_processing.text_normalization.es.taggers.electronic import ElectronicFst -from nemo_text_processing.text_normalization.es.taggers.fraction import FractionFst -from nemo_text_processing.text_normalization.es.taggers.measure import MeasureFst -from nemo_text_processing.text_normalization.es.taggers.money import MoneyFst -from nemo_text_processing.text_normalization.es.taggers.ordinal import OrdinalFst -from nemo_text_processing.text_normalization.es.taggers.telephone import TelephoneFst -from nemo_text_processing.text_normalization.es.taggers.time import TimeFst -from nemo_text_processing.text_normalization.es.taggers.whitelist import WhiteListFst -from nemo_text_processing.text_normalization.es.taggers.word import WordFst -from pynini.lib import pynutil - -from nemo.utils import logging - - -class ClassifyFst(GraphFst): - """ - Final class that composes all other classification grammars. This class can process an entire sentence, that is - lower cased. For deployment, this grammar will be compiled and exported to OpenFst finite-state archive (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - - Args: - input_case: accepting either "lower_cased" or "cased" input. - deterministic: if True will provide a single transduction option, - for False multiple options (used for audio-based normalization) - cache_dir: path to a dir with .far grammar file. Set to None to avoid using cache. - overwrite_cache: set to True to overwrite .far files - whitelist: path to a file with whitelist replacements - """ - - def __init__( - self, - input_case: str, - deterministic: bool = False, - cache_dir: str = None, - overwrite_cache: bool = False, - whitelist: str = None, - ): - super().__init__(name="tokenize_and_classify", kind="classify", deterministic=deterministic) - far_file = None - if cache_dir is not None and cache_dir != "None": - os.makedirs(cache_dir, exist_ok=True) - whitelist_file = os.path.basename(whitelist) if whitelist else "" - far_file = os.path.join( - cache_dir, f"_{input_case}_es_tn_{deterministic}_deterministic{whitelist_file}.far" - ) - if not overwrite_cache and far_file and os.path.exists(far_file): - self.fst = pynini.Far(far_file, mode="r")["tokenize_and_classify"] - logging.info(f"ClassifyFst.fst was restored from {far_file}.") - else: - logging.info(f"Creating ClassifyFst grammars. This might take some time...") - - self.cardinal = CardinalFst(deterministic=deterministic) - cardinal_graph = self.cardinal.fst - - self.ordinal = OrdinalFst(cardinal=self.cardinal, deterministic=deterministic) - ordinal_graph = self.ordinal.fst - - self.decimal = DecimalFst(cardinal=self.cardinal, deterministic=deterministic) - decimal_graph = self.decimal.fst - - self.fraction = FractionFst(cardinal=self.cardinal, ordinal=self.ordinal, deterministic=deterministic) - fraction_graph = self.fraction.fst - self.measure = MeasureFst( - cardinal=self.cardinal, decimal=self.decimal, fraction=self.fraction, deterministic=deterministic - ) - measure_graph = self.measure.fst - self.date = DateFst(cardinal=self.cardinal, deterministic=deterministic) - date_graph = self.date.fst - word_graph = WordFst(deterministic=deterministic).fst - self.time = TimeFst(self.cardinal, deterministic=deterministic) - time_graph = self.time.fst - self.telephone = TelephoneFst(deterministic=deterministic) - telephone_graph = self.telephone.fst - self.electronic = ElectronicFst(deterministic=deterministic) - electronic_graph = self.electronic.fst - self.money = MoneyFst(cardinal=self.cardinal, decimal=self.decimal, deterministic=deterministic) - money_graph = self.money.fst - self.whitelist = WhiteListFst(input_case=input_case, deterministic=deterministic, input_file=whitelist) - whitelist_graph = self.whitelist.fst - punct_graph = PunctuationFst(deterministic=deterministic).fst - - classify = ( - pynutil.add_weight(whitelist_graph, 1.01) - | pynutil.add_weight(time_graph, 1.09) - | pynutil.add_weight(measure_graph, 1.08) - | pynutil.add_weight(cardinal_graph, 1.1) - | pynutil.add_weight(fraction_graph, 1.09) - | pynutil.add_weight(date_graph, 1.1) - | pynutil.add_weight(ordinal_graph, 1.1) - | pynutil.add_weight(decimal_graph, 1.1) - | pynutil.add_weight(money_graph, 1.1) - | pynutil.add_weight(telephone_graph, 1.1) - | pynutil.add_weight(electronic_graph, 1.1) - | pynutil.add_weight(word_graph, 200) - ) - punct = pynutil.insert("tokens { ") + pynutil.add_weight(punct_graph, weight=2.1) + pynutil.insert(" }") - punct = pynini.closure( - pynini.compose(pynini.closure(NEMO_WHITE_SPACE, 1), delete_extra_space) - | (pynutil.insert(" ") + punct), - 1, - ) - token = pynutil.insert("tokens { ") + classify + pynutil.insert(" }") - token_plus_punct = ( - pynini.closure(punct + pynutil.insert(" ")) + token + pynini.closure(pynutil.insert(" ") + punct) - ) - - graph = token_plus_punct + pynini.closure( - ( - pynini.compose(pynini.closure(NEMO_WHITE_SPACE, 1), delete_extra_space) - | (pynutil.insert(" ") + punct + pynutil.insert(" ")) - ) - + token_plus_punct - ) - - graph = delete_space + graph + delete_space - graph |= punct - - self.fst = graph.optimize() - - if far_file: - generator_main(far_file, {"tokenize_and_classify": self.fst}) - logging.info(f"ClassifyFst grammars are saved to {far_file}.") diff --git a/nemo_text_processing/text_normalization/es/taggers/whitelist.py b/nemo_text_processing/text_normalization/es/taggers/whitelist.py deleted file mode 100644 index 9f6c6d99b458..000000000000 --- a/nemo_text_processing/text_normalization/es/taggers/whitelist.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst, convert_space -from nemo_text_processing.text_normalization.es.utils import get_abs_path, load_labels -from pynini.lib import pynutil - - -class WhiteListFst(GraphFst): - """ - Finite state transducer for classifying whitelist, e.g. - "sr." -> tokens { name: "señor" } - This class has highest priority among all classifier grammars. Whitelisted tokens are defined and loaded from "data/whitelist.tsv". - - Args: - input_case: accepting either "lower_cased" or "cased" input. - deterministic: if True will provide a single transduction option, - for False multiple options (used for audio-based normalization) - input_file: path to a file with whitelist replacements - """ - - def __init__(self, input_case: str, deterministic: bool = True, input_file: str = None): - super().__init__(name="whitelist", kind="classify", deterministic=deterministic) - - def _get_whitelist_graph(input_case, file): - whitelist = load_labels(file) - if input_case == "lower_cased": - whitelist = [[x[0].lower()] + x[1:] for x in whitelist] - graph = pynini.string_map(whitelist) - return graph - - graph = _get_whitelist_graph(input_case, get_abs_path("data/whitelist.tsv")) - if not deterministic and input_case != "lower_cased": - graph |= pynutil.add_weight( - _get_whitelist_graph("lower_cased", get_abs_path("data/whitelist.tsv")), weight=0.0001 - ) - - if input_file: - whitelist_provided = _get_whitelist_graph(input_case, input_file) - if not deterministic: - graph |= whitelist_provided - else: - graph = whitelist_provided - - if not deterministic: - units_graph = _get_whitelist_graph(input_case, file=get_abs_path("data/measures/measurements.tsv")) - graph |= units_graph - - self.graph = graph - self.final_graph = convert_space(self.graph).optimize() - self.fst = (pynutil.insert("name: \"") + self.final_graph + pynutil.insert("\"")).optimize() diff --git a/nemo_text_processing/text_normalization/es/taggers/word.py b/nemo_text_processing/text_normalization/es/taggers/word.py deleted file mode 100644 index 90ecbc99c041..000000000000 --- a/nemo_text_processing/text_normalization/es/taggers/word.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_SPACE, GraphFst -from pynini.lib import pynutil - - -class WordFst(GraphFst): - """ - Finite state transducer for classifying word. - e.g. dormir -> tokens { name: "dormir" } - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="word", kind="classify") - word = pynutil.insert("name: \"") + pynini.closure(NEMO_NOT_SPACE, 1) + pynutil.insert("\"") - self.fst = word.optimize() diff --git a/nemo_text_processing/text_normalization/es/utils.py b/nemo_text_processing/text_normalization/es/utils.py deleted file mode 100644 index 375bc1fb1f3e..000000000000 --- a/nemo_text_processing/text_normalization/es/utils.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import csv -import os - - -def get_abs_path(rel_path): - """ - Get absolute path - - Args: - rel_path: relative path to this file - - Returns absolute path - """ - return os.path.dirname(os.path.abspath(__file__)) + '/' + rel_path - - -def load_labels(abs_path): - """ - loads relative path file as dictionary - - Args: - abs_path: absolute path - - Returns dictionary of mappings - """ - with open(abs_path, encoding="utf-8") as label_tsv: - labels = list(csv.reader(label_tsv, delimiter="\t")) - return labels diff --git a/nemo_text_processing/text_normalization/es/verbalizers/__init__.py b/nemo_text_processing/text_normalization/es/verbalizers/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/es/verbalizers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/es/verbalizers/cardinal.py b/nemo_text_processing/text_normalization/es/verbalizers/cardinal.py deleted file mode 100644 index 1806d147785d..000000000000 --- a/nemo_text_processing/text_normalization/es/verbalizers/cardinal.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst -from nemo_text_processing.text_normalization.es.graph_utils import ( - add_cardinal_apocope_fem, - shift_cardinal_gender, - strip_cardinal_apocope, -) -from pynini.lib import pynutil - - -class CardinalFst(GraphFst): - """ - Finite state transducer for verbalizing cardinals - e.g. cardinal { integer: "dos" } -> "dos" - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="cardinal", kind="verbalize", deterministic=deterministic) - optional_sign = pynini.closure(pynini.cross("negative: \"true\" ", "menos "), 0, 1) - self.optional_sign = optional_sign - - integer = pynini.closure(NEMO_NOT_QUOTE, 1) - self.integer = pynutil.delete(" \"") + integer + pynutil.delete("\"") - - integer = pynutil.delete("integer:") + self.integer - - graph_masc = optional_sign + integer - graph_fem = shift_cardinal_gender(graph_masc) - - self.graph_masc = pynini.optimize(graph_masc) - self.graph_fem = pynini.optimize(graph_fem) - - # Adding adjustment for fem gender (choice of gender will be random) - graph = graph_masc | graph_fem - - if not deterministic: - # For alternate renderings when apocope is omitted (i.e. cardinal stands alone) - graph |= strip_cardinal_apocope(graph_masc) - # "una" will drop to "un" in unique contexts - graph |= add_cardinal_apocope_fem(graph_fem) - - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/es/verbalizers/date.py b/nemo_text_processing/text_normalization/es/verbalizers/date.py deleted file mode 100644 index c9f135ab6bd4..000000000000 --- a/nemo_text_processing/text_normalization/es/verbalizers/date.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_NOT_QUOTE, - NEMO_SIGMA, - NEMO_SPACE, - GraphFst, - delete_preserve_order, -) -from nemo_text_processing.text_normalization.es.graph_utils import strip_cardinal_apocope -from nemo_text_processing.text_normalization.es.taggers.date import articles -from pynini.lib import pynutil - - -class DateFst(GraphFst): - """ - Finite state transducer for verbalizing date, e.g. - date { day: "treinta y uno" month: "marzo" year: "dos mil" } -> "treinta y uno de marzo de dos mil" - date { day: "uno" month: "mayo" year: "del mil novecientos noventa" } -> "primero de mayo del mil novecientos noventa" - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="date", kind="verbalize", deterministic=deterministic) - - day_cardinal = pynutil.delete("day: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - day = strip_cardinal_apocope(day_cardinal) - - primero = pynini.cdrewrite(pynini.cross("uno", "primero"), "[BOS]", "[EOS]", NEMO_SIGMA) - day = ( - (day @ primero) if deterministic else pynini.union(day, day @ primero) - ) # Primero for first day is traditional, but will vary depending on region - - month = pynutil.delete("month: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - - year = ( - pynutil.delete("year: \"") - + articles - + NEMO_SPACE - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - - # Insert preposition if wasn't originally with the year. This would mean a space was present - year = pynutil.add_weight(year, -0.001) - year |= ( - pynutil.delete("year: \"") - + pynutil.insert("de ") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - - # day month year - graph_dmy = day + pynini.cross(NEMO_SPACE, " de ") + month + pynini.closure(pynini.accep(" ") + year, 0, 1) - - graph_mdy = month + NEMO_SPACE + day + pynini.closure(NEMO_SPACE + year, 0, 1) - if deterministic: - graph_mdy += pynutil.delete(" preserve_order: true") # Only accepts this if was explicitly passed - - self.graph = graph_dmy | graph_mdy - final_graph = self.graph + delete_preserve_order - - delete_tokens = self.delete_tokens(final_graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/es/verbalizers/decimals.py b/nemo_text_processing/text_normalization/es/verbalizers/decimals.py deleted file mode 100644 index 643c9a5dd865..000000000000 --- a/nemo_text_processing/text_normalization/es/verbalizers/decimals.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_NOT_QUOTE, - GraphFst, - delete_preserve_order, - delete_space, - insert_space, -) -from nemo_text_processing.text_normalization.es import LOCALIZATION -from nemo_text_processing.text_normalization.es.graph_utils import ( - add_cardinal_apocope_fem, - shift_cardinal_gender, - shift_number_gender, - strip_cardinal_apocope, -) -from pynini.lib import pynutil - - -class DecimalFst(GraphFst): - """ - Finite state transducer for classifying decimal, e.g. - decimal { negative: "true" integer_part: "dos" fractional_part: "cuatro cero" quantity: "billones" } -> menos dos coma quatro cero billones - decimal { integer_part: "un" quantity: "billón" } -> un billón - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="decimal", kind="classify", deterministic=deterministic) - - optional_sign = pynini.closure(pynini.cross("negative: \"true\"", "menos ") + delete_space, 0, 1) - integer = pynutil.delete("integer_part: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - fractional_default = ( - pynutil.delete("fractional_part: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - ) - - conjunction = pynutil.insert(" punto ") if LOCALIZATION == "am" else pynutil.insert(" coma ") - if not deterministic: - conjunction |= pynutil.insert(pynini.union(" con ", " y ")) - fractional_default |= strip_cardinal_apocope(fractional_default) - fractional = conjunction + fractional_default - - quantity = ( - delete_space - + insert_space - + pynutil.delete("quantity: \"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - optional_quantity = pynini.closure(quantity, 0, 1) - - graph_masc = optional_sign + pynini.union( - (integer + quantity), (integer + delete_space + fractional + optional_quantity) - ) - - # Allowing permutation for fem gender, don't include quantity since "million","billion", etc.. are masculine - graph_fem = optional_sign + (shift_cardinal_gender(integer) + delete_space + shift_number_gender(fractional)) - if not deterministic: # "una" will drop to "un" in certain cases - graph_fem |= add_cardinal_apocope_fem(graph_fem) - - self.numbers_only_quantity = ( - optional_sign - + pynini.union((integer + quantity), (integer + delete_space + fractional + quantity)).optimize() - ) - - self.graph_masc = (graph_masc + delete_preserve_order).optimize() - self.graph_fem = (graph_fem + delete_preserve_order).optimize() - - graph = graph_masc | graph_fem - - graph += delete_preserve_order - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/es/verbalizers/electronic.py b/nemo_text_processing/text_normalization/es/verbalizers/electronic.py deleted file mode 100644 index 0c866c550d59..000000000000 --- a/nemo_text_processing/text_normalization/es/verbalizers/electronic.py +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_NOT_QUOTE, - NEMO_SIGMA, - GraphFst, - delete_preserve_order, - insert_space, -) -from nemo_text_processing.text_normalization.es.utils import get_abs_path -from pynini.lib import pynutil - -digit_no_zero = pynini.invert(pynini.string_file(get_abs_path("data/numbers/digit.tsv"))) -zero = pynini.invert(pynini.string_file(get_abs_path("data/numbers/zero.tsv"))) - -graph_symbols = pynini.string_file(get_abs_path("data/electronic/symbols.tsv")) -server_common = pynini.string_file(get_abs_path("data/electronic/server_name.tsv")) -domain_common = pynini.string_file(get_abs_path("data/electronic/domain.tsv")) - - -class ElectronicFst(GraphFst): - """ - Finite state transducer for verbalizing electronic - e.g. electronic { username: "abc" domain: "hotmail.com" } -> "a b c arroba hotmail punto com" - -> "a b c arroba h o t m a i l punto c o m" - -> "a b c arroba hotmail punto c o m" - -> "a b c at h o t m a i l punto com" - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="electronic", kind="verbalize", deterministic=deterministic) - - graph_digit_no_zero = ( - digit_no_zero @ pynini.cdrewrite(pynini.cross("un", "uno"), "", "", NEMO_SIGMA).optimize() - ) - graph_digit = graph_digit_no_zero | zero - - def add_space_after_char(): - return pynini.closure(NEMO_NOT_QUOTE - pynini.accep(" ") + insert_space) + ( - NEMO_NOT_QUOTE - pynini.accep(" ") - ) - - verbalize_characters = pynini.cdrewrite(graph_symbols | graph_digit, "", "", NEMO_SIGMA) - - user_name = pynutil.delete("username: \"") + add_space_after_char() + pynutil.delete("\"") - user_name @= verbalize_characters - - convert_defaults = pynutil.add_weight(NEMO_NOT_QUOTE, weight=0.0001) | domain_common | server_common - domain = convert_defaults + pynini.closure(insert_space + convert_defaults) - domain @= verbalize_characters - - domain = pynutil.delete("domain: \"") + domain + pynutil.delete("\"") - protocol = ( - pynutil.delete("protocol: \"") - + add_space_after_char() @ pynini.cdrewrite(graph_symbols, "", "", NEMO_SIGMA) - + pynutil.delete("\"") - ) - self.graph = (pynini.closure(protocol + pynini.accep(" "), 0, 1) + domain) | ( - user_name + pynini.accep(" ") + pynutil.insert("arroba ") + domain - ) - delete_tokens = self.delete_tokens(self.graph + delete_preserve_order) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/es/verbalizers/fraction.py b/nemo_text_processing/text_normalization/es/verbalizers/fraction.py deleted file mode 100644 index de2cbb9741f1..000000000000 --- a/nemo_text_processing/text_normalization/es/verbalizers/fraction.py +++ /dev/null @@ -1,192 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_CHAR, - NEMO_NOT_QUOTE, - NEMO_NOT_SPACE, - NEMO_SIGMA, - NEMO_SPACE, - GraphFst, - delete_space, - insert_space, -) -from nemo_text_processing.text_normalization.es.graph_utils import ( - accents, - shift_cardinal_gender, - strip_cardinal_apocope, -) -from pynini.lib import pynutil - - -class FractionFst(GraphFst): - """ - Finite state transducer for verbalizing fraction - e.g. tokens { fraction { integer: "treinta y tres" numerator: "cuatro" denominator: "quinto" } } -> - treinta y tres y cuatro quintos - - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="fraction", kind="verbalize", deterministic=deterministic) - - # Derivational strings append 'avo' as a suffix. Adding space for processing aid - fraction_stem = pynutil.insert(" avo") - plural = pynutil.insert("s") - conjunction = pynutil.insert(" y ") - - integer = ( - pynutil.delete("integer_part: \"") - + strip_cardinal_apocope(pynini.closure(NEMO_NOT_QUOTE)) - + pynutil.delete("\"") - ) - - numerator_one = pynutil.delete("numerator: \"") + pynini.accep("un") + pynutil.delete("\" ") - numerator = ( - pynutil.delete("numerator: \"") - + pynini.difference(pynini.closure(NEMO_NOT_QUOTE), "un") - + pynutil.delete("\" ") - ) - - denominator_add_stem = pynutil.delete("denominator: \"") + ( - pynini.closure(NEMO_NOT_QUOTE) - + fraction_stem - + pynutil.delete("\" morphosyntactic_features: \"add_root\"") - ) - denominator_ordinal = pynutil.delete("denominator: \"") + ( - pynini.closure(NEMO_NOT_QUOTE) + pynutil.delete("\" morphosyntactic_features: \"ordinal\"") - ) - denominator_cardinal = pynutil.delete("denominator: \"") + ( - pynini.closure(NEMO_NOT_QUOTE) + pynutil.delete("\"") - ) - - denominator_singular = pynini.union(denominator_add_stem, denominator_ordinal) - if not deterministic: - # Occasional exceptions - denominator_singular |= denominator_add_stem @ pynini.string_map( - [("once avo", "undécimo"), ("doce avo", "duodécimo")] - ) - denominator_plural = denominator_singular + plural - - # Merging operations - merge = pynini.cdrewrite( - pynini.cross(" y ", "i"), "", "", NEMO_SIGMA - ) # The denominator must be a single word, with the conjunction "y" replaced by i - merge @= pynini.cdrewrite(delete_space, "", pynini.difference(NEMO_CHAR, "parte"), NEMO_SIGMA) - - # The merger can produce duplicate vowels. This is not allowed in orthography - delete_duplicates = pynini.string_map([("aa", "a"), ("oo", "o")]) # Removes vowels - delete_duplicates = pynini.cdrewrite(delete_duplicates, "", "", NEMO_SIGMA) - - remove_accents = pynini.cdrewrite( - accents, - pynini.union(NEMO_SPACE, pynini.accep("[BOS]")) + pynini.closure(NEMO_NOT_SPACE), - pynini.closure(NEMO_NOT_SPACE) + pynini.union("avo", "ava", "ésimo", "ésima"), - NEMO_SIGMA, - ) - merge_into_single_word = merge @ remove_accents @ delete_duplicates - - fraction_default = numerator + delete_space + insert_space + (denominator_plural @ merge_into_single_word) - - fraction_with_one = ( - numerator_one + delete_space + insert_space + (denominator_singular @ merge_into_single_word) - ) - - fraction_with_cardinal = strip_cardinal_apocope(numerator | numerator_one) - fraction_with_cardinal += ( - delete_space + pynutil.insert(" sobre ") + strip_cardinal_apocope(denominator_cardinal) - ) - - if not deterministic: - # There is an alternative rendering where ordinals act as adjectives for 'parte'. This requires use of the feminine - # Other rules will manage use of "un" at end, so just worry about endings - exceptions = pynini.string_map([("tercia", "tercera")]) - apply_exceptions = pynini.cdrewrite(exceptions, "", "", NEMO_SIGMA) - vowel_change = pynini.cdrewrite(pynini.cross("o", "a"), "", pynini.accep("[EOS]"), NEMO_SIGMA) - - denominator_singular_fem = shift_cardinal_gender(denominator_singular) @ vowel_change @ apply_exceptions - denominator_plural_fem = denominator_singular_fem + plural - - numerator_one_fem = shift_cardinal_gender(numerator_one) - numerator_fem = shift_cardinal_gender(numerator) - - fraction_with_cardinal |= ( - (numerator_one_fem | numerator_fem) - + delete_space - + pynutil.insert(" sobre ") - + shift_cardinal_gender(denominator_cardinal) - ) - - # Still need to manage stems - merge_stem = pynini.cdrewrite( - delete_space, "", pynini.union("avo", "ava", "avos", "avas"), NEMO_SIGMA - ) # For managing alternative spacing - merge_stem @= remove_accents @ delete_duplicates - - fraction_with_one_fem = numerator_one_fem + delete_space + insert_space - fraction_with_one_fem += pynini.union( - denominator_singular_fem @ merge_stem, denominator_singular_fem @ merge_into_single_word - ) # Both forms exists - fraction_with_one_fem += pynutil.insert(" parte") - fraction_with_one_fem @= pynini.cdrewrite( - pynini.cross("una media", "media"), "", "", NEMO_SIGMA - ) # "media" not "una media" - - fraction_default_fem = numerator_fem + delete_space + insert_space - fraction_default_fem += pynini.union( - denominator_plural_fem @ merge_stem, denominator_plural_fem @ merge_into_single_word - ) - fraction_default_fem += pynutil.insert(" partes") - - fraction_default |= ( - numerator + delete_space + insert_space + denominator_plural @ merge_stem - ) # Case of no merger - fraction_default |= fraction_default_fem - - fraction_with_one |= numerator_one + delete_space + insert_space + denominator_singular @ merge_stem - fraction_with_one |= fraction_with_one_fem - - fraction_with_one @= pynini.cdrewrite( - pynini.cross("un medio", "medio"), "", "", NEMO_SIGMA - ) # "medio" not "un medio" - - fraction = fraction_with_one | fraction_default | fraction_with_cardinal - graph_masc = pynini.closure(integer + delete_space + conjunction, 0, 1) + fraction - - # Manage cases of fem gender (only shows on integer except for "medio") - integer_fem = shift_cardinal_gender(integer) - fraction_default |= ( - shift_cardinal_gender(numerator) - + delete_space - + insert_space - + (denominator_plural @ pynini.cross("medios", "medias")) - ) - fraction_with_one |= ( - pynutil.delete(numerator_one) + delete_space + (denominator_singular @ pynini.cross("medio", "media")) - ) - - fraction_fem = fraction_with_one | fraction_default | fraction_with_cardinal - graph_fem = pynini.closure(integer_fem + delete_space + conjunction, 0, 1) + fraction_fem - - self.graph_masc = pynini.optimize(graph_masc) - self.graph_fem = pynini.optimize(graph_fem) - - self.graph = graph_masc | graph_fem - - delete_tokens = self.delete_tokens(self.graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/es/verbalizers/measure.py b/nemo_text_processing/text_normalization/es/verbalizers/measure.py deleted file mode 100644 index b04dd5bf9fe3..000000000000 --- a/nemo_text_processing/text_normalization/es/verbalizers/measure.py +++ /dev/null @@ -1,108 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_NOT_QUOTE, - NEMO_SIGMA, - NEMO_SPACE, - NEMO_WHITE_SPACE, - GraphFst, - delete_extra_space, - delete_preserve_order, -) -from nemo_text_processing.text_normalization.es.graph_utils import ones -from nemo_text_processing.text_normalization.es.utils import get_abs_path -from pynini.lib import pynutil - -unit_plural_fem = pynini.string_file(get_abs_path("data/measures/measurements_plural_fem.tsv")) -unit_plural_masc = pynini.string_file(get_abs_path("data/measures/measurements_plural_masc.tsv")) - -unit_singular_fem = pynini.project(unit_plural_fem, "input") -unit_singular_masc = pynini.project(unit_plural_masc, "input") - -unit_plural_fem = pynini.project(unit_plural_fem, "output") -unit_plural_masc = pynini.project(unit_plural_masc, "output") - - -class MeasureFst(GraphFst): - """ - Finite state transducer for verbalizing measure, e.g. - measure { cardinal { integer: "dos" units: "gramos" } } -> "dos gramos" - measure { decimal { integer_part: "dos" quantity: "millones" units: "gramos" } } -> "dos millones de gramos" - - Args: - decimal: DecimalFst - cardinal: CardinalFst - fraction: FractionFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, decimal: GraphFst, cardinal: GraphFst, fraction: GraphFst, deterministic: bool): - super().__init__(name="measure", kind="verbalize", deterministic=deterministic) - - graph_decimal_masc = decimal.delete_tokens(decimal.graph_masc) - graph_decimal_fem = decimal.delete_tokens(decimal.graph_fem) - graph_cardinal_masc = cardinal.delete_tokens(cardinal.graph_masc) - graph_cardinal_fem = cardinal.delete_tokens(cardinal.graph_fem) - graph_fraction_fem = fraction.delete_tokens(fraction.graph_fem) - graph_fraction_masc = fraction.delete_tokens(fraction.graph_masc) - - unit_masc = (unit_plural_masc | unit_singular_masc) + pynini.closure( - NEMO_WHITE_SPACE + "por" + pynini.closure(NEMO_NOT_QUOTE, 1), 0, 1 - ) - unit_masc |= "por" + pynini.closure(NEMO_NOT_QUOTE, 1) - unit_masc = pynutil.delete("units: \"") + (pynini.closure(NEMO_NOT_QUOTE) @ unit_masc) + pynutil.delete("\"") - - unit_fem = (unit_plural_fem | unit_singular_fem) + pynini.closure( - NEMO_WHITE_SPACE + "por" + pynini.closure(NEMO_NOT_QUOTE, 1), 0, 1 - ) - unit_fem = pynutil.delete("units: \"") + (pynini.closure(NEMO_NOT_QUOTE) @ unit_fem) + pynutil.delete("\"") - - graph_masc = (graph_cardinal_masc | graph_decimal_masc) + NEMO_WHITE_SPACE + unit_masc - graph_masc |= graph_fraction_masc + NEMO_WHITE_SPACE + pynutil.insert("de ") + unit_masc - graph_masc |= pynutil.add_weight( - graph_fraction_masc @ (NEMO_SIGMA + pynini.union("medio", "medios")) + NEMO_WHITE_SPACE + unit_masc, -0.001 - ) # "medio litro" not "medio de litro" - - graph_fem = (graph_cardinal_fem | graph_decimal_fem) + NEMO_WHITE_SPACE + unit_fem - graph_fem |= graph_fraction_fem + NEMO_WHITE_SPACE + pynutil.insert("de ") + unit_fem - graph_fem |= pynutil.add_weight( - graph_fraction_fem @ (NEMO_SIGMA + pynini.union("media", "medias")) + NEMO_WHITE_SPACE + unit_fem, -0.001 - ) - - graph = graph_masc | graph_fem - - graph = ( - pynini.cdrewrite( - pynutil.insert(" de"), "quantity: \"" + pynini.closure(NEMO_NOT_QUOTE, 1), "\"", NEMO_SIGMA - ) - @ graph - ) # billones de xyz - - graph @= pynini.cdrewrite(pynini.cross(ones, "uno"), "", NEMO_WHITE_SPACE + "por", NEMO_SIGMA) - - # To manage alphanumeric combonations ("a-8, 5x"), we let them use a weighted default path. - alpha_num_unit = pynutil.delete("units: \"") + pynini.closure(NEMO_NOT_QUOTE) + pynutil.delete("\"") - graph_alpha_num = pynini.union( - (graph_cardinal_masc | graph_decimal_masc) + NEMO_SPACE + alpha_num_unit, - alpha_num_unit + delete_extra_space + (graph_cardinal_masc | graph_decimal_masc), - ) - - graph |= pynutil.add_weight(graph_alpha_num, 0.01) - - graph += delete_preserve_order - - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/es/verbalizers/money.py b/nemo_text_processing/text_normalization/es/verbalizers/money.py deleted file mode 100644 index d2ba9c707f78..000000000000 --- a/nemo_text_processing/text_normalization/es/verbalizers/money.py +++ /dev/null @@ -1,166 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_NOT_QUOTE, - NEMO_SIGMA, - NEMO_SPACE, - GraphFst, - delete_preserve_order, -) -from nemo_text_processing.text_normalization.es.graph_utils import shift_cardinal_gender, strip_cardinal_apocope -from nemo_text_processing.text_normalization.es.utils import get_abs_path -from pynini.lib import pynutil - -fem = pynini.string_file((get_abs_path("data/money/currency_plural_fem.tsv"))) -masc = pynini.string_file((get_abs_path("data/money/currency_plural_masc.tsv"))) - -fem_singular = pynini.project(fem, "input") -masc_singular = pynini.project(masc, "input") - -fem_plural = pynini.project(fem, "output") -masc_plural = pynini.project(masc, "output") - - -class MoneyFst(GraphFst): - """ - Finite state transducer for verbalizing money, e.g. - money { currency_maj: "euro" integer_part: "un"} -> "un euro" - money { currency_maj: "euro" integer_part: "un" fractional_part: "cero cero un"} -> "uno coma cero cero uno euros" - money { integer_part: "un" currency_maj: "libra" fractional_part: "cuarenta" preserve_order: true} -> "una libra cuarenta" - money { integer_part: "un" currency_maj: "libra" fractional_part: "cuarenta" currency_min: "peniques" preserve_order: true} -> "una libra con cuarenta peniques" - money { fractional_part: "un" currency_min: "penique" preserve_order: true} -> "un penique" - - Args: - decimal: GraphFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, decimal: GraphFst, deterministic: bool = True): - super().__init__(name="money", kind="verbalize", deterministic=deterministic) - - maj_singular_masc = ( - pynutil.delete("currency_maj: \"") - + (pynini.closure(NEMO_NOT_QUOTE, 1) @ masc_singular) - + pynutil.delete("\"") - ) - maj_singular_fem = ( - pynutil.delete("currency_maj: \"") - + (pynini.closure(NEMO_NOT_QUOTE, 1) @ fem_singular) - + pynutil.delete("\"") - ) - - maj_plural_masc = ( - pynutil.delete("currency_maj: \"") - + (pynini.closure(NEMO_NOT_QUOTE, 1) @ masc_plural) - + pynutil.delete("\"") - ) - maj_plural_fem = ( - pynutil.delete("currency_maj: \"") - + (pynini.closure(NEMO_NOT_QUOTE, 1) @ fem_plural) - + pynutil.delete("\"") - ) - - maj_masc = maj_plural_masc | maj_singular_masc # Tagger kept quantity resolution stable - maj_fem = maj_plural_fem | maj_singular_fem - - min_singular_masc = ( - pynutil.delete("currency_min: \"") - + (pynini.closure(NEMO_NOT_QUOTE, 1) @ masc_singular) - + pynutil.delete("\"") - ) - min_singular_fem = ( - pynutil.delete("currency_min: \"") - + (pynini.closure(NEMO_NOT_QUOTE, 1) @ fem_singular) - + pynutil.delete("\"") - ) - - min_plural_masc = ( - pynutil.delete("currency_min: \"") - + (pynini.closure(NEMO_NOT_QUOTE, 1) @ masc_plural) - + pynutil.delete("\"") - ) - min_plural_fem = ( - pynutil.delete("currency_min: \"") - + (pynini.closure(NEMO_NOT_QUOTE, 1) @ fem_plural) - + pynutil.delete("\"") - ) - - min_masc = min_plural_masc | min_singular_masc - min_fem = min_plural_fem | min_singular_fem - - fractional_part = ( - pynutil.delete("fractional_part: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - ) - - integer_part = pynutil.delete("integer_part: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - optional_add_and = pynini.closure(pynutil.insert(pynini.union("con ", "y ")), 0, 1) - - # *** currency_maj - graph_integer_masc = integer_part + NEMO_SPACE + maj_masc - graph_integer_fem = shift_cardinal_gender(integer_part) + NEMO_SPACE + maj_fem - - graph_integer = graph_integer_fem | graph_integer_masc - - # *** currency_maj + (***) | ((con) *** current_min) - graph_integer_with_minor_masc = ( - graph_integer_masc - + NEMO_SPACE - + pynini.union( - optional_add_and + strip_cardinal_apocope(fractional_part), - (optional_add_and + fractional_part + NEMO_SPACE + min_masc), - (optional_add_and + shift_cardinal_gender(fractional_part) + NEMO_SPACE + min_fem), - ) # Could be minor currency that is different gender - + delete_preserve_order - ) - - graph_integer_with_minor_fem = ( - graph_integer_fem - + NEMO_SPACE - + pynini.union( - optional_add_and + shift_cardinal_gender(fractional_part), - (optional_add_and + fractional_part + NEMO_SPACE + min_masc), - (optional_add_and + shift_cardinal_gender(fractional_part) + NEMO_SPACE + min_fem), - ) # Could be minor currency that is different gender - + delete_preserve_order - ) - - graph_integer_with_minor = graph_integer_with_minor_fem | graph_integer_with_minor_masc - - ## *** coma *** currency_maj - graph_decimal_masc = decimal.graph_masc + NEMO_SPACE + maj_masc - - graph_decimal_fem = decimal.graph_fem - graph_decimal_fem |= decimal.numbers_only_quantity # can still have "x billions" with fem currency - graph_decimal_fem += NEMO_SPACE + maj_fem - - graph_decimal = graph_decimal_fem | graph_decimal_masc - graph_decimal = ( - pynini.cdrewrite( - pynutil.insert(" de"), "quantity: \"" + pynini.closure(NEMO_NOT_QUOTE, 1), "\"", NEMO_SIGMA - ) - @ graph_decimal - ) # formally it's millones/billones de *** - - # *** current_min - graph_minor_masc = fractional_part + NEMO_SPACE + min_masc + delete_preserve_order - graph_minor_fem = shift_cardinal_gender(fractional_part) + NEMO_SPACE + min_fem + delete_preserve_order - - graph_minor = graph_minor_fem | graph_minor_masc - - graph = graph_integer | graph_integer_with_minor | graph_decimal | graph_minor - - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/es/verbalizers/ordinal.py b/nemo_text_processing/text_normalization/es/verbalizers/ordinal.py deleted file mode 100644 index 009cdf34352a..000000000000 --- a/nemo_text_processing/text_normalization/es/verbalizers/ordinal.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, NEMO_SIGMA, NEMO_SPACE, GraphFst -from nemo_text_processing.text_normalization.es.graph_utils import shift_number_gender -from pynini.lib import pynutil - - -class OrdinalFst(GraphFst): - """ - Finite state transducer for verbalizing ordinals - e.g. ordinal { integer: "tercer" } } -> "tercero" - -> "tercera" - -> "tercer" - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="ordinal", kind="verbalize", deterministic=deterministic) - - graph = pynutil.delete("integer: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - - # masculne gender we leave as is - graph_masc = graph + pynutil.delete(" morphosyntactic_features: \"gender_masc") - - # shift gender - graph_fem_ending = graph @ pynini.cdrewrite( - pynini.cross("o", "a"), "", NEMO_SPACE | pynini.accep("[EOS]"), NEMO_SIGMA - ) - graph_fem = shift_number_gender(graph_fem_ending) + pynutil.delete(" morphosyntactic_features: \"gender_fem") - - # Apocope just changes tercero and primero. May occur if someone wrote 11.er (uncommon) - graph_apocope = ( - pynini.cross("tercero", "tercer") - | pynini.cross("primero", "primer") - | pynini.cross("undécimo", "decimoprimer") - ) # In case someone wrote 11.er with deterministic - graph_apocope = (graph @ pynini.cdrewrite(graph_apocope, "", "", NEMO_SIGMA)) + pynutil.delete( - " morphosyntactic_features: \"apocope" - ) - - graph = graph_apocope | graph_masc | graph_fem - - if not deterministic: - # Plural graph - graph_plural = pynini.cdrewrite( - pynutil.insert("s"), pynini.union("o", "a"), NEMO_SPACE | pynini.accep("[EOS]"), NEMO_SIGMA - ) - - graph |= (graph @ graph_plural) + pynutil.delete("/plural") - - self.graph = graph + pynutil.delete("\"") - - delete_tokens = self.delete_tokens(self.graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/es/verbalizers/telephone.py b/nemo_text_processing/text_normalization/es/verbalizers/telephone.py deleted file mode 100644 index 8e8932ea2cc2..000000000000 --- a/nemo_text_processing/text_normalization/es/verbalizers/telephone.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst -from pynini.lib import pynutil - - -class TelephoneFst(GraphFst): - """ - Finite state transducer for verbalizing telephone, e.g. - telephone { number_part: "uno dos tres uno dos tres cinco seis siete ocho" } - -> uno dos tres uno dos tres cinco seis siete ocho - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="telephone", kind="verbalize") - - number_part = pynutil.delete("number_part: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - delete_tokens = self.delete_tokens(number_part) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/es/verbalizers/time.py b/nemo_text_processing/text_normalization/es/verbalizers/time.py deleted file mode 100644 index 23fa1d180d61..000000000000 --- a/nemo_text_processing/text_normalization/es/verbalizers/time.py +++ /dev/null @@ -1,256 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_NOT_QUOTE, - NEMO_SIGMA, - GraphFst, - delete_preserve_order, - delete_space, - insert_space, -) -from nemo_text_processing.text_normalization.es.utils import get_abs_path -from pynini.lib import pynutil - -alt_minutes = pynini.string_file(get_abs_path("data/time/alt_minutes.tsv")) - -morning_times = pynini.string_file(get_abs_path("data/time/morning_times.tsv")) -afternoon_times = pynini.string_file(get_abs_path("data/time/afternoon_times.tsv")) -evening_times = pynini.string_file(get_abs_path("data/time/evening_times.tsv")) - - -class TimeFst(GraphFst): - """ - Finite state transducer for verbalizing time, e.g. - time { hours: "doce" minutes: "media" suffix: "a m" } -> doce y media de la noche - time { hours: "doce" } -> twelve o'clock - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="time", kind="verbalize", deterministic=deterministic) - - change_minutes = pynini.cdrewrite(alt_minutes, pynini.accep("[BOS]"), pynini.accep("[EOS]"), NEMO_SIGMA) - - morning_phrases = pynini.cross("am", "de la mañana") - afternoon_phrases = pynini.cross("pm", "de la tarde") - evening_phrases = pynini.cross("pm", "de la noche") - - # For the 12's - mid_times = pynini.accep("doce") - mid_phrases = ( - pynini.string_map([("pm", "del mediodía"), ("am", "de la noche")]) - if deterministic - else pynini.string_map( - [ - ("pm", "de la mañana"), - ("pm", "del día"), - ("pm", "del mediodía"), - ("am", "de la noche"), - ("am", "de la medianoche"), - ] - ) - ) - - hour = ( - pynutil.delete("hours:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - minute = ( - pynutil.delete("minutes:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - minute = (minute @ change_minutes) if deterministic else pynini.union(minute, minute @ change_minutes) - - suffix = ( - pynutil.delete("suffix:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - zone = ( - pynutil.delete("zone:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - optional_zone = pynini.closure(delete_space + insert_space + zone, 0, 1) - second = ( - pynutil.delete("seconds:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - - graph_hms = ( - hour - + pynutil.insert(" horas ") - + delete_space - + minute - + pynutil.insert(" minutos y ") - + delete_space - + second - + pynutil.insert(" segundos") - ) - - graph_hm = hour + delete_space + pynutil.insert(" y ") + minute - graph_hm |= pynini.union( - (hour @ morning_times) - + delete_space - + pynutil.insert(" y ") - + minute - + delete_space - + insert_space - + (suffix @ morning_phrases), - (hour @ afternoon_times) - + delete_space - + pynutil.insert(" y ") - + minute - + delete_space - + insert_space - + (suffix @ afternoon_phrases), - (hour @ evening_times) - + delete_space - + pynutil.insert(" y ") - + minute - + delete_space - + insert_space - + (suffix @ evening_phrases), - (hour @ mid_times) - + delete_space - + pynutil.insert(" y ") - + minute - + delete_space - + insert_space - + (suffix @ mid_phrases), - ) - - graph_h = pynini.union( - hour, - (hour @ morning_times) + delete_space + insert_space + (suffix @ morning_phrases), - (hour @ afternoon_times) + delete_space + insert_space + (suffix @ afternoon_phrases), - (hour @ evening_times) + delete_space + insert_space + (suffix @ evening_phrases), - (hour @ mid_times) + delete_space + insert_space + (suffix @ mid_phrases), - ) - - graph = (graph_hms | graph_hm | graph_h) + optional_zone - - if not deterministic: - graph_style_1 = pynutil.delete(" style: \"1\"") - graph_style_2 = pynutil.delete(" style: \"2\"") - - graph_menos = hour + delete_space + pynutil.insert(" menos ") + minute + graph_style_1 - graph_menos |= ( - (hour @ morning_times) - + delete_space - + pynutil.insert(" menos ") - + minute - + delete_space - + insert_space - + (suffix @ morning_phrases) - + graph_style_1 - ) - graph_menos |= ( - (hour @ afternoon_times) - + delete_space - + pynutil.insert(" menos ") - + minute - + delete_space - + insert_space - + (suffix @ afternoon_phrases) - + graph_style_1 - ) - graph_menos |= ( - (hour @ evening_times) - + delete_space - + pynutil.insert(" menos ") - + minute - + delete_space - + insert_space - + (suffix @ evening_phrases) - + graph_style_1 - ) - graph_menos |= ( - (hour @ mid_times) - + delete_space - + pynutil.insert(" menos ") - + minute - + delete_space - + insert_space - + (suffix @ mid_phrases) - + graph_style_1 - ) - graph_menos += optional_zone - - graph_para = minute + pynutil.insert(" para las ") + delete_space + hour + graph_style_2 - graph_para |= ( - minute - + pynutil.insert(" para las ") - + delete_space - + (hour @ morning_times) - + delete_space - + insert_space - + (suffix @ morning_phrases) - + graph_style_2 - ) - graph_para |= ( - minute - + pynutil.insert(" para las ") - + delete_space - + (hour @ afternoon_times) - + delete_space - + insert_space - + (suffix @ afternoon_phrases) - + graph_style_2 - ) - graph_para |= ( - minute - + pynutil.insert(" para las ") - + delete_space - + (hour @ evening_times) - + delete_space - + insert_space - + (suffix @ evening_phrases) - + graph_style_2 - ) - graph_para |= ( - minute - + pynutil.insert(" para las ") - + delete_space - + (hour @ mid_times) - + delete_space - + insert_space - + (suffix @ mid_phrases) - + graph_style_2 - ) - graph_para += optional_zone - graph_para @= pynini.cdrewrite( - pynini.cross(" las ", " la "), "para", "una", NEMO_SIGMA - ) # Need agreement with one - - graph |= graph_menos | graph_para - delete_tokens = self.delete_tokens(graph + delete_preserve_order) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/es/verbalizers/verbalize.py b/nemo_text_processing/text_normalization/es/verbalizers/verbalize.py deleted file mode 100644 index 28810d67f897..000000000000 --- a/nemo_text_processing/text_normalization/es/verbalizers/verbalize.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst -from nemo_text_processing.text_normalization.en.verbalizers.whitelist import WhiteListFst -from nemo_text_processing.text_normalization.es.verbalizers.cardinal import CardinalFst -from nemo_text_processing.text_normalization.es.verbalizers.date import DateFst -from nemo_text_processing.text_normalization.es.verbalizers.decimals import DecimalFst -from nemo_text_processing.text_normalization.es.verbalizers.electronic import ElectronicFst -from nemo_text_processing.text_normalization.es.verbalizers.fraction import FractionFst -from nemo_text_processing.text_normalization.es.verbalizers.measure import MeasureFst -from nemo_text_processing.text_normalization.es.verbalizers.money import MoneyFst -from nemo_text_processing.text_normalization.es.verbalizers.ordinal import OrdinalFst -from nemo_text_processing.text_normalization.es.verbalizers.telephone import TelephoneFst -from nemo_text_processing.text_normalization.es.verbalizers.time import TimeFst - - -class VerbalizeFst(GraphFst): - """ - Composes other verbalizer grammars. - For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - - Args: - deterministic: if True will provide a single transduction option, - for False multiple options (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="verbalize", kind="verbalize", deterministic=deterministic) - cardinal = CardinalFst(deterministic=deterministic) - cardinal_graph = cardinal.fst - ordinal = OrdinalFst(deterministic=deterministic) - ordinal_graph = ordinal.fst - decimal = DecimalFst(deterministic=deterministic) - decimal_graph = decimal.fst - fraction = FractionFst(deterministic=deterministic) - fraction_graph = fraction.fst - date = DateFst(deterministic=deterministic) - date_graph = date.fst - measure = MeasureFst(cardinal=cardinal, decimal=decimal, fraction=fraction, deterministic=deterministic) - measure_graph = measure.fst - electronic = ElectronicFst(deterministic=deterministic) - electronic_graph = electronic.fst - whitelist_graph = WhiteListFst(deterministic=deterministic).fst - money_graph = MoneyFst(decimal=decimal, deterministic=deterministic).fst - telephone_graph = TelephoneFst(deterministic=deterministic).fst - time_graph = TimeFst(deterministic=deterministic).fst - - graph = ( - cardinal_graph - | measure_graph - | decimal_graph - | ordinal_graph - | date_graph - | electronic_graph - | money_graph - | fraction_graph - | whitelist_graph - | telephone_graph - | time_graph - ) - self.fst = graph diff --git a/nemo_text_processing/text_normalization/es/verbalizers/verbalize_final.py b/nemo_text_processing/text_normalization/es/verbalizers/verbalize_final.py deleted file mode 100644 index fe35f769c3d7..000000000000 --- a/nemo_text_processing/text_normalization/es/verbalizers/verbalize_final.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - GraphFst, - delete_extra_space, - delete_space, - generator_main, -) -from nemo_text_processing.text_normalization.en.verbalizers.word import WordFst -from nemo_text_processing.text_normalization.es.verbalizers.verbalize import VerbalizeFst -from pynini.lib import pynutil - -from nemo.utils import logging - - -class VerbalizeFinalFst(GraphFst): - """ - Finite state transducer that verbalizes an entire sentence - - Args: - deterministic: if True will provide a single transduction option, - for False multiple options (used for audio-based normalization) - cache_dir: path to a dir with .far grammar file. Set to None to avoid using cache. - overwrite_cache: set to True to overwrite .far files - """ - - def __init__(self, deterministic: bool = True, cache_dir: str = None, overwrite_cache: bool = False): - super().__init__(name="verbalize_final", kind="verbalize", deterministic=deterministic) - - far_file = None - if cache_dir is not None and cache_dir != "None": - os.makedirs(cache_dir, exist_ok=True) - far_file = os.path.join(cache_dir, f"es_tn_{deterministic}_deterministic_verbalizer.far") - if not overwrite_cache and far_file and os.path.exists(far_file): - self.fst = pynini.Far(far_file, mode="r")["verbalize"] - logging.info(f'VerbalizeFinalFst graph was restored from {far_file}.') - else: - - verbalize = VerbalizeFst(deterministic=deterministic).fst - word = WordFst(deterministic=deterministic).fst - types = verbalize | word - graph = ( - pynutil.delete("tokens") - + delete_space - + pynutil.delete("{") - + delete_space - + types - + delete_space - + pynutil.delete("}") - ) - graph = delete_space + pynini.closure(graph + delete_extra_space) + graph + delete_space - - self.fst = graph.optimize() - if far_file: - generator_main(far_file, {"verbalize": self.fst}) - logging.info(f"VerbalizeFinalFst grammars are saved to {far_file}.") diff --git a/nemo_text_processing/text_normalization/normalize.py b/nemo_text_processing/text_normalization/normalize.py deleted file mode 100644 index e1a57e01d5bf..000000000000 --- a/nemo_text_processing/text_normalization/normalize.py +++ /dev/null @@ -1,543 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import itertools -import os -import re -from argparse import ArgumentParser -from collections import OrderedDict -from math import factorial -from time import perf_counter -from typing import Dict, List, Union - -import pynini -import regex -from joblib import Parallel, delayed -from nemo_text_processing.text_normalization.data_loader_utils import ( - load_file, - post_process_punct, - pre_process, - write_file, -) -from nemo_text_processing.text_normalization.token_parser import PRESERVE_ORDER_KEY, TokenParser -from pynini.lib.rewrite import top_rewrite -from tqdm import tqdm - -try: - from nemo.collections.common.tokenizers.moses_tokenizers import MosesProcessor - - NLP_AVAILABLE = True -except (ModuleNotFoundError, ImportError) as e: - NLP_AVAILABLE = False - -SPACE_DUP = re.compile(' {2,}') - - -class Normalizer: - """ - Normalizer class that converts text from written to spoken form. - Useful for TTS preprocessing. - - Args: - input_case: expected input capitalization - lang: language specifying the TN rules, by default: English - cache_dir: path to a dir with .far grammar file. Set to None to avoid using cache. - overwrite_cache: set to True to overwrite .far files - whitelist: path to a file with whitelist replacements - post_process: WFST-based post-processing, e.g. to remove extra spaces added during TN. - Note: punct_post_process flag in normalize() supports all languages. - """ - - def __init__( - self, - input_case: str, - lang: str = 'en', - deterministic: bool = True, - cache_dir: str = None, - overwrite_cache: bool = False, - whitelist: str = None, - lm: bool = False, - post_process: bool = True, - ): - assert input_case in ["lower_cased", "cased"] - - self.post_processor = None - - if lang == "en": - from nemo_text_processing.text_normalization.en.verbalizers.post_processing import PostProcessingFst - from nemo_text_processing.text_normalization.en.verbalizers.verbalize_final import VerbalizeFinalFst - - if post_process: - self.post_processor = PostProcessingFst(cache_dir=cache_dir, overwrite_cache=overwrite_cache) - - if deterministic: - from nemo_text_processing.text_normalization.en.taggers.tokenize_and_classify import ClassifyFst - else: - if lm: - from nemo_text_processing.text_normalization.en.taggers.tokenize_and_classify_lm import ClassifyFst - else: - from nemo_text_processing.text_normalization.en.taggers.tokenize_and_classify_with_audio import ( - ClassifyFst, - ) - elif lang == 'ru': - # Ru TN only support non-deterministic cases and produces multiple normalization options - # use normalize_with_audio.py - from nemo_text_processing.text_normalization.ru.taggers.tokenize_and_classify import ClassifyFst - from nemo_text_processing.text_normalization.ru.verbalizers.verbalize_final import VerbalizeFinalFst - elif lang == 'de': - from nemo_text_processing.text_normalization.de.taggers.tokenize_and_classify import ClassifyFst - from nemo_text_processing.text_normalization.de.verbalizers.verbalize_final import VerbalizeFinalFst - elif lang == 'es': - from nemo_text_processing.text_normalization.es.taggers.tokenize_and_classify import ClassifyFst - from nemo_text_processing.text_normalization.es.verbalizers.verbalize_final import VerbalizeFinalFst - elif lang == 'zh': - from nemo_text_processing.text_normalization.zh.taggers.tokenize_and_classify import ClassifyFst - from nemo_text_processing.text_normalization.zh.verbalizers.verbalize_final import VerbalizeFinalFst - else: - raise NotImplementedError(f"Language {lang} has not been supported yet.") - - self.tagger = ClassifyFst( - input_case=input_case, - deterministic=deterministic, - cache_dir=cache_dir, - overwrite_cache=overwrite_cache, - whitelist=whitelist, - ) - - self.verbalizer = VerbalizeFinalFst( - deterministic=deterministic, cache_dir=cache_dir, overwrite_cache=overwrite_cache - ) - - self.parser = TokenParser() - self.lang = lang - - if NLP_AVAILABLE: - self.processor = MosesProcessor(lang_id=lang) - else: - self.processor = None - print("NeMo NLP is not available. Moses de-tokenization will be skipped.") - - def normalize_list( - self, - texts: List[str], - verbose: bool = False, - punct_pre_process: bool = False, - punct_post_process: bool = False, - batch_size: int = 1, - n_jobs: int = 1, - ): - """ - NeMo text normalizer - - Args: - texts: list of input strings - verbose: whether to print intermediate meta information - punct_pre_process: whether to do punctuation pre-processing - punct_post_process: whether to do punctuation post-processing - n_jobs: the maximum number of concurrently running jobs. If -1 all CPUs are used. If 1 is given, - no parallel computing code is used at all, which is useful for debugging. For n_jobs below -1, - (n_cpus + 1 + n_jobs) are used. Thus for n_jobs = -2, all CPUs but one are used. - batch_size: Number of examples for each process - - Returns converted list input strings - """ - - # to save intermediate results to a file - batch = min(len(texts), batch_size) - - try: - normalized_texts = Parallel(n_jobs=n_jobs)( - delayed(self.process_batch)(texts[i : i + batch], verbose, punct_pre_process, punct_post_process) - for i in range(0, len(texts), batch) - ) - except BaseException as e: - raise e - - normalized_texts = list(itertools.chain(*normalized_texts)) - return normalized_texts - - def process_batch(self, batch, verbose, punct_pre_process, punct_post_process): - """ - Normalizes batch of text sequences - Args: - batch: list of texts - verbose: whether to print intermediate meta information - punct_pre_process: whether to do punctuation pre-processing - punct_post_process: whether to do punctuation post-processing - """ - normalized_lines = [ - self.normalize( - text, verbose=verbose, punct_pre_process=punct_pre_process, punct_post_process=punct_post_process - ) - for text in tqdm(batch) - ] - return normalized_lines - - def _estimate_number_of_permutations_in_nested_dict( - self, token_group: Dict[str, Union[OrderedDict, str, bool]] - ) -> int: - num_perms = 1 - for k, inner in token_group.items(): - if isinstance(inner, dict): - num_perms *= self._estimate_number_of_permutations_in_nested_dict(inner) - num_perms *= factorial(len(token_group)) - return num_perms - - def _split_tokens_to_reduce_number_of_permutations( - self, tokens: List[dict], max_number_of_permutations_per_split: int = 729 - ) -> List[List[dict]]: - """ - Splits a sequence of tokens in a smaller sequences of tokens in a way that maximum number of composite - tokens permutations does not exceed ``max_number_of_permutations_per_split``. - - For example, - - .. code-block:: python - tokens = [ - {"tokens": {"date": {"year": "twenty eighteen", "month": "december", "day": "thirty one"}}}, - {"tokens": {"date": {"year": "twenty eighteen", "month": "january", "day": "eight"}}}, - ] - split = normalizer._split_tokens_to_reduce_number_of_permutations( - tokens, max_number_of_permutations_per_split=6 - ) - assert split == [ - [{"tokens": {"date": {"year": "twenty eighteen", "month": "december", "day": "thirty one"}}}], - [{"tokens": {"date": {"year": "twenty eighteen", "month": "january", "day": "eight"}}}], - ] - - Date tokens contain 3 items each which gives 6 permutations for every date. Since there are 2 dates, total - number of permutations would be ``6 * 6 == 36``. Parameter ``max_number_of_permutations_per_split`` equals 6, - so input sequence of tokens is split into 2 smaller sequences. - - Args: - tokens (:obj:`List[dict]`): a list of dictionaries, possibly nested. - max_number_of_permutations_per_split (:obj:`int`, `optional`, defaults to :obj:`243`): a maximum number - of permutations which can be generated from input sequence of tokens. - - Returns: - :obj:`List[List[dict]]`: a list of smaller sequences of tokens resulting from ``tokens`` split. - """ - splits = [] - prev_end_of_split = 0 - current_number_of_permutations = 1 - for i, token_group in enumerate(tokens): - n = self._estimate_number_of_permutations_in_nested_dict(token_group) - if n * current_number_of_permutations > max_number_of_permutations_per_split: - splits.append(tokens[prev_end_of_split:i]) - prev_end_of_split = i - current_number_of_permutations = 1 - if n > max_number_of_permutations_per_split: - raise ValueError( - f"Could not split token list with respect to condition that every split can generate number of " - f"permutations less or equal to " - f"`max_number_of_permutations_per_split={max_number_of_permutations_per_split}`. " - f"There is an unsplittable token group that generates more than " - f"{max_number_of_permutations_per_split} permutations. Try to increase " - f"`max_number_of_permutations_per_split` parameter." - ) - current_number_of_permutations *= n - splits.append(tokens[prev_end_of_split:]) - assert sum([len(s) for s in splits]) == len(tokens) - return splits - - def normalize( - self, text: str, verbose: bool = False, punct_pre_process: bool = False, punct_post_process: bool = False - ) -> str: - """ - Main function. Normalizes tokens from written to spoken form - e.g. 12 kg -> twelve kilograms - - Args: - text: string that may include semiotic classes - verbose: whether to print intermediate meta information - punct_pre_process: whether to perform punctuation pre-processing, for example, [25] -> [ 25 ] - punct_post_process: whether to normalize punctuation - - Returns: spoken form - """ - if len(text.split()) > 500: - print( - "WARNING! Your input is too long and could take a long time to normalize." - "Use split_text_into_sentences() to make the input shorter and then call normalize_list()." - ) - - original_text = text - if punct_pre_process: - text = pre_process(text) - text = text.strip() - if not text: - if verbose: - print(text) - return text - text = pynini.escape(text) - tagged_lattice = self.find_tags(text) - tagged_text = Normalizer.select_tag(tagged_lattice) - if verbose: - print(tagged_text) - self.parser(tagged_text) - tokens = self.parser.parse() - split_tokens = self._split_tokens_to_reduce_number_of_permutations(tokens) - output = "" - for s in split_tokens: - tags_reordered = self.generate_permutations(s) - verbalizer_lattice = None - for tagged_text in tags_reordered: - tagged_text = pynini.escape(tagged_text) - - verbalizer_lattice = self.find_verbalizer(tagged_text) - if verbalizer_lattice.num_states() != 0: - break - if verbalizer_lattice is None: - raise ValueError(f"No permutations were generated from tokens {s}") - output += ' ' + Normalizer.select_verbalizer(verbalizer_lattice) - output = SPACE_DUP.sub(' ', output[1:]) - - if self.lang == "en" and hasattr(self, 'post_processor'): - output = self.post_process(output) - - if punct_post_process: - # do post-processing based on Moses detokenizer - if self.processor: - output = self.processor.moses_detokenizer.detokenize([output], unescape=False) - output = post_process_punct(input=original_text, normalized_text=output) - else: - print("NEMO_NLP collection is not available: skipping punctuation post_processing") - - return output - - def split_text_into_sentences(self, text: str) -> List[str]: - """ - Split text into sentences. - - Args: - text: text - - Returns list of sentences - """ - lower_case_unicode = '' - upper_case_unicode = '' - if self.lang == "ru": - lower_case_unicode = '\u0430-\u04FF' - upper_case_unicode = '\u0410-\u042F' - - # Read and split transcript by utterance (roughly, sentences) - split_pattern = rf"(? List[str]: - """ - Creates reorderings of dictionary elements and serializes as strings - - Args: - d: (nested) dictionary of key value pairs - - Return permutations of different string serializations of key value pairs - """ - l = [] - if PRESERVE_ORDER_KEY in d.keys(): - d_permutations = [d.items()] - else: - d_permutations = itertools.permutations(d.items()) - for perm in d_permutations: - subl = [""] - for k, v in perm: - if isinstance(v, str): - subl = ["".join(x) for x in itertools.product(subl, [f"{k}: \"{v}\" "])] - elif isinstance(v, OrderedDict): - rec = self._permute(v) - subl = ["".join(x) for x in itertools.product(subl, [f" {k} {{ "], rec, [f" }} "])] - elif isinstance(v, bool): - subl = ["".join(x) for x in itertools.product(subl, [f"{k}: true "])] - else: - raise ValueError() - l.extend(subl) - return l - - def generate_permutations(self, tokens: List[dict]): - """ - Generates permutations of string serializations of list of dictionaries - - Args: - tokens: list of dictionaries - - Returns string serialization of list of dictionaries - """ - - def _helper(prefix: str, token_list: List[dict], idx: int): - """ - Generates permutations of string serializations of given dictionary - - Args: - token_list: list of dictionaries - prefix: prefix string - idx: index of next dictionary - - Returns string serialization of dictionary - """ - if idx == len(token_list): - yield prefix - return - token_options = self._permute(token_list[idx]) - for token_option in token_options: - yield from _helper(prefix + token_option, token_list, idx + 1) - - return _helper("", tokens, 0) - - def find_tags(self, text: str) -> 'pynini.FstLike': - """ - Given text use tagger Fst to tag text - - Args: - text: sentence - - Returns: tagged lattice - """ - lattice = text @ self.tagger.fst - return lattice - - @staticmethod - def select_tag(lattice: 'pynini.FstLike') -> str: - """ - Given tagged lattice return shortest path - - Args: - lattice: pynini.FstLike tag lattice - - Returns: shortest path - """ - tagged_text = pynini.shortestpath(lattice, nshortest=1, unique=True).string() - return tagged_text - - def find_verbalizer(self, tagged_text: str) -> 'pynini.FstLike': - """ - Given tagged text creates verbalization lattice - This is context-independent. - - Args: - tagged_text: input text - - Returns: verbalized lattice - """ - lattice = tagged_text @ self.verbalizer.fst - return lattice - - @staticmethod - def select_verbalizer(lattice: 'pynini.FstLike') -> str: - """ - Given verbalized lattice return shortest path - - Args: - lattice: verbalization lattice - - Returns: shortest path - """ - output = pynini.shortestpath(lattice, nshortest=1, unique=True).string() - # lattice = output @ self.verbalizer.punct_graph - # output = pynini.shortestpath(lattice, nshortest=1, unique=True).string() - return output - - def post_process(self, normalized_text: 'pynini.FstLike') -> str: - """ - Runs post-processing graph on normalized text - - Args: - normalized_text: normalized text - - Returns: shortest path - """ - normalized_text = normalized_text.strip() - if not normalized_text: - return normalized_text - normalized_text = pynini.escape(normalized_text) - - if self.post_processor is not None: - normalized_text = top_rewrite(normalized_text, self.post_processor.fst) - return normalized_text - - -def parse_args(): - parser = ArgumentParser() - input = parser.add_mutually_exclusive_group() - input.add_argument("--text", dest="input_string", help="input string", type=str) - input.add_argument("--input_file", dest="input_file", help="input file path", type=str) - parser.add_argument('--output_file', dest="output_file", help="output file path", type=str) - parser.add_argument("--language", help="language", choices=["en", "de", "es", "zh"], default="en", type=str) - parser.add_argument( - "--input_case", help="input capitalization", choices=["lower_cased", "cased"], default="cased", type=str - ) - parser.add_argument("--verbose", help="print info for debugging", action='store_true') - parser.add_argument( - "--punct_post_process", - help="set to True to enable punctuation post processing to match input.", - action="store_true", - ) - parser.add_argument( - "--punct_pre_process", help="set to True to enable punctuation pre processing", action="store_true" - ) - parser.add_argument("--overwrite_cache", help="set to True to re-create .far grammar files", action="store_true") - parser.add_argument("--whitelist", help="path to a file with with whitelist", default=None, type=str) - parser.add_argument( - "--cache_dir", - help="path to a dir with .far grammar file. Set to None to avoid using cache", - default=None, - type=str, - ) - return parser.parse_args() - - -if __name__ == "__main__": - start_time = perf_counter() - - args = parse_args() - whitelist = os.path.abspath(args.whitelist) if args.whitelist else None - - if not args.input_string and not args.input_file: - raise ValueError("Either `--text` or `--input_file` required") - - normalizer = Normalizer( - input_case=args.input_case, - cache_dir=args.cache_dir, - overwrite_cache=args.overwrite_cache, - whitelist=whitelist, - lang=args.language, - ) - if args.input_string: - print( - normalizer.normalize( - args.input_string, - verbose=args.verbose, - punct_pre_process=args.punct_pre_process, - punct_post_process=args.punct_post_process, - ) - ) - elif args.input_file: - print("Loading data: " + args.input_file) - data = load_file(args.input_file) - - print("- Data: " + str(len(data)) + " sentences") - normalizer_prediction = normalizer.normalize_list( - data, - verbose=args.verbose, - punct_pre_process=args.punct_pre_process, - punct_post_process=args.punct_post_process, - ) - if args.output_file: - write_file(args.output_file, normalizer_prediction) - print(f"- Normalized. Writing out to {args.output_file}") - else: - print(normalizer_prediction) - - print(f"Execution time: {perf_counter() - start_time:.02f} sec") diff --git a/nemo_text_processing/text_normalization/normalize_with_audio.py b/nemo_text_processing/text_normalization/normalize_with_audio.py deleted file mode 100644 index 27fcaa1e5f87..000000000000 --- a/nemo_text_processing/text_normalization/normalize_with_audio.py +++ /dev/null @@ -1,554 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import json -import os -import time -from argparse import ArgumentParser -from glob import glob -from typing import List, Optional, Tuple - -import pynini -from joblib import Parallel, delayed -from nemo_text_processing.text_normalization.data_loader_utils import post_process_punct, pre_process -from nemo_text_processing.text_normalization.normalize import Normalizer -from pynini.lib import rewrite -from tqdm import tqdm - -try: - from nemo.collections.asr.metrics.wer import word_error_rate - from nemo.collections.asr.models import ASRModel - - ASR_AVAILABLE = True -except (ModuleNotFoundError, ImportError): - ASR_AVAILABLE = False - - -""" -The script provides multiple normalization options and chooses the best one that minimizes CER of the ASR output -(most of the semiotic classes use deterministic=False flag). - -To run this script with a .json manifest file, the manifest file should contain the following fields: - "audio_data" - path to the audio file - "text" - raw text - "pred_text" - ASR model prediction - - See https://github.com/NVIDIA/NeMo/blob/main/examples/asr/transcribe_speech.py on how to add ASR predictions - - When the manifest is ready, run: - python normalize_with_audio.py \ - --audio_data PATH/TO/MANIFEST.JSON \ - --language en - - -To run with a single audio file, specify path to audio and text with: - python normalize_with_audio.py \ - --audio_data PATH/TO/AUDIO.WAV \ - --language en \ - --text raw text OR PATH/TO/.TXT/FILE - --model QuartzNet15x5Base-En \ - --verbose - -To see possible normalization options for a text input without an audio file (could be used for debugging), run: - python python normalize_with_audio.py --text "RAW TEXT" - -Specify `--cache_dir` to generate .far grammars once and re-used them for faster inference -""" - - -class NormalizerWithAudio(Normalizer): - """ - Normalizer class that converts text from written to spoken form. - Useful for TTS preprocessing. - - Args: - input_case: expected input capitalization - lang: language - cache_dir: path to a dir with .far grammar file. Set to None to avoid using cache. - overwrite_cache: set to True to overwrite .far files - whitelist: path to a file with whitelist replacements - post_process: WFST-based post processing, e.g. to remove extra spaces added during TN. - Note: punct_post_process flag in normalize() supports all languages. - """ - - def __init__( - self, - input_case: str, - lang: str = 'en', - cache_dir: str = None, - overwrite_cache: bool = False, - whitelist: str = None, - lm: bool = False, - post_process: bool = True, - ): - - super().__init__( - input_case=input_case, - lang=lang, - deterministic=False, - cache_dir=cache_dir, - overwrite_cache=overwrite_cache, - whitelist=whitelist, - lm=lm, - post_process=post_process, - ) - self.lm = lm - - def normalize(self, text: str, n_tagged: int, punct_post_process: bool = True, verbose: bool = False,) -> str: - """ - Main function. Normalizes tokens from written to spoken form - e.g. 12 kg -> twelve kilograms - - Args: - text: string that may include semiotic classes - n_tagged: number of tagged options to consider, -1 - to get all possible tagged options - punct_post_process: whether to normalize punctuation - verbose: whether to print intermediate meta information - - Returns: - normalized text options (usually there are multiple ways of normalizing a given semiotic class) - """ - - if len(text.split()) > 500: - raise ValueError( - "Your input is too long. Please split up the input into sentences, " - "or strings with fewer than 500 words" - ) - - original_text = text - text = pre_process(text) # to handle [] - - text = text.strip() - if not text: - if verbose: - print(text) - return text - text = pynini.escape(text) - print(text) - - if self.lm: - if self.lang not in ["en"]: - raise ValueError(f"{self.lang} is not supported in LM mode") - - if self.lang == "en": - # this to keep arpabet phonemes in the list of options - if "[" in text and "]" in text: - - lattice = rewrite.rewrite_lattice(text, self.tagger.fst) - else: - try: - lattice = rewrite.rewrite_lattice(text, self.tagger.fst_no_digits) - except pynini.lib.rewrite.Error: - lattice = rewrite.rewrite_lattice(text, self.tagger.fst) - lattice = rewrite.lattice_to_nshortest(lattice, n_tagged) - tagged_texts = [(x[1], float(x[2])) for x in lattice.paths().items()] - tagged_texts.sort(key=lambda x: x[1]) - tagged_texts, weights = list(zip(*tagged_texts)) - else: - tagged_texts = self._get_tagged_text(text, n_tagged) - # non-deterministic Eng normalization uses tagger composed with verbalizer, no permutation in between - if self.lang == "en": - normalized_texts = tagged_texts - normalized_texts = [self.post_process(text) for text in normalized_texts] - else: - normalized_texts = [] - for tagged_text in tagged_texts: - self._verbalize(tagged_text, normalized_texts, verbose=verbose) - - if len(normalized_texts) == 0: - raise ValueError() - - if punct_post_process: - # do post-processing based on Moses detokenizer - if self.processor: - normalized_texts = [self.processor.detokenize([t]) for t in normalized_texts] - normalized_texts = [ - post_process_punct(input=original_text, normalized_text=t) for t in normalized_texts - ] - - if self.lm: - remove_dup = sorted(list(set(zip(normalized_texts, weights))), key=lambda x: x[1]) - normalized_texts, weights = zip(*remove_dup) - return list(normalized_texts), weights - - normalized_texts = set(normalized_texts) - return normalized_texts - - def _get_tagged_text(self, text, n_tagged): - """ - Returns text after tokenize and classify - Args; - text: input text - n_tagged: number of tagged options to consider, -1 - return all possible tagged options - """ - if n_tagged == -1: - if self.lang == "en": - # this to keep arpabet phonemes in the list of options - if "[" in text and "]" in text: - tagged_texts = rewrite.rewrites(text, self.tagger.fst) - else: - try: - tagged_texts = rewrite.rewrites(text, self.tagger.fst_no_digits) - except pynini.lib.rewrite.Error: - tagged_texts = rewrite.rewrites(text, self.tagger.fst) - else: - tagged_texts = rewrite.rewrites(text, self.tagger.fst) - else: - if self.lang == "en": - # this to keep arpabet phonemes in the list of options - if "[" in text and "]" in text: - tagged_texts = rewrite.top_rewrites(text, self.tagger.fst, nshortest=n_tagged) - else: - try: - # try self.tagger graph that produces output without digits - tagged_texts = rewrite.top_rewrites(text, self.tagger.fst_no_digits, nshortest=n_tagged) - except pynini.lib.rewrite.Error: - tagged_texts = rewrite.top_rewrites(text, self.tagger.fst, nshortest=n_tagged) - else: - tagged_texts = rewrite.top_rewrites(text, self.tagger.fst, nshortest=n_tagged) - return tagged_texts - - def _verbalize(self, tagged_text: str, normalized_texts: List[str], verbose: bool = False): - """ - Verbalizes tagged text - - Args: - tagged_text: text with tags - normalized_texts: list of possible normalization options - verbose: if true prints intermediate classification results - """ - - def get_verbalized_text(tagged_text): - return rewrite.rewrites(tagged_text, self.verbalizer.fst) - - self.parser(tagged_text) - tokens = self.parser.parse() - tags_reordered = self.generate_permutations(tokens) - for tagged_text_reordered in tags_reordered: - try: - tagged_text_reordered = pynini.escape(tagged_text_reordered) - normalized_texts.extend(get_verbalized_text(tagged_text_reordered)) - if verbose: - print(tagged_text_reordered) - - except pynini.lib.rewrite.Error: - continue - - def select_best_match( - self, - normalized_texts: List[str], - input_text: str, - pred_text: str, - verbose: bool = False, - remove_punct: bool = False, - cer_threshold: int = 100, - ): - """ - Selects the best normalization option based on the lowest CER - - Args: - normalized_texts: normalized text options - input_text: input text - pred_text: ASR model transcript of the audio file corresponding to the normalized text - verbose: whether to print intermediate meta information - remove_punct: whether to remove punctuation before calculating CER - cer_threshold: if CER for pred_text is above the cer_threshold, no normalization will be performed - - Returns: - normalized text with the lowest CER and CER value - """ - if pred_text == "": - return input_text, cer_threshold - - normalized_texts_cer = calculate_cer(normalized_texts, pred_text, remove_punct) - normalized_texts_cer = sorted(normalized_texts_cer, key=lambda x: x[1]) - normalized_text, cer = normalized_texts_cer[0] - - if cer > cer_threshold: - return input_text, cer - - if verbose: - print('-' * 30) - for option in normalized_texts: - print(option) - print('-' * 30) - return normalized_text, cer - - -def calculate_cer(normalized_texts: List[str], pred_text: str, remove_punct=False) -> List[Tuple[str, float]]: - """ - Calculates character error rate (CER) - - Args: - normalized_texts: normalized text options - pred_text: ASR model output - - Returns: normalized options with corresponding CER - """ - normalized_options = [] - for text in normalized_texts: - text_clean = text.replace('-', ' ').lower() - if remove_punct: - for punct in "!?:;,.-()*+-/<=>@^_": - text_clean = text_clean.replace(punct, "") - cer = round(word_error_rate([pred_text], [text_clean], use_cer=True) * 100, 2) - normalized_options.append((text, cer)) - return normalized_options - - -def get_asr_model(asr_model): - """ - Returns ASR Model - - Args: - asr_model: NeMo ASR model - """ - if os.path.exists(args.model): - asr_model = ASRModel.restore_from(asr_model) - elif args.model in ASRModel.get_available_model_names(): - asr_model = ASRModel.from_pretrained(asr_model) - else: - raise ValueError( - f'Provide path to the pretrained checkpoint or choose from {ASRModel.get_available_model_names()}' - ) - return asr_model - - -def parse_args(): - parser = ArgumentParser() - parser.add_argument("--text", help="input string or path to a .txt file", default=None, type=str) - parser.add_argument( - "--input_case", help="input capitalization", choices=["lower_cased", "cased"], default="cased", type=str - ) - parser.add_argument( - "--language", help="Select target language", choices=["en", "ru", "de", "es"], default="en", type=str - ) - parser.add_argument("--audio_data", default=None, help="path to an audio file or .json manifest") - parser.add_argument( - "--output_filename", - default=None, - help="Path of where to save .json manifest with normalization outputs." - " It will only be saved if --audio_data is a .json manifest.", - type=str, - ) - parser.add_argument( - '--model', type=str, default='QuartzNet15x5Base-En', help='Pre-trained model name or path to model checkpoint' - ) - parser.add_argument( - "--n_tagged", - type=int, - default=30, - help="number of tagged options to consider, -1 - return all possible tagged options", - ) - parser.add_argument("--verbose", help="print info for debugging", action="store_true") - parser.add_argument( - "--no_remove_punct_for_cer", - help="Set to True to NOT remove punctuation before calculating CER", - action="store_true", - ) - parser.add_argument( - "--no_punct_post_process", help="set to True to disable punctuation post processing", action="store_true" - ) - parser.add_argument("--overwrite_cache", help="set to True to re-create .far grammar files", action="store_true") - parser.add_argument("--whitelist", help="path to a file with with whitelist", default=None, type=str) - parser.add_argument( - "--cache_dir", - help="path to a dir with .far grammar file. Set to None to avoid using cache", - default=None, - type=str, - ) - parser.add_argument("--n_jobs", default=-2, type=int, help="The maximum number of concurrently running jobs") - parser.add_argument( - "--lm", action="store_true", help="Set to True for WFST+LM. Only available for English right now." - ) - parser.add_argument( - "--cer_threshold", - default=100, - type=int, - help="if CER for pred_text is above the cer_threshold, no normalization will be performed", - ) - parser.add_argument("--batch_size", default=200, type=int, help="Number of examples for each process") - return parser.parse_args() - - -def _normalize_line( - normalizer: NormalizerWithAudio, n_tagged, verbose, line: str, remove_punct, punct_post_process, cer_threshold -): - line = json.loads(line) - pred_text = line["pred_text"] - - normalized_texts = normalizer.normalize( - text=line["text"], verbose=verbose, n_tagged=n_tagged, punct_post_process=punct_post_process, - ) - - normalized_texts = set(normalized_texts) - normalized_text, cer = normalizer.select_best_match( - normalized_texts=normalized_texts, - input_text=line["text"], - pred_text=pred_text, - verbose=verbose, - remove_punct=remove_punct, - cer_threshold=cer_threshold, - ) - line["nemo_normalized"] = normalized_text - line["CER_nemo_normalized"] = cer - return line - - -def normalize_manifest( - normalizer, - audio_data: str, - n_jobs: int, - n_tagged: int, - remove_punct: bool, - punct_post_process: bool, - batch_size: int, - cer_threshold: int, - output_filename: Optional[str] = None, -): - """ - Args: - args.audio_data: path to .json manifest file. - """ - - def __process_batch(batch_idx: int, batch: List[str], dir_name: str): - """ - Normalizes batch of text sequences - Args: - batch: list of texts - batch_idx: batch index - dir_name: path to output directory to save results - """ - normalized_lines = [ - _normalize_line( - normalizer, - n_tagged, - verbose=False, - line=line, - remove_punct=remove_punct, - punct_post_process=punct_post_process, - cer_threshold=cer_threshold, - ) - for line in tqdm(batch) - ] - - with open(f"{dir_name}/{batch_idx:05}.json", "w") as f_out: - for line in normalized_lines: - f_out.write(json.dumps(line, ensure_ascii=False) + '\n') - - print(f"Batch -- {batch_idx} -- is complete") - - if output_filename is None: - output_filename = audio_data.replace('.json', '_normalized.json') - - with open(audio_data, 'r') as f: - lines = f.readlines() - - print(f'Normalizing {len(lines)} lines of {audio_data}...') - - # to save intermediate results to a file - batch = min(len(lines), batch_size) - - tmp_dir = output_filename.replace(".json", "_parts") - os.makedirs(tmp_dir, exist_ok=True) - - Parallel(n_jobs=n_jobs)( - delayed(__process_batch)(idx, lines[i : i + batch], tmp_dir) - for idx, i in enumerate(range(0, len(lines), batch)) - ) - - # aggregate all intermediate files - with open(output_filename, "w") as f_out: - for batch_f in sorted(glob(f"{tmp_dir}/*.json")): - with open(batch_f, "r") as f_in: - lines = f_in.read() - f_out.write(lines) - - print(f'Normalized version saved at {output_filename}') - - -if __name__ == "__main__": - args = parse_args() - - if not ASR_AVAILABLE and args.audio_data: - raise ValueError("NeMo ASR collection is not installed.") - start = time.time() - args.whitelist = os.path.abspath(args.whitelist) if args.whitelist else None - if args.text is not None: - normalizer = NormalizerWithAudio( - input_case=args.input_case, - lang=args.language, - cache_dir=args.cache_dir, - overwrite_cache=args.overwrite_cache, - whitelist=args.whitelist, - lm=args.lm, - ) - - if os.path.exists(args.text): - with open(args.text, 'r') as f: - args.text = f.read().strip() - normalized_texts = normalizer.normalize( - text=args.text, - verbose=args.verbose, - n_tagged=args.n_tagged, - punct_post_process=not args.no_punct_post_process, - ) - - if not normalizer.lm: - normalized_texts = set(normalized_texts) - if args.audio_data: - asr_model = get_asr_model(args.model) - pred_text = asr_model.transcribe([args.audio_data])[0] - normalized_text, cer = normalizer.select_best_match( - normalized_texts=normalized_texts, - pred_text=pred_text, - input_text=args.text, - verbose=args.verbose, - remove_punct=not args.no_remove_punct_for_cer, - cer_threshold=args.cer_threshold, - ) - print(f"Transcript: {pred_text}") - print(f"Normalized: {normalized_text}") - else: - print("Normalization options:") - for norm_text in normalized_texts: - print(norm_text) - elif not os.path.exists(args.audio_data): - raise ValueError(f"{args.audio_data} not found.") - elif args.audio_data.endswith('.json'): - normalizer = NormalizerWithAudio( - input_case=args.input_case, - lang=args.language, - cache_dir=args.cache_dir, - overwrite_cache=args.overwrite_cache, - whitelist=args.whitelist, - ) - normalize_manifest( - normalizer=normalizer, - audio_data=args.audio_data, - n_jobs=args.n_jobs, - n_tagged=args.n_tagged, - remove_punct=not args.no_remove_punct_for_cer, - punct_post_process=not args.no_punct_post_process, - batch_size=args.batch_size, - cer_threshold=args.cer_threshold, - output_filename=args.output_filename, - ) - else: - raise ValueError( - "Provide either path to .json manifest in '--audio_data' OR " - + "'--audio_data' path to audio file and '--text' path to a text file OR" - "'--text' string text (for debugging without audio)" - ) - print(f'Execution time: {round((time.time() - start)/60, 2)} min.') diff --git a/nemo_text_processing/text_normalization/ru/__init__.py b/nemo_text_processing/text_normalization/ru/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/text_normalization/ru/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/ru/alphabet.py b/nemo_text_processing/text_normalization/ru/alphabet.py deleted file mode 100644 index 3df59f4688c0..000000000000 --- a/nemo_text_processing/text_normalization/ru/alphabet.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# Copyright 2017 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Adapted from https://github.com/google/TextNormalizationCoveringGrammars -# Russian minimally supervised number grammar. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NON_BREAKING_SPACE, NEMO_SPACE -from nemo_text_processing.text_normalization.ru.utils import get_abs_path - -RU_LOWER_ALPHA = "абвгдеёжзийклмнопрстуфхцчшщъыьэюя" -RU_UPPER_ALPHA = RU_LOWER_ALPHA.upper() -RU_LOWER_ALPHA = pynini.union(*RU_LOWER_ALPHA).optimize() -RU_UPPER_ALPHA = pynini.union(*RU_UPPER_ALPHA).optimize() -RU_ALPHA = (RU_LOWER_ALPHA | RU_UPPER_ALPHA).optimize() - -RU_STRESSED_MAP = [ - ("А́", "А'"), - ("Е́", "Е'"), - ("Ё́", "Е'"), - ("И́", "И'"), - ("О́", "О'"), - ("У́", "У'"), - ("Ы́", "Ы'"), - ("Э́", "Э'"), - ("Ю́", "Ю'"), - ("Я́", "Я'"), - ("а́", "а'"), - ("е́", "е'"), - ("ё́", "е'"), - ("и́", "и'"), - ("о́", "о'"), - ("у́", "у'"), - ("ы́", "ы'"), - ("э́", "э'"), - ("ю́", "ю'"), - ("я́", "я'"), - ("ё", "е"), - ("Ё", "Е"), -] - -REWRITE_STRESSED = pynini.closure(pynini.string_map(RU_STRESSED_MAP).optimize() | RU_ALPHA).optimize() -TO_CYRILLIC = pynini.string_file(get_abs_path("data/latin_to_cyrillic.tsv")).optimize() -TO_LATIN = pynini.invert(TO_CYRILLIC).optimize() -RU_ALPHA_OR_SPACE = pynini.union(RU_ALPHA, NEMO_SPACE, NEMO_NON_BREAKING_SPACE).optimize() diff --git a/nemo_text_processing/text_normalization/ru/data/__init__.py b/nemo_text_processing/text_normalization/ru/data/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/ru/data/currency/__init__.py b/nemo_text_processing/text_normalization/ru/data/currency/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/currency/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/ru/data/currency/currency_plural.tsv b/nemo_text_processing/text_normalization/ru/data/currency/currency_plural.tsv deleted file mode 100644 index c1d281590e33..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/currency/currency_plural.tsv +++ /dev/null @@ -1,53 +0,0 @@ -р. рубли 0.1 -р. рубля 0.1 -р. рублей 0.1 -р. рублям 0.1 -р. рублями 0.1 -р. рублях 0.1 -руб. рубли -0.1 -руб. рубля -0.1 -руб. рублей -0.1 -руб. рублям -0.1 -руб. рублями -0.1 -руб. рублях -0.1 -₽ рубли 0.1 -₽ рубля 0.1 -₽ рублей 0.1 -₽ рублям 0.1 -₽ рублями 0.1 -₽ рублях 0.1 -RUB рубли 0.1 -RUB рублей 0.1 -RUB рубля 0.1 -RUB рублям 0.1 -RUB рублями 0.1 -RUB рублях 0.1 -$ доллары 0.1 -$ долларов 0.1 -$ доллара 0.1 -$ долларам 0.1 -$ доллары 0.1 -$ долларами 0.1 -USD доллары -0.1 -USD долларов -0.1 -USD долларам -0.1 -USD долларах -0.1 -USD долларами -0.1 -USD американские доллары 0.1 -USD американских долларов 0.1 -USD американским долларам 0.1 -USD американскими долларами 0.1 -USD американских долларах 0.1 -долл. США долларов сэ ш а 0.1 -долл. США долларов с ш а 0.1 -долл. США долларов сша -0.1 -долл. США доллара сша -0.1 -£ фунта 0.1 -£ фунтов 0.1 -€ евро 0.1 -€ евро 0.1 -коп. копейки -коп. копеек -коп. копейкам -коп. копейками -коп. копейках \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/ru/data/currency/currency_singular.tsv b/nemo_text_processing/text_normalization/ru/data/currency/currency_singular.tsv deleted file mode 100644 index de7d1c7ebed0..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/currency/currency_singular.tsv +++ /dev/null @@ -1,48 +0,0 @@ -р. рубль 1 -р. рубля 1 -р. рублю 1 -р. рублем 1 -р. рубле 1 -руб. рубль -1 -руб. рубля -1 -руб. рублю -1 -руб. рублем -1 -руб. рубле -1 -₽ рубль 1 -₽ рубля 1 -₽ рублю 1 -₽ рублем 1 -₽ рубле 1 -RUB рубль 1 -RUB рубль 1 -RUB рубля 1 -RUB рублю 1 -RUB рублем 1 -RUB рубле 1 -USD доллар -1 -USD доллара -1 -USD доллару -1 -USD долларом -1 -USD долларе -1 -USD американский доллар 1 -USD американского доллара 1 -USD американскому доллару 1 -USD американским долларом 1 -USD американском долларе 1 -$ доллар 1 -$ доллар сэ ш а -$ доллар с ш а -$ доллар 1 -$ доллара 1 -$ доллару 1 -$ долларом 1 -$ долларе 1 -£ фунт -€ евро -коп. копейка -коп. копейки -коп. копейке -коп. копейку -коп. копейкой -коп. копейкою -коп. копейке \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/ru/data/electronic/__init__.py b/nemo_text_processing/text_normalization/ru/data/electronic/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/electronic/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/ru/data/electronic/domain.tsv b/nemo_text_processing/text_normalization/ru/data/electronic/domain.tsv deleted file mode 100644 index c208e05595b8..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/electronic/domain.tsv +++ /dev/null @@ -1,9 +0,0 @@ -com ком -uk -fr -net нет -br -in -ru ру -de -it \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/ru/data/electronic/server_name.tsv b/nemo_text_processing/text_normalization/ru/data/electronic/server_name.tsv deleted file mode 100644 index 5d885db1c955..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/electronic/server_name.tsv +++ /dev/null @@ -1,17 +0,0 @@ -gmail джи мейл -n vidia н видия -n vidia эн видия -n vidia энвидия -n vidia нвидия -outlook аутлук -hotmail хот мейл -yahoo яху -aol -gmx -msn -live лайв -yandex яндекс -orange орандж -wanadoo ванаду -web веб -comcast комкаст \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/ru/data/electronic/symbols.tsv b/nemo_text_processing/text_normalization/ru/data/electronic/symbols.tsv deleted file mode 100644 index e772ad912c73..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/electronic/symbols.tsv +++ /dev/null @@ -1,28 +0,0 @@ -. точка -- тире -- дефис -- минус -_ нижнее подчеркивание -_ знак подчеркивания -! восклицательный знак -# решетка -$ доллар -% процент -& энд -& амперсанд -' кавычка -* звездочка -* знак умножения -+ плюс -/ слэш -/ косая черта -\ бэк слэш -\ обратная косая черта -= равно -? вопросительный знак -^ знак вставки -` кавычка -{ открытая фигурная скобка -| вертикальный слэш -} закрытая фигурная скобка -~ тильда \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/ru/data/latin_to_cyrillic.tsv b/nemo_text_processing/text_normalization/ru/data/latin_to_cyrillic.tsv deleted file mode 100644 index 8e0c8f569955..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/latin_to_cyrillic.tsv +++ /dev/null @@ -1,38 +0,0 @@ -a эй -b би -b бэ -c си -d ди -e и -f эф -f ф -h эйч -h х -i ай -g джи -j джей -k кей -k к -l л -l эл -m эм -n н -n эн -o оу -o о -p пи -q кью -r а -r ар -r р -s эс -t ти -u ю -v ви -w дабл ю -w дабл в -x экс -y уай -z з -z зед -z зи \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/ru/data/measurements.tsv b/nemo_text_processing/text_normalization/ru/data/measurements.tsv deleted file mode 100644 index a48079dd2268..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/measurements.tsv +++ /dev/null @@ -1,321 +0,0 @@ -°F Фаренгейт -°F градусов Фаренгейта -°F градусов по Фаренгейту -°F фаренгейт -°F градусов фаренгейта -°F градусов по фаренгейту -°C цельсия -°C градусов цельсия -°C градусов по цельсию -°C Цельсия -°C градусов цельсия -°C градусов по цельсию -° градус -° градуса -° градусу -° градусом -° градусе -° градусы -° градусов -° градусам -° градусы -° градусами -° градусах -рад радиан -рад радиана -рад радиану -рад радианом -рад радиане -рад радианы -рад радианов -рад радианам -рад радианами -рад радианах -км километр -км километры -км километра -км километров -км километру -км километрам -км километром -км километрами -км километре -км километрах -м метр -м метра -м метру -м метра -м метром -м метре -м метры -м метров -м метрам -м метров -м метрами -м метрах -см сантиметр -см сантиметры -см сантиметра -см сантиметров -см сантиметру -см сантиметрам -см сантиметром -см сантиметрами -см сантиметре -см сантиметрах -мм миллиметр -мм миллиметры -мм миллиметра -мм миллиметров -мм миллиметру -мм миллиметрам -мм миллиметр -мм миллиметром -мм миллиметрами -мм миллиметре -мм миллиметрах -га гектар -га гектары -га гектара -га гектаров -га гектару -га гектарам -га гектаром -га гектарами -га гектаре -га гектарах -м² квадратный метр -0.11 -м² квадратные метры -0.11 -м² квадратного метра -0.11 -м² квадратных метров -0.11 -м² квадратному метру -0.11 -м² квадратным метрам -0.11 -м² квадратные метры -0.11 -м² квадратным метром -0.11 -м² квадратными метрами -0.11 -м² квадратном метре -0.11 -м² квадратных метрах -0.11 -кв. м. квадратный метр -0.1 -кв. м. квадратные метры -0.1 -кв. м. квадратного метра -0.1 -кв. м. квадратных метров -0.1 -кв. м. квадратному метру -0.1 -кв. м. квадратным метрам -0.1 -кв. м. квадратные метры -0.1 -кв. м. квадратным метром -0.1 -кв. м. квадратными метрами -0.1 -кв. м. квадратном метре -0.1 -кв.м. квадратных метрах -0.1 -кв.м. квадратный метр -0.1 -кв.м. квадратные метры -0.1 -кв.м. квадратного метра -0.1 -кв.м. квадратных метров -0.1 -кв.м. квадратному метру -0.1 -кв.м. квадратным метрам -0.1 -кв.м. квадратные метры -0.1 -кв.м. квадратным метром -0.1 -кв.м. квадратными метрами -0.1 -кв.м. квадратном метре -0.1 -кв.м. квадратных метрах -0.1 -м2 квадратный метр 0.1 -м2 квадратные метры 0.1 -м2 квадратного метра 0.1 -м2 квадратных метров 0.1 -м2 квадратному метру 0.1 -м2 квадратным метрам 0.1 -м2 квадратные метры 0.1 -м2 квадратным метром 0.1 -м2 квадратными метрами 0.1 -м2 квадратном метре 0.1 -м2 квадратных метрах 0.1 -₽ рублей 0.1 -₽ рубля 0.1 -₽ рубль 0.1 -RUB рублей 0.1 -RUB рубля 0.1 -RUB рубль 0.1 -pуб. рублей -0.1 -pуб. рубля -0.1 -pуб. рубль -0.1 -pуб рублей 0.1 -pуб рубля 0.1 -pуб рубль 0.1 -км² квадратный километр -0.1 -км2 квадратный километр 0.1 -% процент -% проценты -% процента -% процентов -% проценту -% процентам -% процентом -% процентами -% проценте -% процентах -кВт киловатт -л. с. лошадиная сила -л. с. лошадиные силы -л. с. лошадиной силы -л. с. лошадиных сил -л. с. лошадиной силе -л. с. лошадиным силам -л. с. лошадиную силу -л. с. лошадиные силы -л. с. лошадиной силой -л. с. лошадиной силою -л. с. лошадиными силами -л. с. лошадиных силах -мг миллиграм -кг килограм -кг килограма -кг килограму -кг килограм -кг килограмом -кг килограме -кг килограмы -кг килограмов -кг килограмам -кг килограмы -кг килограмами -кг килограмах -л литр -л литры -л литра -л литров -л литру -л литрам -л литры -л литром -л литрами -л литре -л литрах -л литре -кл килолитр -гл гектолитр -Мл мегалитр -Гл гигалитр -мл миллиитр -ч час -сек. секунд -0.1 -сек. секунда -0.1 -сек. секунды -0.1 -сек. секунды -0.1 -сек. секунд -0.1 -сек. секунде -0.1 -сек. секундам -0.1 -сек. секунду -0.1 -сек. секунды -0.1 -сек. секундой -0.1 -сек. секундами -0.1 -сек. секунде -0.1 -сек. секундах -0.1 -сек секунд 0.1 -сек секунда 0.1 -сек секунды 0.1 -сек секунды 0.1 -сек секунд 0.1 -сек секунде 0.1 -сек секундам 0.1 -сек секунду 0.1 -сек секунды 0.1 -сек секундой 0.1 -сек секундами 0.1 -сек секунде 0.1 -сек секундах 0.1 -с секунд 0.1 -с секунда 0.1 -с секунды 0.1 -с секунды 0.1 -с секунд 0.1 -с секунде 0.1 -с секундам 0.1 -с секунду 0.1 -с секунды 0.1 -с секундой 0.1 -с секундами 0.1 -с секунде 0.1 -с секундах 0.1 -дас декасекунда -кс килосекунда -Мс мегасекунда -мс миллисекунда -нм нанометр -нм нанометра -нм нанометров -нм нанометры -А ампер -К кельвин -кд кандела -мА миллиампер -кВт⋅ч киловатт час -м³ кубический метр -0.1 -м³ кубический метров -0.1 -м3 кубический метр 0.1 -м3 кубический метров 0.1 -м³ кубометр -0.1 -м³ кубометров -0.1 -км/ч километров в час -км/ч километра в час -В вольт -гВ гектовольт -кВ киловольт -МВ мегавольт -мВ милливольт -мВт мегаватт -ТВт тераватт -мкм микрометр -г грамм -г граммов -дБ децибел -Б байт -Кбайт килобайт -Мбайт мегабайт -Пбайт петабайт -Тбайт терабайт 0.1 -ТБ терабайт -0.1 -Гбайт гигабайт 0.1 -Гб гигабайт -0.1 -Гц герц -даГц декагерц -дГц децигерц -гГц гектогерц -сГц сантигерц -кГц килогерц -мГц миллигерц -МГц мегагерц -мкГц микрогерц -ГГц гигагерц -нГц наногерц -ТГц терагерц -пГц пикогерц -ПГц петагерц -фГц фемтогерц -ЭГц эксагерц -аГц аттогерц -ЗГц зеттагерц -зГц зептогерц -ИГц иоттагерц -иГц иоктогерц -кбит килобит -Мбит мегабит -Гбит гигабит -Тбит терабит -бит/с бит в секунду -кбит/с килобит в секунду -Мбит/с мегабит в секунду -Гбит/с гигабит в секунду -нс наносекунда -нс наносекунды -нс наносекунд -мм² квадратный миллиметр -0.1 -мм² квадратных миллиметров -0.1 -мм² квадратных миллиметра -0.1 -мм2 квадратный миллиметр 0.1 -мм2 квадратных миллимета 0.1 -мм2 квадратных миллиметров 0.1 -см² квадратный сантиметр -0.1 -см² квадратных сантимета -0.1 -см² квадратных сантиметров -0.1 -см2 квадратный сантиметр 0.1 -см2 квадратных сантиметра 0.1 -см2 квадратных сантиметров 0.1 diff --git a/nemo_text_processing/text_normalization/ru/data/months/__init__.py b/nemo_text_processing/text_normalization/ru/data/months/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/months/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/ru/data/months/abbr.tsv b/nemo_text_processing/text_normalization/ru/data/months/abbr.tsv deleted file mode 100644 index cd51fe389b09..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/months/abbr.tsv +++ /dev/null @@ -1,12 +0,0 @@ -янв январь -фев февраль -мар март -апр апрель -май май -июнь июнь -июль июль -авг август -сент сентябрь -окт октябрь -нояб ноябрь -дек декабрь \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/ru/data/months/abbr_to_name.tsv b/nemo_text_processing/text_normalization/ru/data/months/abbr_to_name.tsv deleted file mode 100644 index 7a4c61b21582..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/months/abbr_to_name.tsv +++ /dev/null @@ -1,62 +0,0 @@ -янв январь 0.1 -янв января -0.1 -янв январю 0.1 -янв январем 0.1 -янв январе 0.1 -фев февраль 0.1 -фев февраль 0.1 -фев февраля -0.1 -фев февралю 0.1 -фев февралем 0.1 -фев феврале 0.1 -мар март 0.1 -мар марта -0.1 -мар марту 0.1 -мар мартом 0.1 -мар марте 0.1 -апр апрель 0.1 -апр апреля -0.1 -апр апрелю 0.1 -апр апрелем 0.1 -апр апреле 0.1 -май май 0.1 -май мая -0.1 -май маю 0.1 -май маем 0.1 -май мае 0.1 -июнь июнь 0.1 -июнь июня -0.1 -июнь июню 0.1 -июнь июнем 0.1 -июнь июне 0.1 -июль июль 0.1 -июль июля -0.1 -июль июлю 0.1 -июль июлем 0.1 -июль июле 0.1 -авг август 0.1 -авг августа -0.1 -авг августу 0.1 -авг август 0.1 -авг августом 0.1 -авг августе 0.1 -сент сентябрь 0.1 -сент сентября -0.1 -сент сентябрю 0.1 -сент сентябрем 0.1 -сент сентябре 0.1 -окт октябрь 0.1 -окт октября -0.1 -окт октябрю 0.1 -окт октябрем 0.1 -окт октябре 0.1 -нояб ноябрь 0.1 -нояб ноября -0.1 -нояб ноябрю 0.1 -нояб ноябрем 0.1 -нояб ноябре 0.1 -дек декабрь 0.1 -дек декабря -0.1 -дек декабрю 0.1 -дек декабрем 0.1 -дек декабре 0.1 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/ru/data/months/numbers.tsv b/nemo_text_processing/text_normalization/ru/data/months/numbers.tsv deleted file mode 100644 index 16256a59d9f6..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/months/numbers.tsv +++ /dev/null @@ -1,21 +0,0 @@ -1 янв -2 фев -3 мар -4 апр -5 май -6 июнь -7 июль -8 авг -9 сент -10 окт -11 нояб -12 дек -01 янв -02 фев -03 мар -04 апр -05 май -06 июнь -07 июль -08 авг -09 сент \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/ru/data/numbers/1_cardinals_nominative.tsv b/nemo_text_processing/text_normalization/ru/data/numbers/1_cardinals_nominative.tsv deleted file mode 100644 index 58c543af2a75..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/numbers/1_cardinals_nominative.tsv +++ /dev/null @@ -1,50 +0,0 @@ -0 ноль -1 один -1 одна -1 одно -1 одни -2 два -2 две -3 три -4 четыре -5 пять -6 шесть -7 семь -8 восемь -9 девять -10 десять -11 одиннадцать -12 двенадцать -13 тринадцать -14 четырнадцать -15 пятнадцать -16 шестнадцать -17 семнадцать -18 восемнадцать -19 девятнадцать -20 двадцать -30 тридцать -40 сорок -50 пятьдесят -60 шестьдесят -70 семьдесят -80 восемьдесят -90 девяносто -100 сто -200 двести -300 триста -400 четыреста -500 пятьсот -600 шестьсот -700 семьсот -800 восемьсот -900 девятьсот -1000 тысяч -1000 тысяча -1000 тысячи -1000000 миллион -1000000 миллиона -1000000 миллионов -1000000000 миллиард -1000000000 миллиарда -1000000000 миллиардов \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/ru/data/numbers/2_cardinals_genitive.tsv b/nemo_text_processing/text_normalization/ru/data/numbers/2_cardinals_genitive.tsv deleted file mode 100644 index d46d6a10b01a..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/numbers/2_cardinals_genitive.tsv +++ /dev/null @@ -1,45 +0,0 @@ -0 ноля -1 одного -1 одной -1 одних -2 двух -3 трех -4 четырех -5 пяти -6 шести -7 семи -8 восьми -9 девяти -10 десяти -11 одиннадцати -12 двенадцати -13 тринадцати -14 четырнадцати -15 пятнадцати -16 шестнадцати -17 семнадцати -18 восемнадцати -19 девятнадцати -20 двадцати -30 тридцати -40 сорока -50 пятидесяти -60 шестидесяти -70 семидесяти -80 восьмидесяти -90 девяноста -100 ста -200 двухсот -300 трехсот -400 четырехсот -500 пятисот -600 шестисот -700 семисот -800 восьмисот -900 девятисот -1000 тысячи -1000 тысяч -1000000 миллиона -1000000 миллионов -1000000000 миллиарда -1000000000 миллиардов diff --git a/nemo_text_processing/text_normalization/ru/data/numbers/3_cardinals_dative.tsv b/nemo_text_processing/text_normalization/ru/data/numbers/3_cardinals_dative.tsv deleted file mode 100644 index 85d335fdeb47..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/numbers/3_cardinals_dative.tsv +++ /dev/null @@ -1,45 +0,0 @@ -0 нолем -1 одним -1 одному -1 одной -2 двум -3 трем -4 четырем -5 пяти -6 шести -7 семи -8 восьми -9 девяти -10 десяти -11 одиннадцати -12 двенадцати -13 тринадцати -14 четырнадцати -15 пятнадцати -16 шестнадцати -17 семнадцати -18 восемнадцати -19 девятнадцати -20 двадцати -30 тридцати -40 сорока -50 пятидесяти -60 шестидесяти -70 семидесяти -80 восьмидесяти -90 девяноста -100 ста -200 двумстам -300 тремстам -400 четыремстам -500 пятистам -600 шестистам -700 семистам -800 восьмистам -900 девятистам -1000 тысяче -1000 тысячам -1000000 миллиону -1000000 миллионам -1000000000 миллиарду -1000000000 миллиардам diff --git a/nemo_text_processing/text_normalization/ru/data/numbers/4_cardinals_accusative.tsv b/nemo_text_processing/text_normalization/ru/data/numbers/4_cardinals_accusative.tsv deleted file mode 100644 index 51617a408422..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/numbers/4_cardinals_accusative.tsv +++ /dev/null @@ -1,48 +0,0 @@ -0 ноль -1 один -1 одного -1 одни -1 одно -1 одну -2 два -2 две -3 трех -4 четырех -5 пять -6 шесть -7 семь -8 восемь -9 девять -10 десять -11 одиннадцать -12 двенадцать -13 тринадцать -14 четырнадцать -15 пятнадцать -16 шестнадцать -17 семнадцать -18 восемнадцать -19 девятнадцать -20 двадцать -30 тридцать -40 сорок -50 пятьдесят -60 шестьдесят -70 семьдесят -80 восемьдесят -90 девяносто -100 сто -200 двести -300 триста -400 четыреста -500 пятьсот -600 шестьсот -700 семьсот -800 восемьсот -900 девятьсот -1000 тысячи -1000 тысячу -1000000 миллион -1000000 миллионы -1000000000 миллиард -1000000000 миллиарды \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/ru/data/numbers/5_cardinals_instrumental.tsv b/nemo_text_processing/text_normalization/ru/data/numbers/5_cardinals_instrumental.tsv deleted file mode 100644 index a86f304347f0..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/numbers/5_cardinals_instrumental.tsv +++ /dev/null @@ -1,47 +0,0 @@ -0 нолем -1 одним -1 одними -1 одной -1 одною -1 одну -2 двумя -3 тремя -4 четырьмя -5 пятью -6 шестью -7 семью -8 восемью -8 восьмью -9 девятью -10 десятью -11 одиннадцатью -12 двенадцатью -13 тринадцатью -14 четырнадцатью -15 пятнадцатью -16 шестнадцатью -17 семнадцатью -18 восемнадцатью -19 девятнадцатью -20 двадцатью -30 тридцатью -40 сорока -50 пятьюдесятью -60 шестьюдесятью -70 семьюдесятью -80 восьмьюдесятью -90 девяноста -100 ста -200 двумястами -300 тремястами -400 четырьмястами -500 пятьюстами -600 шестьюстами -700 семьюстами -800 восемьюстами -900 девятьюстами -1000000 миллионами -1000000 миллионом -1000000 миллиону -1000000 миллионы -1000000000 миллиардом diff --git a/nemo_text_processing/text_normalization/ru/data/numbers/6_cardinals_prepositional.tsv b/nemo_text_processing/text_normalization/ru/data/numbers/6_cardinals_prepositional.tsv deleted file mode 100644 index 6a10bc16aab8..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/numbers/6_cardinals_prepositional.tsv +++ /dev/null @@ -1,46 +0,0 @@ -0 ноле -0 нуле -1 одной -1 одном -2 двух -3 трех -4 четырех -5 пяти -6 шести -7 семи -8 восьми -9 девяти -10 десяти -11 одиннадцати -12 двенадцати -13 тринадцати -14 четырнадцати -15 пятнадцати -16 шестнадцати -17 семнадцати -18 восемнадцати -19 девятнадцати -20 двадцати -30 тридцати -40 сорока -50 пятидесяти -60 шестидесяти -70 семидесяти -90 девяноста -100 ста -200 двухстах -300 трехстах -400 четырехстах -500 пятистах -600 шестистах -700 семистах -800 восьмистах -900 девятистах -900 девятьсот -900 девятьюстами -1000 тысячах -1000 тысяче -1000000 миллионах -1000000 миллионе -1000000000 миллиардах -1000000000 миллиарде diff --git a/nemo_text_processing/text_normalization/ru/data/numbers/__init__.py b/nemo_text_processing/text_normalization/ru/data/numbers/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/numbers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/ru/data/numbers/cardinals_alternatives.tsv b/nemo_text_processing/text_normalization/ru/data/numbers/cardinals_alternatives.tsv deleted file mode 100644 index 5afd1d6f5465..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/numbers/cardinals_alternatives.tsv +++ /dev/null @@ -1,16 +0,0 @@ -1т одна тысяча -1т одной тысячи -1т одной тысяче -1т одну тысячу -1т одной тысячей -1т одной тысяче -1млн один миллион -1млн одного миллиона -1млн одному миллиону -1млн одним миллионом -1млн одном миллионе -1млрд один миллиард -1млрд одного миллиарда -1млрд одному миллиарду -1млрд одним миллиардом -1млрд одном миллиарде \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/ru/data/numbers/cardinals_nominative_case.tsv b/nemo_text_processing/text_normalization/ru/data/numbers/cardinals_nominative_case.tsv deleted file mode 100644 index 83cfb227e2aa..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/numbers/cardinals_nominative_case.tsv +++ /dev/null @@ -1,37 +0,0 @@ -0 ноль -1 один -2 два -3 три -4 четыре -5 пять -6 шесть -7 семь -8 восемь -9 девять -10 десять -11 одиннадцать -12 двенадцать -13 тринадцать -14 четырнадцать -15 пятнадцать -16 шестнадцать -17 семнадцать -18 восемнадцать -19 девятнадцать -20 двадцать -30 тридцать -40 сорок -50 пятьдесят -60 шестьдесят -70 семьдесят -80 восемьдесят -90 девяносто -100 сто -200 двести -300 триста -400 четыреста -500 пятьсот -600 шестьсот -700 семьсот -800 восемьсот -900 девятьсот \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/ru/data/numbers/decimal_delimiter.tsv b/nemo_text_processing/text_normalization/ru/data/numbers/decimal_delimiter.tsv deleted file mode 100644 index fb796cfa1a73..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/numbers/decimal_delimiter.tsv +++ /dev/null @@ -1,15 +0,0 @@ -@@decimal_delimiter@@ целого 0.1 -@@decimal_delimiter@@ целому 0.1 -@@decimal_delimiter@@ целый 0.1 -@@decimal_delimiter@@ целого 0.1 -@@decimal_delimiter@@ целым 0.1 -@@decimal_delimiter@@ целом 0.1 -@@decimal_delimiter@@ целая -0.1 -@@decimal_delimiter@@ целой 0.1 -@@decimal_delimiter@@ целую 0.1 -@@decimal_delimiter@@ целою 0.1 -@@decimal_delimiter@@ целые -0.1 -@@decimal_delimiter@@ целых -0.1 -@@decimal_delimiter@@ целым 0.1 -@@decimal_delimiter@@ целые 0.1 -@@decimal_delimiter@@ целыми 0.1 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/ru/data/numbers/decimal_endings.tsv b/nemo_text_processing/text_normalization/ru/data/numbers/decimal_endings.tsv deleted file mode 100644 index d2c30e146bee..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/numbers/decimal_endings.tsv +++ /dev/null @@ -1,37 +0,0 @@ -10 десятая -0.1 -10 десятой 0.1 -10 десятой 0.1 -10 десятую 0.1 -10 десятою 0.1 -10 десятой 0.1 -10 десятые 0.1 -10 десятых -0.1 -10 десятым 0.1 -10 десятыми 0.1 -100 сотые -0.1 -100 сотых -0.1 -100 сотым 0.1 -100 сотыми 0.1 -100 сотая -0.1 -100 сотой 0.1 -100 сотую 0.1 -100 сотою 0.1 -1000 тысячные -0.1 -1000 тысячных -0.1 -1000 тысячным 0.1 -1000 тысячными 0.1 -1000 тысячная -0.1 -1000 тысячной 0.1 -1000 тысячную 0.1 -1000 тысячною 0.1 -10000 десятитысячная -0.1 -10000 десятитысячной 0.1 -10000 десятитысячной 0.1 -10000 десятитысячную 0.1 -10000 десятитысячною 0.1 -10000 десятитысячной 0.1 -10000 десятитысячные 0.1 -10000 десятитысячных -0.1 -10000 десятитысячным 0.1 -10000 десятитысячные 0.1 -10000 десятитысячными 0.1 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/ru/data/numbers/digits_nominative_case.tsv b/nemo_text_processing/text_normalization/ru/data/numbers/digits_nominative_case.tsv deleted file mode 100644 index c6663fb395b7..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/numbers/digits_nominative_case.tsv +++ /dev/null @@ -1,10 +0,0 @@ -0 ноль -1 один -2 два -3 три -4 четыре -5 пять -6 шесть -7 семь -8 восемь -9 девять \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/ru/data/numbers/ordinal_endings.tsv b/nemo_text_processing/text_normalization/ru/data/numbers/ordinal_endings.tsv deleted file mode 100644 index 7f37fdcf9525..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/numbers/ordinal_endings.tsv +++ /dev/null @@ -1,40 +0,0 @@ -ая-ая -ого-го -ьего-го -ьего-его -ьей-ей -ьему-ему -ьем-ем -ое-е -ые-е -ье-е -ий-ий -ьими-ими -ьим-им -ьих-их -ьи-и -ий-й -ой-й -ый-й -ыми-ми -ьими-ми -ому-му -ьему-му -ого-ого -ое-ое -ой-ой -ом-ом -ому-ому -ую-ую -ых-х -ьих-х -ые-ые -ый-ый -ыми-ыми -ым-ым -ых-ых -ую-ю -ью-ю -ая-я -ья-я -ом-м diff --git a/nemo_text_processing/text_normalization/ru/data/numbers/ordinals.tsv b/nemo_text_processing/text_normalization/ru/data/numbers/ordinals.tsv deleted file mode 100644 index 32b418b2e344..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/numbers/ordinals.tsv +++ /dev/null @@ -1,752 +0,0 @@ -0 нулевая 0.1 -0 нулевого 0.1 -0 нулевое 0.1 -0 нулевой 0.1 -0 нулевом 0.1 -0 нулевому 0.1 -0 нулевую 0.1 -0 нулевые -0.01 -0 нулевым 0.1 -0 нулевыми 0.1 -0 нулевых 0.1 -1 первая -0.01 -1 первого -0.01 -1 первое -0.01 -1 первой 0.1 -1 первом 0.1 -1 первому 0.1 -1 первую 0.1 -1 первые 0.1 -1 первый -0.01 -1 первым 0.1 -1 первыми 0.1 -1 первых 0.1 -2 вторая -0.01 -2 второго 0.1 -2 второе -0.01 -2 второй -0.01 -2 втором 0.1 -2 второму 0.1 -2 вторую 0.1 -2 вторые 0.1 -2 вторым 0.1 -2 вторыми 0.1 -2 вторых 0.1 -3 третий -3 третье -3 третьего -3 третьей -3 третьем -3 третьему -3 третьи -3 третьим -3 третьим -3 третьими -3 третьих -3 третью -3 третья -4 четвертая -4 четвертого -4 четвертое -4 четвертой -4 четвертом -4 четвертому -4 четвертую -4 четвертые -4 четвертый -4 четвертым -4 четвертым -4 четвертыми -4 четвертых -4 четвертая -4 четвертого -4 четвертое -4 четвертой -4 четвертом -4 четвертому -4 четвертую -4 четвертые -4 четвертый -4 четвертым -4 четвертым -4 четвертыми -4 четвертых -5 пятая -5 пятого -5 пятое -5 пятой -5 пятом -5 пятому -5 пятую -5 пятые -5 пятый -5 пятым -5 пятым -5 пятыми -5 пятых -6 шестая -6 шестого -6 шестое -6 шестой -6 шестом -6 шестому -6 шестую -6 шестые -6 шестым -6 шестым -6 шестыми -6 шестых -7 седьмая -7 седьмого -7 седьмое -7 седьмой -7 седьмом -7 седьмому -7 седьмую -7 седьмые -7 седьмым -7 седьмым -7 седьмыми -7 седьмых -8 восьмая -8 восьмого -8 восьмое -8 восьмой -8 восьмом -8 восьмому -8 восьмую -8 восьмые -8 восьмым -8 восьмым -8 восьмыми -8 восьмых -9 девятая -9 девятого -9 девятое -9 девятой -9 девятом -9 девятому -9 девятую -9 девятые -9 девятый -9 девятым -9 девятым -9 девятыми -9 девятых -10 десятая -10 десятого -10 десятое -10 десятой -10 десятом -10 десятому -10 десятую -10 десятые -10 десятый -10 десятым -10 десятым -10 десятыми -10 десятых -11 одиннадцатая -11 одиннадцатого -11 одиннадцатое -11 одиннадцатой -11 одиннадцатом -11 одиннадцатому -11 одиннадцатую -11 одиннадцатые -11 одиннадцатый -11 одиннадцатым -11 одиннадцатым -11 одиннадцатыми -11 одиннадцатых -12 двенадцатая -12 двенадцатого -12 двенадцатое -12 двенадцатой -12 двенадцатом -12 двенадцатому -12 двенадцатую -12 двенадцатые -12 двенадцатый -12 двенадцатым -12 двенадцатым -12 двенадцатыми -12 двенадцатых -13 тринадцатая -13 тринадцатого -13 тринадцатое -13 тринадцатой -13 тринадцатом -13 тринадцатому -13 тринадцатую -13 тринадцатые -13 тринадцатый -13 тринадцатым -13 тринадцатым -13 тринадцатыми -13 тринадцатых -14 четырнадцатая -14 четырнадцатого -14 четырнадцатое -14 четырнадцатой -14 четырнадцатом -14 четырнадцатому -14 четырнадцатую -14 четырнадцатые -14 четырнадцатый -14 четырнадцатым -14 четырнадцатым -14 четырнадцатыми -14 четырнадцатых -15 пятнадцатая -15 пятнадцатого -15 пятнадцатое -15 пятнадцатой -15 пятнадцатом -15 пятнадцатому -15 пятнадцатую -15 пятнадцатые -15 пятнадцатый -15 пятнадцатым -15 пятнадцатым -15 пятнадцатыми -15 пятнадцатых -16 шестнадцатая -16 шестнадцатого -16 шестнадцатое -16 шестнадцатой -16 шестнадцатом -16 шестнадцатому -16 шестнадцатую -16 шестнадцатые -16 шестнадцатый -16 шестнадцатым -16 шестнадцатым -16 шестнадцатыми -16 шестнадцатых -17 семнадцатая -17 семнадцатого -17 семнадцатое -17 семнадцатой -17 семнадцатом -17 семнадцатому -17 семнадцатую -17 семнадцатые -17 семнадцатый -17 семнадцатым -17 семнадцатым -17 семнадцатыми -17 семнадцатых -18 восемнадцатая -18 восемнадцатого -18 восемнадцатое -18 восемнадцатой -18 восемнадцатом -18 восемнадцатому -18 восемнадцатую -18 восемнадцатые -18 восемнадцатый -18 восемнадцатым -18 восемнадцатым -18 восемнадцатыми -18 восемнадцатых -19 девятнадцатая -19 девятнадцатого -19 девятнадцатое -19 девятнадцатой -19 девятнадцатом -19 девятнадцатому -19 девятнадцатую -19 девятнадцатые -19 девятнадцатый -19 девятнадцатым -19 девятнадцатым -19 девятнадцатыми -19 девятнадцатых -20 двадцатая -20 двадцатого -20 двадцатое -20 двадцатой -20 двадцатом -20 двадцатому -20 двадцатую -20 двадцатые -20 двадцатый -20 двадцатым -20 двадцатым -20 двадцатыми -20 двадцатых -30 тридцатая -30 тридцатого -30 тридцатое -30 тридцатой -30 тридцатом -30 тридцатому -30 тридцатую -30 тридцатые -30 тридцатый -30 тридцатым -30 тридцатым -30 тридцатыми -30 тридцатых -40 сороковая -40 сорокового -40 сороковое -40 сороковой -40 сороковом -40 сороковому -40 сороковую -40 сороковые -40 сороковым -40 сороковым -40 сороковыми -40 сороковых -50 пятидесятая -50 пятидесятого -50 пятидесятое -50 пятидесятой -50 пятидесятом -50 пятидесятому -50 пятидесятую -50 пятидесятые -50 пятидесятый -50 пятидесятым -50 пятидесятым -50 пятидесятыми -50 пятидесятых -60 шестидесятая -60 шестидесятого -60 шестидесятое -60 шестидесятой -60 шестидесятом -60 шестидесятому -60 шестидесятую -60 шестидесятые -60 шестидесятый -60 шестидесятым -60 шестидесятым -60 шестидесятыми -60 шестидесятых -70 семидесятая -70 семидесятого -70 семидесятое -70 семидесятой -70 семидесятом -70 семидесятому -70 семидесятую -70 семидесятые -70 семидесятый -70 семидесятым -70 семидесятым -70 семидесятыми -70 семидесятых -80 восьмидесятая -80 восьмидесятого -80 восьмидесятое -80 восьмидесятой -80 восьмидесятом -80 восьмидесятому -80 восьмидесятую -80 восьмидесятые -80 восьмидесятый -80 восьмидесятым -80 восьмидесятым -80 восьмидесятыми -80 восьмидесятых -90 девяностая -90 девяностого -90 девяностое -90 девяностой -90 девяностом -90 девяностому -90 девяностую -90 девяностые -90 девяностый -90 девяностым -90 девяностым -90 девяностыми -90 девяностых -100 сотая -100 сотого -100 сотое -100 сотой -100 сотом -100 сотому -100 сотую -100 сотые -100 сотый -100 сотым -100 сотым -100 сотыми -100 сотых -200 двухсотая -200 двухсотого -200 двухсотое -200 двухсотой -200 двухсотом -200 двухсотому -200 двухсотую -200 двухсотые -200 двухсотый -200 двухсотым -200 двухсотым -200 двухсотыми -200 двухсотых -300 трехсотая -300 трехсотого -300 трехсотое -300 трехсотой -300 трехсотом -300 трехсотому -300 трехсотую -300 трехсотые -300 трехсотый -300 трехсотым -300 трехсотым -300 трехсотыми -300 трехсотых -400 четырехсотая -400 четырехсотого -400 четырехсотое -400 четырехсотой -400 четырехсотом -400 четырехсотому -400 четырехсотую -400 четырехсотые -400 четырехсотый -400 четырехсотым -400 четырехсотым -400 четырехсотыми -400 четырехсотых -500 пятисотая -500 пятисотого -500 пятисотое -500 пятисотой -500 пятисотом -500 пятисотому -500 пятисотую -500 пятисотые -500 пятисотый -500 пятисотым -500 пятисотым -500 пятисотыми -500 пятисотых -600 шестисотая -600 шестисотого -600 шестисотое -600 шестисотой -600 шестисотом -600 шестисотому -600 шестисотую -600 шестисотые -600 шестисотый -600 шестисотым -600 шестисотым -600 шестисотыми -600 шестисотых -700 семисотая -700 семисотого -700 семисотое -700 семисотой -700 семисотом -700 семисотому -700 семисотую -700 семисотые -700 семисотый -700 семисотым -700 семисотым -700 семисотыми -700 семисотых -800 восьмисотая -800 восьмисотого -800 восьмисотое -800 восьмисотой -800 восьмисотом -800 восьмисотому -800 восьмисотую -800 восьмисотые -800 восьмисотый -800 восьмисотым -800 восьмисотым -800 восьмисотыми -800 восьмисотых -900 девятисотая -900 девятисотого -900 девятисотое -900 девятисотой -900 девятисотом -900 девятисотому -900 девятисотую -900 девятисотые -900 девятисотый -900 девятисотым -900 девятисотым -900 девятисотыми -900 девятисотых -1000 тысячная -1000 тысячного -1000 тысячное -1000 тысячной -1000 тысячном -1000 тысячному -1000 тысячную -1000 тысячные -1000 тысячный -1000 тысячным -1000 тысячным -1000 тысячными -1000 тысячных -1000000 миллионная -1000000 миллионного -1000000 миллионное -1000000 миллионной -1000000 миллионном -1000000 миллионному -1000000 миллионную -1000000 миллионные -1000000 миллионный -1000000 миллионным -1000000 миллионным -1000000 миллионными -1000000 миллионных -1000000000 миллиардная -1000000000 миллиардного -1000000000 миллиардное -1000000000 миллиардной -1000000000 миллиардном -1000000000 миллиардному -1000000000 миллиардную -1000000000 миллиардные -1000000000 миллиардный -1000000000 миллиардным -1000000000 миллиардным -1000000000 миллиардными -1000000000 миллиардных -2000 двухтысячная -2000 двухтысячных -2000 двухтысячные -2000 двухтысячный -2000 двухтысячными -2000 двухтысячному -2000 двухтысячным -2000 двухтысячном -2000 двухтысячной -2000 двухтысячного -2000 двухтысячную -2000 двухтысячное -3000 трехтысячное -3000 трехтысячных -3000 трехтысячного -3000 трехтысячные -3000 трехтысячную -3000 трехтысячная -3000 трехтысячному -3000 трехтысячной -3000 трехтысячном -3000 трехтысячный -3000 трехтысячным -3000 трехтысячными -4000 четырехтысячных -4000 четырехтысячный -4000 четырехтысячное -4000 четырехтысячным -4000 четырехтысячными -4000 четырехтысячные -4000 четырехтысячному -4000 четырехтысячного -4000 четырехтысячной -4000 четырехтысячная -4000 четырехтысячную -4000 четырехтысячном -5000 пятитысячными -5000 пятитысячные -5000 пятитысячному -5000 пятитысячную -5000 пятитысячного -5000 пятитысячной -5000 пятитысячным -5000 пятитысячная -5000 пятитысячное -5000 пятитысячных -5000 пятитысячном -5000 пятитысячный -6000 шеститысячных -6000 шеститысячным -6000 шеститысячная -6000 шеститысячной -6000 шеститысячными -6000 шеститысячные -6000 шеститысячное -6000 шеститысячную -6000 шеститысячный -6000 шеститысячному -6000 шеститысячного -6000 шеститысячном -7000 семитысячную -7000 семитысячная -7000 семитысячном -7000 семитысячным -7000 семитысячного -7000 семитысячной -7000 семитысячный -7000 семитысячное -7000 семитысячных -7000 семитысячными -7000 семитысячные -7000 семитысячному -8000 восьмитысячному -8000 восьмитысячном -8000 восьмитысячную -8000 восьмитысячная -8000 восьмитысячными -8000 восьмитысячный -8000 восьмитысячной -8000 восьмитысячного -8000 восьмитысячное -8000 восьмитысячных -8000 восьмитысячным -8000 восьмитысячные -9000 девятитысячных -9000 девятитысячная -9000 девятитысячное -9000 девятитысячному -9000 девятитысячными -9000 девятитысячного -9000 девятитысячной -9000 девятитысячном -9000 девятитысячный -9000 девятитысячные -9000 девятитысячную -9000 девятитысячным -10000 десятитысячным -10000 десятитысячное -10000 десятитысячных -10000 десятитысячными -10000 десятитысячный -10000 десятитысячная -10000 десятитысячные -10000 десятитысячном -10000 десятитысячного -10000 десятитысячному -10000 десятитысячной -10000 десятитысячную -11000 одиннадцатитысячном -11000 одиннадцатитысячное -11000 одиннадцатитысячными -11000 одиннадцатитысячная -11000 одиннадцатитысячных -11000 одиннадцатитысячной -11000 одиннадцатитысячного -11000 одиннадцатитысячную -11000 одиннадцатитысячные -11000 одиннадцатитысячный -11000 одиннадцатитысячным -11000 одиннадцатитысячному -12000 двенадцатитысячного -12000 двенадцатитысячным -12000 двенадцатитысячной -12000 двенадцатитысячную -12000 двенадцатитысячный -12000 двенадцатитысячном -12000 двенадцатитысячными -12000 двенадцатитысячная -12000 двенадцатитысячному -12000 двенадцатитысячные -12000 двенадцатитысячное -12000 двенадцатитысячных -13000 тринадцатитысячных -13000 тринадцатитысячные -13000 тринадцатитысячной -13000 тринадцатитысячным -13000 тринадцатитысячное -13000 тринадцатитысячного -13000 тринадцатитысячная -13000 тринадцатитысячном -13000 тринадцатитысячными -13000 тринадцатитысячный -13000 тринадцатитысячную -13000 тринадцатитысячному -14000 четырнадцатитысячных -14000 четырнадцатитысячном -14000 четырнадцатитысячным -14000 четырнадцатитысячной -14000 четырнадцатитысячному -14000 четырнадцатитысячная -14000 четырнадцатитысячными -14000 четырнадцатитысячный -14000 четырнадцатитысячные -14000 четырнадцатитысячную -14000 четырнадцатитысячного -14000 четырнадцатитысячное -15000 пятнадцатитысячное -15000 пятнадцатитысячных -15000 пятнадцатитысячный -15000 пятнадцатитысячной -15000 пятнадцатитысячную -15000 пятнадцатитысячном -15000 пятнадцатитысячным -15000 пятнадцатитысячными -15000 пятнадцатитысячные -15000 пятнадцатитысячному -15000 пятнадцатитысячная -15000 пятнадцатитысячного -16000 шестнадцатитысячному -16000 шестнадцатитысячным -16000 шестнадцатитысячные -16000 шестнадцатитысячное -16000 шестнадцатитысячный -16000 шестнадцатитысячными -16000 шестнадцатитысячном -16000 шестнадцатитысячного -16000 шестнадцатитысячной -16000 шестнадцатитысячных -16000 шестнадцатитысячная -16000 шестнадцатитысячную -17000 семнадцатитысячного -17000 семнадцатитысячный -17000 семнадцатитысячное -17000 семнадцатитысячному -17000 семнадцатитысячную -17000 семнадцатитысячная -17000 семнадцатитысячной -17000 семнадцатитысячными -17000 семнадцатитысячным -17000 семнадцатитысячном -17000 семнадцатитысячные -17000 семнадцатитысячных -18000 восемнадцатитысячные -18000 восемнадцатитысячным -18000 восемнадцатитысячный -18000 восемнадцатитысячного -18000 восемнадцатитысячному -18000 восемнадцатитысячными -18000 восемнадцатитысячное -18000 восемнадцатитысячном -18000 восемнадцатитысячных -18000 восемнадцатитысячной -18000 восемнадцатитысячную -18000 восемнадцатитысячная -19000 девятнадцатитысячных -19000 девятнадцатитысячного -19000 девятнадцатитысячному -19000 девятнадцатитысячный -19000 девятнадцатитысячным -19000 девятнадцатитысячные -19000 девятнадцатитысячной -19000 девятнадцатитысячными -19000 девятнадцатитысячном -19000 девятнадцатитысячную -19000 девятнадцатитысячная -19000 девятнадцатитысячное -20000 двадцатитысячном -20000 двадцатитысячной -20000 двадцатитысячная -20000 двадцатитысячным -20000 двадцатитысячными -20000 двадцатитысячный -20000 двадцатитысячного -20000 двадцатитысячному -20000 двадцатитысячное -20000 двадцатитысячные -20000 двадцатитысячных -20000 двадцатитысячную diff --git a/nemo_text_processing/text_normalization/ru/data/numbers/quantity.tsv b/nemo_text_processing/text_normalization/ru/data/numbers/quantity.tsv deleted file mode 100644 index 3dca7725dc89..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/numbers/quantity.tsv +++ /dev/null @@ -1,16 +0,0 @@ -тысяч тысяч 0.1 -тысяча тысяча 0.1 -тысячи тысячи 0.1 -тыс. тысячи 0.1 -тыс тысячи -0.1 -миллион миллион 0.1 -миллионов миллионов 0.1 -миллиона миллиона 0.1 -миллиона миллиона 0.1 -млн. миллиона 0.1 -млн миллиона -0.1 -миллиарда миллиарда 0.1 -миллиард миллиард 0.1 -миллиардов миллиардов 0.1 -млрд. миллиарда 0.1 -млрд миллиарда -0.1 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/ru/data/time/__init__.py b/nemo_text_processing/text_normalization/ru/data/time/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/time/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/ru/data/time/increment_hour_cardinal.tsv b/nemo_text_processing/text_normalization/ru/data/time/increment_hour_cardinal.tsv deleted file mode 100644 index 96fd67729b88..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/time/increment_hour_cardinal.tsv +++ /dev/null @@ -1,24 +0,0 @@ -00 час -0.1 -01 два -0.1 -02 три -0.1 -03 четыре -0.1 -04 пять -0.1 -05 шесть -0.1 -06 семь -0.1 -07 восемь -0.1 -08 девять -0.1 -09 десять -0.1 -10 одиннадцать 0.1 -11 двенадцать 0.1 -12 час 0.1 -13 два 0.1 -14 три 0.1 -15 четыре 0.1 -16 пять 0.1 -17 шесть 0.1 -18 семь 0.1 -19 восемь 0.1 -20 девять 0.1 -21 десять 0.1 -22 одиннадцать 0.1 -23 двенадцать 0.1 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/ru/data/time/increment_hour_ordinal.tsv b/nemo_text_processing/text_normalization/ru/data/time/increment_hour_ordinal.tsv deleted file mode 100644 index 83583e14dbb5..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/time/increment_hour_ordinal.tsv +++ /dev/null @@ -1,24 +0,0 @@ -00 первого 0.1 -01 второго -0.1 -02 третьего -0.1 -03 четвертого -0.1 -04 пятого -0.1 -05 шестого -0.1 -06 седьмого -0.1 -07 восьмого -0.1 -08 девятого -0.1 -09 десятого -0.1 -10 одиннадцатого -0.1 -11 двенадцатого -0.1 -12 первого -0.1 -13 второго 0.1 -14 третьего 0.1 -15 четвертого 0.1 -16 пятого 0.1 -17 шестого 0.1 -18 седьмого 0.1 -19 восьмого 0.1 -20 девятого 0.1 -21 десятого 0.1 -22 одиннадцатого 0.1 -23 двенадцатого 0.1 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/ru/data/time/minutes_to_hour.tsv b/nemo_text_processing/text_normalization/ru/data/time/minutes_to_hour.tsv deleted file mode 100644 index 50ab97133dde..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/time/minutes_to_hour.tsv +++ /dev/null @@ -1,25 +0,0 @@ -59 одной минуты -58 двух минут -57 трех минут -56 четырех минут -55 пяти минут -54 шести минут -53 семи минут -52 восеми минут -51 девяти минут -50 десяти минут -49 одиннадцати минут -48 двенадцати минут -47 тринадцати минут -46 четырнадцати минут -45 пятнадцати минут -44 шестнадцати минут -43 семнадцати минут -42 восемьнадцати минут -41 девятнадцати минут -40 двадцати минут -39 двадцати одной минуты -38 двадцати двух минут -37 двадцати трех минут -36 двадцати четырех минут -35 двадцать пяти минут diff --git a/nemo_text_processing/text_normalization/ru/data/time/time_convert.tsv b/nemo_text_processing/text_normalization/ru/data/time/time_convert.tsv deleted file mode 100644 index b10345c4faaa..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/time/time_convert.tsv +++ /dev/null @@ -1,12 +0,0 @@ -13 1 -14 2 -15 3 -16 4 -17 5 -18 6 -19 7 -20 8 -21 9 -22 10 -23 11 -24 12 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/ru/data/utils/__init__.py b/nemo_text_processing/text_normalization/ru/data/utils/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/text_normalization/ru/data/utils/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/ru/data/utils/g.fst b/nemo_text_processing/text_normalization/ru/data/utils/g.fst deleted file mode 100644 index 66665f390b5b9a3fa50fb177a8ed52b4b8fc9e1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3206 zcmd6p*-{ii5JekALEHcpcWpsXQNRVaC%=UdUo`>=ABAu7WBeNmjV#%9|DpN8&DoT z6OYG2FOVPVh_nIpRPVa&GJlZdb-w47WUBa+p5Mw2kZXdkz1rRFpXqsCC~RK4#jxdi zd5p z_o>hLQN5LBbIqT$k3X}V#cq4j`BSgKlx*H>YvNvm$;_}mW`;M|t|P2RmP&PKlaxO{fg6Q@15qtE8> v9m)3&Qu|grY3{x3R`z||ii29N^EcRVUJQNWxufRA<`~;nXRE#6>ofcx!wr7O diff --git a/nemo_text_processing/text_normalization/ru/data/utils/universal_thousands_punct.far b/nemo_text_processing/text_normalization/ru/data/utils/universal_thousands_punct.far deleted file mode 100644 index 4c24b65152f0cc96fd8ed95befe23b5a954c01c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 69495 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 diff --git a/nemo_text_processing/text_normalization/ru/data/utils/util_arithmetic.far b/nemo_text_processing/text_normalization/ru/data/utils/util_arithmetic.far deleted file mode 100644 index 584f04dd5ddfbb2b1ac86f381eb93fc5087102e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7085564 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Ám-mee3+FTHMf$$Ozk|c5*_ZLb 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�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 ^=8i7oS>NM|XXXwa;T~T&&vT5c<9VJ> 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Ðq~|VCkF-W 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

^2CUZPzb;Jhlys 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

oQX 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%0A%yO+V}@7{qq8(%1FP*-j69B04I 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 z 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 A(EtDd diff --git a/nemo_text_processing/text_normalization/ru/data/utils/util_byte.far b/nemo_text_processing/text_normalization/ru/data/utils/util_byte.far deleted file mode 100644 index eabf8f1319dc92086c71ddfec29dc58e4c62fc7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14099 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 cardinal { integer: "тысяча один" } - - Args: - number_names: number_names for cardinal and ordinal numbers - alternative_formats: alternative number formats - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, number_names: dict, alternative_formats: dict, deterministic: bool = False): - super().__init__(name="cardinal", kind="classify", deterministic=deterministic) - - self.cardinal_numbers_default = self.get_cardinal_numbers(number_names, alternative_formats, mode="all") - self.cardinal_numbers_nominative = self.get_cardinal_numbers( - number_names, alternative_formats, mode="nominative" - ) - self.optional_graph_negative = pynini.closure( - pynutil.insert("negative: ") + pynini.cross("-", "\"true\"") + insert_space, 0, 1 - ) - - self.cardinal_numbers_with_optional_negative = ( - self.optional_graph_negative - + pynutil.insert("integer: \"") - + self.cardinal_numbers_default - + pynutil.insert("\"") - ) - - # "03" -> remove leading zeros and verbalize - leading_zeros = pynini.closure(pynini.cross("0", "")) - self.cardinal_numbers_with_leading_zeros = (leading_zeros + self.cardinal_numbers_default).optimize() - - # "123" -> "один два три" - single_digits_graph = pynini.string_file(get_abs_path("data/numbers/cardinals_nominative_case.tsv")).optimize() - single_digits_graph = pynini.compose(NEMO_DIGIT, single_digits_graph) - self.single_digits_graph = single_digits_graph + pynini.closure(insert_space + single_digits_graph) - - optional_quantity = pynini.string_file(get_abs_path("data/numbers/quantity.tsv")).optimize() - optional_quantity = pynutil.insert("quantity: \"") + optional_quantity + pynutil.insert("\"") - optional_quantity = pynini.closure( - (pynutil.add_weight(pynini.accep(NEMO_SPACE), -0.1) | insert_space) + optional_quantity, 0, 1 - ) - - serial_graph = self.get_serial_graph() - - final_graph = ( - self.optional_graph_negative - + pynutil.insert("integer: \"") - + self.cardinal_numbers_with_leading_zeros - + pynutil.insert("\"") - + optional_quantity - ).optimize() - - final_graph = pynutil.add_weight(final_graph, -0.1) - final_graph |= pynutil.insert("integer: \"") + pynutil.add_weight(serial_graph, 10) + pynutil.insert("\"") - self.final_graph = final_graph - - # to cover cases "2-х" -> "двух" (this is not covered by ordinal endings) - final_graph |= pynini.compose( - pynini.compose(NEMO_DIGIT ** (1, ...) + pynini.cross('-х', ''), final_graph), - NEMO_SIGMA + pynini.accep("х\"") + NEMO_SIGMA, - ) - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() - - def get_cardinal_numbers(self, number_names: dict, alternative_formats: dict, mode: str = "all"): - """Returns cardinal numbers names graph. - - Args: - number_names: number_names for cardinal and ordinal numbers - alternative_formats: alternative number formats - mode: "all" - to return graph that includes all Ru cases, "nominative" to return only the nominative form - """ - if mode == "all": - cardinal_names = number_names['cardinal_number_names'] - elif mode == "nominative": - cardinal_names = number_names['cardinal_names_nominative'] - else: - raise ValueError(f'{mode} is not supported.') - one_thousand_alternative = alternative_formats['one_thousand_alternative'] - separators = alternative_formats['separators'] - - cardinal_numbers = cardinal_names | pynini.compose(cardinal_names, one_thousand_alternative) - cardinal_numbers = pynini.compose(separators, cardinal_numbers) - return cardinal_numbers - - def get_serial_graph(self): - """ - Finite state transducer for classifying serial. - The serial is a combination of digits, letters and dashes, e.g.: - c325-b -> tokens { cardinal { integer: "си три два пять би" } } - """ - num_graph = self.single_digits_graph - - alpha = TO_CYRILLIC | RU_ALPHA - - delimiter = insert_space | pynini.cross("-", " ") | pynini.cross("/", " ") - letter_num = pynini.closure(alpha + delimiter, 1) + num_graph - num_letter = pynini.closure(num_graph + delimiter, 1) + alpha - num_delimiter_num = pynini.closure(num_graph + delimiter, 1) + num_graph - next_alpha_or_num = pynini.closure(delimiter + (alpha | num_graph)) - serial_graph = (letter_num | num_letter | num_delimiter_num) + next_alpha_or_num - - # at least 1 alpha and 1 digit is present - at_least_one_alpha_num = ( - NEMO_SIGMA + (RU_ALPHA | pynini.project(TO_CYRILLIC, "input")) + NEMO_SIGMA + NEMO_DIGIT + NEMO_SIGMA - ) | (NEMO_SIGMA + NEMO_DIGIT + NEMO_SIGMA + (RU_ALPHA | pynini.project(TO_CYRILLIC, "input")) + NEMO_SIGMA) - serial_graph = pynini.compose(at_least_one_alpha_num, serial_graph.optimize()).optimize() - # numbers only with 2+ delimiters - serial_graph |= ( - num_graph + delimiter + num_graph + delimiter + num_graph + pynini.closure(delimiter + num_graph) - ).optimize() - return serial_graph.optimize() diff --git a/nemo_text_processing/text_normalization/ru/taggers/date.py b/nemo_text_processing/text_normalization/ru/taggers/date.py deleted file mode 100644 index d4ce6a64601b..000000000000 --- a/nemo_text_processing/text_normalization/ru/taggers/date.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_DIGIT, - NEMO_NOT_QUOTE, - NEMO_SIGMA, - GraphFst, - delete_extra_space, - delete_space, - insert_space, -) -from nemo_text_processing.text_normalization.ru.utils import get_abs_path -from pynini.lib import pynutil - - -class DateFst(GraphFst): - """ - Finite state transducer for classifying date, e.g. - "01.05" -> tokens { date { day: "первое мая" } } - - Args: - number_names: number_names for cardinal and ordinal numbers - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, number_names: dict, deterministic: bool): - super().__init__(name="date", kind="classify", deterministic=deterministic) - - # Ru format: DD-MM-YYYY or DD-MM-YY - month_abbr_to_names = pynini.string_file(get_abs_path("data/months/abbr_to_name.tsv")).optimize() - - delete_sep = pynutil.add_weight(pynini.cross(".", " "), 1.09) | pynutil.add_weight( - pynini.cross(pynini.union("/", "-"), " "), 1.1 - ) - - numbers = number_names['ordinal_number_names'] - - zero = (pynutil.add_weight(pynini.cross("0", ""), -0.1)) | ( - pynutil.add_weight(pynini.cross("0", "ноль "), 0.1) - ) - zero_digit = zero + pynini.compose(NEMO_DIGIT, numbers) - digit_day = (pynini.union("1", "2", "3") + NEMO_DIGIT) | NEMO_DIGIT - digit_day = pynini.compose(digit_day, numbers) - - day = zero_digit | digit_day - day = pynini.compose( - day, pynini.difference(NEMO_SIGMA, NEMO_SIGMA + pynini.union("ой", "ая", "ых", "ые", "ыми")) - ) - day = (pynutil.insert("day: \"") + day + pynutil.insert("\"")).optimize() - - digit_month = zero_digit | pynini.compose(pynini.accep("1") + NEMO_DIGIT, numbers) - month_number_to_abbr = pynini.string_file(get_abs_path("data/months/numbers.tsv")).optimize() - month_number_to_abbr = ( - ( - ((pynutil.add_weight(pynini.cross("0", ""), -0.1) | pynini.accep("1")) + NEMO_DIGIT) | NEMO_DIGIT - ).optimize() - @ month_number_to_abbr - ).optimize() - - month_name = ( - (month_number_to_abbr @ month_abbr_to_names) | pynutil.add_weight(month_abbr_to_names, 0.1) - ).optimize() - month = ( - pynutil.insert("month: \"") + (month_name | pynutil.add_weight(digit_month, 0.1)) + pynutil.insert("\"") - ).optimize() - year = pynini.compose(((NEMO_DIGIT ** 4) | (NEMO_DIGIT ** 2)), numbers).optimize() - year |= zero_digit - - # reduce year options - year_wrong_endings = ["ую", "ая"] - year_wrong_beginning = ["две тысяча", "два тысяч", "два тысячи", "две тысяч "] - year = pynini.compose( - year, pynini.difference(NEMO_SIGMA, NEMO_SIGMA + pynini.union("ой", "ого")) - ) | pynutil.add_weight(pynini.compose(year, NEMO_SIGMA + pynini.union("ой", "ого")), -0.1) - - year_restrict1 = pynini.difference(NEMO_SIGMA, pynini.union(*year_wrong_beginning) + NEMO_SIGMA) - year_restrict2 = pynini.difference(NEMO_SIGMA, NEMO_SIGMA + pynini.union(*year_wrong_endings)) - year = pynini.compose(pynini.compose(year, year_restrict1), year_restrict2) - - year_word_singular = ["год", "года", "году", "годом", "годе"] - year_word_plural = ["годы", "годов", "годам", "годами", "годам", "годах"] - - year_word = pynini.cross("г.", pynini.union(*year_word_singular)) - year_word |= pynini.cross("гг.", pynini.union(*year_word_plural)) - year_word = (pynutil.add_weight(insert_space, -0.1) | pynutil.add_weight(pynini.accep(" "), 0.1)) + year_word - - year_optional = pynutil.insert("year: \"") + year + pynini.closure(year_word, 0, 1) + pynutil.insert("\"") - year_optional = pynini.closure(delete_sep + year_optional, 0, 1).optimize() - year_only = pynutil.insert("year: \"") + year + year_word + pynutil.insert("\"") - - tagger_graph = (day + delete_sep + month + year_optional) | year_only - - # Verbalizer - day = ( - pynutil.delete("day:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - month = ( - pynutil.delete("month:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - year = ( - pynutil.delete("year:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + delete_space - + pynutil.delete("\"") - ) - year_optional = pynini.closure(delete_extra_space + year, 0, 1) - graph_dmy = day + delete_extra_space + month + year_optional - verbalizer_graph = (graph_dmy | year) + delete_space - - self.final_graph = pynini.compose(tagger_graph, verbalizer_graph).optimize() - self.fst = pynutil.insert("day: \"") + self.final_graph + pynutil.insert("\"") - self.fst = self.add_tokens(self.fst).optimize() diff --git a/nemo_text_processing/text_normalization/ru/taggers/decimals.py b/nemo_text_processing/text_normalization/ru/taggers/decimals.py deleted file mode 100644 index de29513a388c..000000000000 --- a/nemo_text_processing/text_normalization/ru/taggers/decimals.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from collections import defaultdict - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_DIGIT, NEMO_SPACE, GraphFst, insert_space -from nemo_text_processing.text_normalization.en.utils import load_labels -from nemo_text_processing.text_normalization.ru.utils import get_abs_path -from pynini.lib import pynutil - -delete_space = pynutil.delete(" ") - - -def prepare_labels_for_insertion(file_path: str): - """ - Read the file and creates a union insertion graph - - Args: - file_path: path to a file (3 columns: a label type e.g. - "@@decimal_delimiter@@", a label e.g. "целого", and a weight e.g. "0.1"). - - Returns dictionary mapping from label type to an fst that inserts the labels with the specified weights. - - """ - labels = load_labels(file_path) - mapping = defaultdict(list) - for k, v, w in labels: - mapping[k].append((v, w)) - - for k in mapping: - mapping[k] = ( - insert_space - + pynini.union(*[pynutil.add_weight(pynutil.insert(end), weight) for end, weight in mapping[k]]) - ).optimize() - return mapping - - -class DecimalFst(GraphFst): - """ - Finite state transducer for classifying decimal, e.g. - "1,08" -> tokens { decimal { integer_part: "одно целая" fractional_part: "восемь сотых} } - - Args: - cardinal: CardinalFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, cardinal: GraphFst, deterministic: bool = False): - super().__init__(name="decimal", kind="classify", deterministic=deterministic) - - integer_part = cardinal.cardinal_numbers_default - cardinal_numbers_with_leading_zeros = cardinal.cardinal_numbers_with_leading_zeros - - delimiter_map = prepare_labels_for_insertion(get_abs_path("data/numbers/decimal_delimiter.tsv")) - delimiter = ( - pynini.cross(",", "") - + delimiter_map['@@decimal_delimiter@@'] - + pynini.closure(pynutil.add_weight(pynutil.insert(" и"), 0.5), 0, 1) - ).optimize() - - decimal_endings_map = prepare_labels_for_insertion(get_abs_path("data/numbers/decimal_endings.tsv")) - - self.integer_part = integer_part + delimiter - graph_integer = pynutil.insert("integer_part: \"") + self.integer_part + pynutil.insert("\"") - - graph_fractional = NEMO_DIGIT @ cardinal_numbers_with_leading_zeros + decimal_endings_map['10'] - graph_fractional |= (NEMO_DIGIT + NEMO_DIGIT) @ cardinal_numbers_with_leading_zeros + decimal_endings_map[ - '100' - ] - graph_fractional |= ( - NEMO_DIGIT + NEMO_DIGIT + NEMO_DIGIT - ) @ cardinal_numbers_with_leading_zeros + decimal_endings_map['1000'] - graph_fractional |= ( - NEMO_DIGIT + NEMO_DIGIT + NEMO_DIGIT + NEMO_DIGIT - ) @ cardinal_numbers_with_leading_zeros + decimal_endings_map['10000'] - - self.optional_quantity = pynini.string_file(get_abs_path("data/numbers/quantity.tsv")).optimize() - - self.graph_fractional = graph_fractional - graph_fractional = pynutil.insert("fractional_part: \"") + graph_fractional + pynutil.insert("\"") - optional_quantity = pynini.closure( - (pynutil.add_weight(pynini.accep(NEMO_SPACE), -0.1) | insert_space) - + pynutil.insert("quantity: \"") - + self.optional_quantity - + pynutil.insert("\""), - 0, - 1, - ) - self.final_graph = ( - cardinal.optional_graph_negative + graph_integer + insert_space + graph_fractional + optional_quantity - ) - - self.final_graph = self.add_tokens(self.final_graph) - self.fst = self.final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/ru/taggers/electronic.py b/nemo_text_processing/text_normalization/ru/taggers/electronic.py deleted file mode 100644 index 22530a28b80a..000000000000 --- a/nemo_text_processing/text_normalization/ru/taggers/electronic.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_ALPHA, - NEMO_DIGIT, - NEMO_NOT_QUOTE, - GraphFst, - delete_space, - insert_space, -) -from nemo_text_processing.text_normalization.ru.alphabet import RU_ALPHA, TO_CYRILLIC -from nemo_text_processing.text_normalization.ru.utils import get_abs_path -from pynini.lib import pynutil - - -class ElectronicFst(GraphFst): - """ - Finite state transducer for classifying electronic: email addresses - e.g. "ab@nd.ru" -> electronic { username: "эй би собака эн ди точка ру" } - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="electronic", kind="classify", deterministic=deterministic) - - # tagger - accepted_symbols = [] - with open(get_abs_path("data/electronic/symbols.tsv"), 'r', encoding='utf-8') as f: - for line in f: - symbol, _ = line.split('\t') - accepted_symbols.append(pynini.accep(symbol)) - username = ( - pynutil.insert("username: \"") - + NEMO_ALPHA - + pynini.closure(NEMO_ALPHA | NEMO_DIGIT | pynini.union(*accepted_symbols)) - + pynutil.insert("\"") - + pynini.cross('@', ' ') - ) - domain_graph = ( - NEMO_ALPHA - + (pynini.closure(NEMO_ALPHA | NEMO_DIGIT | pynini.accep('-') | pynini.accep('.'))) - + (NEMO_ALPHA | NEMO_DIGIT) - ) - domain_graph = pynutil.insert("domain: \"") + domain_graph + pynutil.insert("\"") - tagger_graph = (username + domain_graph).optimize() - - # verbalizer - graph_digit = pynini.string_file(get_abs_path("data/numbers/digits_nominative_case.tsv")).optimize() - graph_symbols = pynini.string_file(get_abs_path("data/electronic/symbols.tsv")).optimize() - user_name = ( - pynutil.delete("username:") - + delete_space - + pynutil.delete("\"") - + ( - pynini.closure( - pynutil.add_weight(graph_digit + insert_space, 1.09) - | pynutil.add_weight(pynini.closure(graph_symbols + pynutil.insert(" ")), 1.09) - | pynutil.add_weight(NEMO_NOT_QUOTE + insert_space, 1.1) - ) - ) - + pynutil.delete("\"") - ) - - domain_default = ( - pynini.closure(NEMO_NOT_QUOTE + insert_space) - + pynini.cross(".", "точка ") - + NEMO_NOT_QUOTE - + pynini.closure(insert_space + NEMO_NOT_QUOTE) - ) - - server_default = ( - pynini.closure((graph_digit | NEMO_ALPHA) + insert_space, 1) - + pynini.closure(graph_symbols + insert_space) - + pynini.closure((graph_digit | NEMO_ALPHA) + insert_space, 1) - ) - server_common = pynini.string_file(get_abs_path("data/electronic/server_name.tsv")) + insert_space - domain_common = pynini.cross(".", "точка ") + pynini.string_file(get_abs_path("data/electronic/domain.tsv")) - domain = ( - pynutil.delete("domain:") - + delete_space - + pynutil.delete("\"") - + (pynutil.add_weight(server_common, 1.09) | pynutil.add_weight(server_default, 1.1)) - + (pynutil.add_weight(domain_common, 1.09) | pynutil.add_weight(domain_default, 1.1)) - + delete_space - + pynutil.delete("\"") - ) - - graph = user_name + delete_space + pynutil.insert("собака ") + delete_space + domain + delete_space - # replace all latin letters with their Ru verbalization - verbalizer_graph = (graph.optimize() @ (pynini.closure(TO_CYRILLIC | RU_ALPHA | pynini.accep(" ")))).optimize() - verbalizer_graph = verbalizer_graph.optimize() - - self.final_graph = (tagger_graph @ verbalizer_graph).optimize() - self.fst = self.add_tokens(pynutil.insert("username: \"") + self.final_graph + pynutil.insert("\"")).optimize() diff --git a/nemo_text_processing/text_normalization/ru/taggers/measure.py b/nemo_text_processing/text_normalization/ru/taggers/measure.py deleted file mode 100644 index a4c60f49ce03..000000000000 --- a/nemo_text_processing/text_normalization/ru/taggers/measure.py +++ /dev/null @@ -1,170 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_NON_BREAKING_SPACE, - NEMO_NOT_QUOTE, - NEMO_SPACE, - GraphFst, - insert_space, -) -from nemo_text_processing.text_normalization.ru.alphabet import RU_ALPHA -from nemo_text_processing.text_normalization.ru.utils import get_abs_path -from pynini.lib import pynutil - - -class MeasureFst(GraphFst): - """ - Finite state transducer for classifying measure, e.g. - "2 кг" -> measure { cardinal { integer: "два килограма" } } - This class also converts words containing numbers and letters - e.g. "тест-8" —> "тест восемь" - e.g. "тест-1,02" —> "тест одна целая две сотых" - - Args: - cardinal: CardinalFst - decimal: DecimalFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, cardinal: GraphFst, decimal: GraphFst, deterministic: bool = True): - super().__init__(name="measure", kind="classify", deterministic=deterministic) - - # adding weight to make sure the space is preserved for ITN - delete_space = pynini.closure( - pynutil.add_weight(pynutil.delete(pynini.union(NEMO_SPACE, NEMO_NON_BREAKING_SPACE)), -1), 0, 1 - ) - - cardinal_graph = cardinal.cardinal_numbers_default - cardinal_graph_nominative = cardinal.cardinal_numbers_nominative - graph_unit = pynini.string_file(get_abs_path("data/measurements.tsv")) - optional_graph_negative = cardinal.optional_graph_negative - - space_for_units = ( - pynutil.add_weight(pynutil.insert(NEMO_NON_BREAKING_SPACE), -0.1) - | pynutil.add_weight(pynutil.insert(NEMO_SPACE), 0.1) - ).optimize() - slash_unit = (pynini.cross("/", "в") | pynini.cross("/", "за")) + space_for_units + graph_unit - - unit_slash_unit = pynutil.add_weight(graph_unit + space_for_units + slash_unit, -0.1) - default_units = pynutil.insert("units: \"") + (graph_unit | unit_slash_unit) + pynutil.insert("\"") - slash_units = pynutil.insert("units: \"") + slash_unit + pynutil.insert("\"") - subgraph_decimal = decimal.final_graph + ((delete_space + default_units) | slash_units) - - cardinal_space = ( - pynutil.insert("cardinal { ") - + optional_graph_negative - + pynutil.insert("integer: \"") - + cardinal_graph - + ( - (delete_space + pynutil.insert("\"") + pynutil.insert(" } ") + default_units) - | (pynutil.insert("\"") + pynutil.insert(" } ") + slash_units) - ) - ) - - cardinal_optional_dash_alpha = ( - pynutil.insert("cardinal { integer: \"") - + cardinal_graph - + pynini.closure(pynini.cross('-', ''), 0, 1) - + pynutil.insert("\" } units: \"") - + pynini.closure(RU_ALPHA, 1) - + pynutil.insert("\"") - ) - - alpha_optional_dash_cardinal = ( - pynutil.insert("units: \"") - + pynini.closure(RU_ALPHA, 1) - + pynini.closure(pynini.cross('-', ''), 0, 1) - + pynutil.insert("\"") - + pynutil.insert(" cardinal { integer: \"") - + cardinal_graph_nominative - + pynutil.insert("\" } preserve_order: true") - ) - - decimal_dash_alpha = ( - decimal.final_graph - + pynini.cross('-', '') - + pynutil.insert(" units: \"") - + pynini.closure(RU_ALPHA, 1) - + pynutil.insert("\"") - ) - - alpha_dash_decimal = ( - pynutil.insert("units: \"") - + pynini.closure(RU_ALPHA, 1) - + pynini.cross('-', '') - + pynutil.insert("\" ") - + decimal.final_graph - + pynutil.insert(" preserve_order: true") - ) - - self.tagger_graph_default = (subgraph_decimal | cardinal_space).optimize() - - tagger_graph = ( - self.tagger_graph_default - | cardinal_optional_dash_alpha - | alpha_optional_dash_cardinal - | decimal_dash_alpha - | alpha_dash_decimal - ).optimize() - - # verbalizer - unit = pynutil.delete("units: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") + delete_space - - optional_sign = pynini.closure(pynini.cross("negative: \"true\" ", "минус "), 0, 1) - integer = pynutil.delete(" \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - integer_part = pynutil.delete("integer_part:") + integer - fractional_part = pynutil.delete("fractional_part:") + integer - optional_quantity_part = pynini.closure( - pynini.accep(" ") - + pynutil.delete("quantity: \"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\""), - 0, - 1, - ) - graph_decimal = optional_sign + integer_part + pynini.accep(" ") + fractional_part + optional_quantity_part - - graph_decimal = pynutil.delete("decimal {") + delete_space + graph_decimal + delete_space + pynutil.delete("}") - - graph_cardinal = ( - pynutil.delete("cardinal {") - + delete_space - + optional_sign - + pynutil.delete("integer: \"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - + delete_space - + pynutil.delete("}") - ) - - verbalizer_graph = (graph_cardinal | graph_decimal) + delete_space + insert_space + unit - - # SH adds "preserve_order: true" by default - preserve_order = pynutil.delete("preserve_order:") + delete_space + pynutil.delete("true") + delete_space - verbalizer_graph |= ( - unit - + insert_space - + (graph_cardinal | graph_decimal) - + delete_space - + pynini.closure(preserve_order, 0, 1) - ) - self.verbalizer_graph = verbalizer_graph.optimize() - - final_graph = (tagger_graph @ verbalizer_graph).optimize() - self.fst = self.add_tokens( - pynutil.insert("cardinal { integer: \"") + final_graph + pynutil.insert("\" }") - ).optimize() diff --git a/nemo_text_processing/text_normalization/ru/taggers/money.py b/nemo_text_processing/text_normalization/ru/taggers/money.py deleted file mode 100644 index c08723ae8c3c..000000000000 --- a/nemo_text_processing/text_normalization/ru/taggers/money.py +++ /dev/null @@ -1,95 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, NEMO_SIGMA, NEMO_SPACE, GraphFst -from nemo_text_processing.text_normalization.ru.utils import get_abs_path -from pynini.lib import pynutil - - -class MoneyFst(GraphFst): - """ - Finite state transducer for classifying money, e.g. - "5руб." -> money { "пять рублей" } - - Args: - cardinal: CardinalFst - decimal: DecimalFst - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, cardinal: GraphFst, decimal: GraphFst, deterministic: bool = True): - super().__init__(name="money", kind="classify", deterministic=deterministic) - cardinal_graph = cardinal.cardinal_numbers_default - decimal_graph = decimal.final_graph - - unit_singular = pynini.string_file(get_abs_path("data/currency/currency_singular.tsv")) - unit_plural = pynini.string_file(get_abs_path("data/currency/currency_plural.tsv")) - - # adding weight to make sure the space is preserved for ITN - optional_delimiter = pynini.closure(pynutil.add_weight(pynini.cross(NEMO_SPACE, ""), -100), 0, 1) - graph_unit_singular = ( - optional_delimiter + pynutil.insert(" currency: \"") + unit_singular + pynutil.insert("\"") - ) - graph_unit_plural = optional_delimiter + pynutil.insert(" currency: \"") + unit_plural + pynutil.insert("\"") - - one = pynini.compose(pynini.accep("1"), cardinal_graph).optimize() - singular_graph = pynutil.insert("integer_part: \"") + one + pynutil.insert("\"") + graph_unit_singular - - graph_decimal = decimal_graph + graph_unit_plural - - graph_integer = ( - pynutil.insert("integer_part: \"") - + ((NEMO_SIGMA - "1") @ cardinal_graph) - + pynutil.insert("\"") - + (graph_unit_plural) - ) - - graph_integer |= singular_graph - tagger_graph = (graph_integer.optimize() | graph_decimal.optimize()).optimize() - - # verbalizer - integer = pynutil.delete("\"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - integer_part = pynutil.delete("integer_part: ") + integer - - unit = ( - pynutil.delete("currency: ") - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - unit = pynini.accep(NEMO_SPACE) + unit - - verbalizer_graph_cardinal = (integer_part + unit).optimize() - - fractional_part = pynutil.delete("fractional_part: ") + integer - optional_quantity = pynini.closure(pynini.accep(NEMO_SPACE) + pynutil.delete("quantity: ") + integer, 0, 1) - - verbalizer_graph_decimal = ( - pynutil.delete('decimal { ') - + integer_part - + pynini.accep(" ") - + fractional_part - + optional_quantity - + pynutil.delete(" }") - + unit - ) - - verbalizer_graph = (verbalizer_graph_cardinal | verbalizer_graph_decimal).optimize() - - self.final_graph = (tagger_graph @ verbalizer_graph).optimize() - self.fst = self.add_tokens( - pynutil.insert("integer_part: \"") + self.final_graph + pynutil.insert("\"") - ).optimize() diff --git a/nemo_text_processing/text_normalization/ru/taggers/number_names.py b/nemo_text_processing/text_normalization/ru/taggers/number_names.py deleted file mode 100644 index 048b5f43ecb9..000000000000 --- a/nemo_text_processing/text_normalization/ru/taggers/number_names.py +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# Copyright 2017 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Adapted from https://github.com/google/TextNormalizationCoveringGrammars -# Russian minimally supervised number grammar. -# -# Supports cardinals and ordinals in all inflected forms. -# -# The language-specific acceptor G was compiled with digit, teen, decade, -# century, and big power-of-ten preterminals. The lexicon transducer is -# highly ambiguous, but no LM is used. - -# Intersects the universal factorization transducer (F) with language-specific -# acceptor (G). - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_SIGMA -from nemo_text_processing.text_normalization.ru.utils import get_abs_path, load_labels -from pynini.lib import pynutil, rewrite - - -def get_number_names(): - """ - Creates numbers names. - - Based on: 1) Gorman, K., and Sproat, R. 2016. Minimally supervised number normalization. - Transactions of the Association for Computational Linguistics 4: 507-519. - and 2) Ng, A. H., Gorman, K., and Sproat, R. 2017. - Minimally supervised written-to-spoken text normalization. In ASRU, pages 665-670. - """ - a = pynini.Far(get_abs_path('data/utils/util_arithmetic.far'), mode='r') - d = a['DELTA_STAR'] - f = a['IARITHMETIC_RESTRICTED'] - g = pynini.Fst.read(get_abs_path('data/utils/g.fst')) - fg = (d @ (f @ (f @ (f @ g).optimize()).optimize()).optimize()).optimize() - assert rewrite.top_rewrite("230", fg) == "(+ 200 30 +)" - - # Compiles lexicon transducers (L). - cardinal_name_nominative = pynini.string_file(get_abs_path("data/numbers/1_cardinals_nominative.tsv")).optimize() - cardinal_name_genitive = pynini.string_file(get_abs_path("data/numbers/2_cardinals_genitive.tsv")).optimize() - cardinal_name_dative = pynini.string_file(get_abs_path("data/numbers/3_cardinals_dative.tsv")).optimize() - cardinal_name_accusative = pynini.string_file(get_abs_path("data/numbers/4_cardinals_accusative.tsv")).optimize() - cardinal_name_instrumental = pynini.string_file( - get_abs_path("data/numbers/5_cardinals_instrumental.tsv") - ).optimize() - cardinal_name_prepositional = pynini.string_file( - get_abs_path("data/numbers/6_cardinals_prepositional.tsv") - ).optimize() - - cardinal_name_nominative = ( - pynini.closure(cardinal_name_nominative + pynini.accep(" ")) + cardinal_name_nominative - ).optimize() - cardinal_l = pynutil.add_weight(cardinal_name_nominative, -0.1) - for case in [ - pynutil.add_weight(cardinal_name_genitive, 0.1).optimize(), - pynutil.add_weight(cardinal_name_dative, 0.1).optimize(), - pynutil.add_weight(cardinal_name_accusative, 0.1).optimize(), - pynutil.add_weight(cardinal_name_instrumental, 0.1).optimize(), - pynutil.add_weight(cardinal_name_prepositional, 0.1).optimize(), - ]: - cardinal_l |= (pynini.closure(case + pynini.accep(" ")) + case).optimize() - - # Numbers in nominative case (to use, for example, with telephone or serial_graph (in cardinals)) - cardinal_names_nominative_l = ( - pynini.closure(cardinal_name_nominative + pynini.accep(" ")) + cardinal_name_nominative - ).optimize() - - # Convert e.g. "(* 5 1000 *)" back to "5000" so complex ordinals will be formed correctly, - # e.g. "пятитысячный" will eventually be formed. (If we didn't do this, the incorrect phrase - # "пять тысячный" would be formed). - # We do this for all thousands from "(*2 1000 *)" —> "2000" to "(*20 1000 *)" —> "20000". - # We do not go higher, in order to prevent the WFST graph becoming even larger. - complex_numbers = pynini.cross("(* 2 1000 *)", "2000") - for number in range(3, 21): - complex_numbers |= pynini.cross(f"(* {number} 1000 *)", f"{number}000") - - complex_numbers = ( - NEMO_SIGMA + pynutil.add_weight(complex_numbers, -1) + pynini.closure(pynini.union(" ", ")", "(", "+", "*")) - ).optimize() - fg_ordinal = pynutil.add_weight(pynini.compose(fg, complex_numbers), -1) | fg - ordinal_name = pynini.string_file(get_abs_path("data/numbers/ordinals.tsv")) - ordinal_l = (pynini.closure(cardinal_name_nominative + pynini.accep(" ")) + ordinal_name).optimize() - - # Composes L with the leaf transducer (P), then composes that with FG. - p = a['LEAVES'] - number_names = {} - number_names['ordinal_number_names'] = (fg_ordinal @ (p @ ordinal_l)).optimize() - number_names['cardinal_number_names'] = (fg @ (p @ cardinal_l)).optimize() - number_names['cardinal_names_nominative'] = (fg @ (p @ cardinal_names_nominative_l)).optimize() - return number_names - - -def get_alternative_formats(): - """ - Utils to get alternative formats for numbers. - """ - one_alternatives = load_labels(get_abs_path('data/numbers/cardinals_alternatives.tsv')) - one_thousand_map = [] - for k in one_alternatives: - default, alternative = k - one_thousand_map.append((alternative.split()[1], alternative)) - one_thousand_map = pynini.string_map(one_thousand_map) - - one_thousand_alternative = pynini.cdrewrite(one_thousand_map, "[BOS]", "", NEMO_SIGMA) - - # Adapted from - # https://github.com/google/TextNormalizationCoveringGrammars/blob/master/src/universal/thousands_punct.grm - # Specifies common ways of delimiting thousands in digit strings. - t = pynini.Far(get_abs_path('data/utils/universal_thousands_punct.far')) - separators = ( - pynutil.add_weight(t['dot_thousands'], 0.1) - | pynutil.add_weight(t['no_delimiter'], -0.1) - | pynutil.add_weight(t['space_thousands'], 0.1) - ) - alternative_formats = {} - alternative_formats['one_thousand_alternative'] = one_thousand_alternative.optimize() - alternative_formats['separators'] = separators.optimize() - return alternative_formats - - -if __name__ == '__main__': - from nemo_text_processing.text_normalization.en.graph_utils import generator_main - - numbers = get_number_names() - for k, v in numbers.items(): - generator_main(f'{k}.far', {k: v}) diff --git a/nemo_text_processing/text_normalization/ru/taggers/ordinal.py b/nemo_text_processing/text_normalization/ru/taggers/ordinal.py deleted file mode 100644 index 9a09e40636d8..000000000000 --- a/nemo_text_processing/text_normalization/ru/taggers/ordinal.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Adapted from https://github.com/google/TextNormalizationCoveringGrammars -# Russian minimally supervised number grammar. - - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_SIGMA, GraphFst -from nemo_text_processing.text_normalization.ru.utils import get_abs_path -from pynini.lib import pynutil - - -class OrdinalFst(GraphFst): - """ - Finite state transducer for classifying cardinals, e.g. - "2" -> ordinal { integer: "второе" } } - - Args: - number_names: number_names for cardinal and ordinal numbers - alternative_formats: alternative format for cardinal and ordinal numbers - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, number_names: dict, alternative_formats: dict, deterministic=False): - super().__init__(name="ordinal", kind="classify", deterministic=deterministic) - - one_thousand_alternative = alternative_formats['one_thousand_alternative'] - separators = alternative_formats['separators'] - - ordinal = number_names['ordinal_number_names'] - - ordinal |= ordinal @ one_thousand_alternative - ordinal_numbers = separators @ ordinal - - # to handle cases like 2-ая - endings = pynini.string_file(get_abs_path("data/numbers/ordinal_endings.tsv")) - not_dash = pynini.closure(pynini.difference(NEMO_SIGMA, "-")) - del_ending = pynini.cdrewrite(pynini.cross("-" + not_dash, ""), "", "[EOS]", NEMO_SIGMA) - ordinal_numbers_marked = ( - ((separators @ ordinal).optimize() + pynini.accep("-") + not_dash).optimize() - @ (NEMO_SIGMA + endings).optimize() - @ del_ending - ).optimize() - - self.ordinal_numbers = ordinal_numbers - # "03" -> remove leading zeros and verbalize - leading_zeros = pynini.closure(pynini.cross("0", "")) - self.ordinal_numbers_with_leading_zeros = (leading_zeros + ordinal_numbers).optimize() - - final_graph = (ordinal_numbers | ordinal_numbers_marked).optimize() - final_graph = pynutil.insert("integer: \"") + final_graph + pynutil.insert("\"") - final_graph = self.add_tokens(final_graph) - self.fst = final_graph.optimize() diff --git a/nemo_text_processing/text_normalization/ru/taggers/telephone.py b/nemo_text_processing/text_normalization/ru/taggers/telephone.py deleted file mode 100644 index 142242e8a4b7..000000000000 --- a/nemo_text_processing/text_normalization/ru/taggers/telephone.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_DIGIT, GraphFst, delete_space, insert_space -from nemo_text_processing.text_normalization.ru.alphabet import RU_ALPHA_OR_SPACE -from pynini.lib import pynutil - - -class TelephoneFst(GraphFst): - """ - Finite state transducer for classifying telephone, which includes country code, number part and extension - - E.g - "8-913-983-56-01" -> telephone { number_part: "восемь девятьсот тринадцать девятьсот восемьдесят три пятьдесят шесть ноль один" } - - Args: - number_names: number_names for cardinal and ordinal numbers - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, number_names: dict, deterministic: bool = True): - super().__init__(name="telephone", kind="classify", deterministic=deterministic) - - separator = pynini.cross("-", " ") # between components - number = number_names["cardinal_names_nominative"] - - country_code = ( - pynutil.insert("country_code: \"") - + pynini.closure(pynutil.add_weight(pynutil.delete("+"), 0.1), 0, 1) - + number - + separator - + pynutil.insert("\"") - ) - optional_country_code = pynini.closure(country_code + insert_space, 0, 1) - - number_part = ( - NEMO_DIGIT ** 3 @ number - + separator - + NEMO_DIGIT ** 3 @ number - + separator - + NEMO_DIGIT ** 2 @ number - + separator - + NEMO_DIGIT ** 2 @ (pynini.closure(pynini.cross("0", "ноль ")) + number) - ) - number_part = pynutil.insert("number_part: \"") + number_part + pynutil.insert("\"") - tagger_graph = (optional_country_code + number_part).optimize() - - # verbalizer - verbalizer_graph = pynini.closure( - pynutil.delete("country_code: \"") - + pynini.closure(RU_ALPHA_OR_SPACE, 1) - + pynutil.delete("\"") - + delete_space, - 0, - 1, - ) - verbalizer_graph += ( - pynutil.delete("number_part: \"") + pynini.closure(RU_ALPHA_OR_SPACE, 1) + pynutil.delete("\"") - ) - verbalizer_graph = verbalizer_graph.optimize() - - self.final_graph = (tagger_graph @ verbalizer_graph).optimize() - self.fst = self.add_tokens( - pynutil.insert("number_part: \"") + self.final_graph + pynutil.insert("\"") - ).optimize() diff --git a/nemo_text_processing/text_normalization/ru/taggers/time.py b/nemo_text_processing/text_normalization/ru/taggers/time.py deleted file mode 100644 index bb97f36ea31f..000000000000 --- a/nemo_text_processing/text_normalization/ru/taggers/time.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst -from nemo_text_processing.text_normalization.ru.utils import get_abs_path -from pynini.lib import pynutil - - -class TimeFst(GraphFst): - """ - Finite state transducer for classifying time, e.g. - "02:15" -> time { hours: "два часа пятнадцать минут" } - - Args: - number_names: number_names for cardinal and ordinal numbers - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, number_names: dict, deterministic: bool = True): - super().__init__(name="time", kind="classify", deterministic=deterministic) - - increment_hour_ordinal = pynini.string_file(get_abs_path("data/time/increment_hour_ordinal.tsv")) - increment_hour_cardinal = pynini.string_file(get_abs_path("data/time/increment_hour_cardinal.tsv")) - convert_hour = pynini.string_file(get_abs_path("data/time/time_convert.tsv")) - - number = pynini.closure(pynini.cross("0", ""), 0, 1) + number_names['cardinal_names_nominative'] - hour_options = pynini.project(increment_hour_ordinal, "input") - hour_options = hour_options | pynini.project(convert_hour, "output") - - hour_exeption_ends_with_one = pynini.union(*["01", "21"]) - hour_exeption_ends_rest = pynini.union(*["02", "03", "04", "22", "23"]) - hour_other = ( - pynini.difference(hour_options, pynini.union(hour_exeption_ends_with_one, hour_exeption_ends_rest)) - ).optimize() - - hour = hour_exeption_ends_with_one @ number + pynutil.insert(" час") - hour |= hour_exeption_ends_rest @ number + pynutil.insert(" часа") - hour |= hour_other @ number + pynutil.insert(" часов") - - optional_and = pynini.closure(pynutil.insert("и "), 0, 1) - digits = pynini.union(*[str(x) for x in range(10)]) - mins_start = pynini.union(*"012345") - mins_options = mins_start + digits - mins_exception_ends_with_one = mins_start + pynini.accep("1") - mins_exception_ends_rest = pynini.difference( - mins_start + pynini.union(*"234"), pynini.union(*["12", "13", "14"]) - ) - mins_other = pynini.difference( - mins_options, pynini.union(mins_exception_ends_with_one, mins_exception_ends_rest) - ) - - minutes = mins_exception_ends_with_one @ number + pynutil.insert(" минута") - minutes |= mins_exception_ends_rest @ number + pynutil.insert(" минуты") - minutes |= mins_other @ number + pynutil.insert(" минут") - self.minutes = minutes.optimize() - # 17:15 -> "семнадцать часов и пятнадцать минут" - hm = ( - pynutil.insert("hours: \"") - + hour.optimize() - + pynutil.insert("\"") - + (pynini.cross(":", " ") + pynutil.insert("minutes: \"") + optional_and + minutes.optimize()) - + pynutil.insert("\"") - + pynutil.insert(" preserve_order: true") - ) - h = pynutil.insert("hours: \"") + hour + pynutil.insert("\"") + pynutil.delete(":00") - self.graph_preserve_order = (hm | h).optimize() - - # 17:15 -> "пятнадцать минут шестого" - # Requires permutations for the correct verbalization - self.increment_hour_ordinal = pynini.compose(hour_options, increment_hour_ordinal).optimize() - m_next_h = ( - pynutil.insert("hours: \"") - + self.increment_hour_ordinal - + pynutil.insert("\"") - + pynini.cross(":", " ") - + pynutil.insert("minutes: \"") - + minutes - + pynutil.insert("\"") - ) - - # 17:45 -> "без пятнадцати минут шесть" - # Requires permutations for the correct verbalization - self.mins_to_h = pynini.string_file(get_abs_path("data/time/minutes_to_hour.tsv")).optimize() - self.increment_hour_cardinal = pynini.compose(hour_options, increment_hour_cardinal).optimize() - m_to_h = ( - pynutil.insert("hours: \"") - + self.increment_hour_cardinal - + pynutil.insert("\"") - + pynini.cross(":", " ") - + pynutil.insert("minutes: \"без ") - + self.mins_to_h - + pynutil.insert("\"") - ) - - self.final_graph = m_next_h | self.graph_preserve_order | m_to_h - self.fst = self.add_tokens(self.final_graph) - self.fst = self.fst.optimize() diff --git a/nemo_text_processing/text_normalization/ru/taggers/tokenize_and_classify.py b/nemo_text_processing/text_normalization/ru/taggers/tokenize_and_classify.py deleted file mode 100644 index 676bc575511b..000000000000 --- a/nemo_text_processing/text_normalization/ru/taggers/tokenize_and_classify.py +++ /dev/null @@ -1,142 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - GraphFst, - delete_extra_space, - delete_space, - generator_main, -) -from nemo_text_processing.text_normalization.en.taggers.punctuation import PunctuationFst -from nemo_text_processing.text_normalization.ru.taggers.cardinal import CardinalFst -from nemo_text_processing.text_normalization.ru.taggers.date import DateFst -from nemo_text_processing.text_normalization.ru.taggers.decimals import DecimalFst -from nemo_text_processing.text_normalization.ru.taggers.electronic import ElectronicFst -from nemo_text_processing.text_normalization.ru.taggers.measure import MeasureFst -from nemo_text_processing.text_normalization.ru.taggers.money import MoneyFst -from nemo_text_processing.text_normalization.ru.taggers.number_names import get_alternative_formats, get_number_names -from nemo_text_processing.text_normalization.ru.taggers.ordinal import OrdinalFst -from nemo_text_processing.text_normalization.ru.taggers.telephone import TelephoneFst -from nemo_text_processing.text_normalization.ru.taggers.time import TimeFst -from nemo_text_processing.text_normalization.ru.taggers.whitelist import WhiteListFst -from nemo_text_processing.text_normalization.ru.taggers.word import WordFst -from pynini.lib import pynutil - -from nemo.utils import logging - - -class ClassifyFst(GraphFst): - """ - Final class that composes all other classification grammars. This class can process an entire sentence, that is lower cased. - For deployment, this grammar will be compiled and exported to OpenFst Finite State Archive (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - - Args: - input_case: accepting either "lower_cased" or "cased" input. - deterministic: if True will provide a single transduction option, - for False multiple options (used for audio-based normalization) - cache_dir: path to a dir with .far grammar file. Set to None to avoid using cache. - overwrite_cache: set to True to overwrite .far files - whitelist: path to a file with whitelist replacements - """ - - def __init__( - self, - input_case: str, - deterministic: bool = False, - cache_dir: str = None, - overwrite_cache: bool = False, - whitelist: str = None, - ): - super().__init__(name="tokenize_and_classify", kind="classify", deterministic=deterministic) - if deterministic: - raise ValueError( - 'Ru TN only supports non-deterministic cases and produces multiple normalization options.' - ) - far_file = None - if cache_dir is not None and cache_dir != "None": - os.makedirs(cache_dir, exist_ok=True) - whitelist_file = os.path.basename(whitelist) if whitelist else "" - far_file = os.path.join( - cache_dir, f"_{input_case}_ru_tn_{deterministic}_deterministic{whitelist_file}.far" - ) - if not overwrite_cache and far_file and os.path.exists(far_file): - self.fst = pynini.Far(far_file, mode="r")["tokenize_and_classify"] - logging.info(f"ClassifyFst.fst was restored from {far_file}.") - else: - logging.info(f"Creating ClassifyFst grammars. This might take some time...") - number_names = get_number_names() - alternative_formats = get_alternative_formats() - - self.cardinal = CardinalFst( - number_names=number_names, alternative_formats=alternative_formats, deterministic=deterministic - ) - cardinal_graph = self.cardinal.fst - - self.ordinal = OrdinalFst( - number_names=number_names, alternative_formats=alternative_formats, deterministic=deterministic - ) - ordinal_graph = self.ordinal.fst - - self.decimal = DecimalFst(cardinal=self.cardinal, deterministic=deterministic) - decimal_graph = self.decimal.fst - - self.measure = MeasureFst(cardinal=self.cardinal, decimal=self.decimal, deterministic=deterministic) - measure_graph = self.measure.fst - self.date = DateFst(number_names=number_names, deterministic=deterministic) - date_graph = self.date.fst - word_graph = WordFst(deterministic=deterministic).fst - self.time = TimeFst(number_names=number_names, deterministic=deterministic) - time_graph = self.time.fst - self.telephone = TelephoneFst(number_names=number_names, deterministic=deterministic) - telephone_graph = self.telephone.fst - self.electronic = ElectronicFst(deterministic=deterministic) - electronic_graph = self.electronic.fst - self.money = MoneyFst(cardinal=self.cardinal, decimal=self.decimal, deterministic=deterministic) - money_graph = self.money.fst - self.whitelist = WhiteListFst(input_case=input_case, deterministic=deterministic, input_file=whitelist) - whitelist_graph = self.whitelist.fst - punct_graph = PunctuationFst(deterministic=deterministic).fst - - classify = ( - pynutil.add_weight(whitelist_graph, 1.01) - | pynutil.add_weight(time_graph, 1.1) - | pynutil.add_weight(date_graph, 1.09) - | pynutil.add_weight(decimal_graph, 1.1) - | pynutil.add_weight(measure_graph, 0.9) - | pynutil.add_weight(cardinal_graph, 1.1) - | pynutil.add_weight(ordinal_graph, 1.1) - | pynutil.add_weight(money_graph, 1.1) - | pynutil.add_weight(telephone_graph, 1.1) - | pynutil.add_weight(electronic_graph, 1.1) - | pynutil.add_weight(word_graph, 100) - ) - - punct = pynutil.insert("tokens { ") + pynutil.add_weight(punct_graph, weight=1.1) + pynutil.insert(" }") - token = pynutil.insert("tokens { ") + classify + pynutil.insert(" }") - token_plus_punct = ( - pynini.closure(punct + pynutil.insert(" ")) + token + pynini.closure(pynutil.insert(" ") + punct) - ) - - graph = token_plus_punct + pynini.closure(pynutil.add_weight(delete_extra_space, 1.1) + token_plus_punct) - graph = delete_space + graph + delete_space - - self.fst = graph.optimize() - - if far_file: - generator_main(far_file, {"tokenize_and_classify": self.fst}) - logging.info(f"ClassifyFst grammars are saved to {far_file}.") diff --git a/nemo_text_processing/text_normalization/ru/taggers/whitelist.py b/nemo_text_processing/text_normalization/ru/taggers/whitelist.py deleted file mode 100644 index 85182920806b..000000000000 --- a/nemo_text_processing/text_normalization/ru/taggers/whitelist.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_CHAR, GraphFst, convert_space -from nemo_text_processing.text_normalization.ru.alphabet import RU_ALPHA, TO_CYRILLIC -from nemo_text_processing.text_normalization.ru.utils import get_abs_path, load_labels -from pynini.lib import pynutil - - -class WhiteListFst(GraphFst): - """ - Finite state transducer for classifying whitelist, e.g. - misses -> tokens { name: "mrs" } - for non-deterministic case: "Dr. Abc" -> - tokens { name: "drive" } tokens { name: "Abc" } - tokens { name: "doctor" } tokens { name: "Abc" } - tokens { name: "Dr." } tokens { name: "Abc" } - This class has highest priority among all classifier grammars. Whitelisted tokens are defined and loaded from "data/whitelist.tsv". - - Args: - input_case: accepting either "lower_cased" or "cased" input. - deterministic: if True will provide a single transduction option, - for False multiple options (used for audio-based normalization) - input_file: path to a file with whitelist replacements - """ - - def __init__(self, input_case: str, deterministic: bool = True, input_file: str = None): - super().__init__(name="whitelist", kind="classify", deterministic=deterministic) - - def _get_whitelist_graph(input_case, file): - whitelist = load_labels(file) - if input_case == "lower_cased": - whitelist = [[x[0].lower()] + x[1:] for x in whitelist] - else: - whitelist = [[x[0].lower()] + x[1:] for x in whitelist] - graph = pynini.string_map(whitelist) - return graph - - graph = _get_whitelist_graph(input_case, get_abs_path("data/whitelist.tsv")) - - if input_file: - graph = _get_whitelist_graph(input_case, input_file) - - units_graph = _get_whitelist_graph(input_case, file=get_abs_path("data/measurements.tsv")) - # do not replace single letter units, like `м`, `°` and `%` will be replaced - units_graph = pynini.compose((NEMO_CHAR ** (2, ...) | pynini.difference(NEMO_CHAR, RU_ALPHA)), units_graph) - graph |= units_graph.optimize() - graph |= TO_CYRILLIC + pynini.closure(pynutil.insert(" ") + TO_CYRILLIC) - - self.final_graph = convert_space(graph) - self.fst = (pynutil.insert("name: \"") + self.final_graph + pynutil.insert("\"")).optimize() diff --git a/nemo_text_processing/text_normalization/ru/taggers/word.py b/nemo_text_processing/text_normalization/ru/taggers/word.py deleted file mode 100644 index 16743d28b574..000000000000 --- a/nemo_text_processing/text_normalization/ru/taggers/word.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_SPACE, GraphFst -from pynini.lib import pynutil - - -class WordFst(GraphFst): - """ - Finite state transducer for classifying word. - e.g. sleep -> tokens { name: "sleep" } - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="word", kind="classify") - word = pynutil.insert("name: \"") + pynini.closure(NEMO_NOT_SPACE, 1) + pynutil.insert("\"") - self.fst = word.optimize() diff --git a/nemo_text_processing/text_normalization/ru/utils.py b/nemo_text_processing/text_normalization/ru/utils.py deleted file mode 100644 index aeb332d0ef5b..000000000000 --- a/nemo_text_processing/text_normalization/ru/utils.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import csv -import os - -from nemo.utils import logging - - -def get_abs_path(rel_path): - """ - Get absolute path - - Args: - rel_path: relative path to this file - - Returns absolute path - """ - abs_path = os.path.dirname(os.path.abspath(__file__)) + os.sep + rel_path - - if not os.path.exists(abs_path): - logging.warning(f'{abs_path} does not exist') - return abs_path - - -def load_labels(abs_path): - """ - loads relative path file as dictionary - - Args: - abs_path: absolute path - - Returns dictionary of mappings - """ - with open(abs_path, encoding='utf-8') as label_tsv: - labels = list(csv.reader(label_tsv, delimiter="\t")) - return labels diff --git a/nemo_text_processing/text_normalization/ru/verbalizers/__init__.py b/nemo_text_processing/text_normalization/ru/verbalizers/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/nemo_text_processing/text_normalization/ru/verbalizers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/ru/verbalizers/cardinal.py b/nemo_text_processing/text_normalization/ru/verbalizers/cardinal.py deleted file mode 100644 index 4bff9ed3f168..000000000000 --- a/nemo_text_processing/text_normalization/ru/verbalizers/cardinal.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst -from pynini.lib import pynutil - - -class CardinalFst(GraphFst): - """ - Finite state transducer for verbalizing cardinals - e.g. cardinal { integer: "тысяча один" } -> "тысяча один" - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="cardinal", kind="verbalize", deterministic=deterministic) - optional_sign = pynini.closure(pynini.cross("negative: \"true\" ", "минус "), 0, 1) - optional_quantity_part = pynini.closure( - pynini.accep(" ") - + pynutil.delete("quantity: \"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\""), - 0, - 1, - ) - integer = pynutil.delete("integer: \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - self.graph = optional_sign + integer + optional_quantity_part - delete_tokens = self.delete_tokens(self.graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/ru/verbalizers/date.py b/nemo_text_processing/text_normalization/ru/verbalizers/date.py deleted file mode 100644 index 52a69b0c9a36..000000000000 --- a/nemo_text_processing/text_normalization/ru/verbalizers/date.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst -from nemo_text_processing.text_normalization.ru.alphabet import RU_ALPHA -from pynini.lib import pynutil - - -class DateFst(GraphFst): - """ - Finite state transducer for verbalizing date, e.g. - tokens { date { day: "первое мая" } } -> "первое мая" - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="date", kind="verbalize", deterministic=deterministic) - - graph = pynutil.delete("day: \"") + pynini.closure(RU_ALPHA | " ", 1) + pynutil.delete("\"") - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/ru/verbalizers/decimal.py b/nemo_text_processing/text_normalization/ru/verbalizers/decimal.py deleted file mode 100644 index c3006fe3d535..000000000000 --- a/nemo_text_processing/text_normalization/ru/verbalizers/decimal.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class DecimalFst(GraphFst): - """ - Finite state transducer for verbalizing decimal, e.g. - tokens { decimal { integer_part: "одно целая" fractional_part: "восемь сотых} } -> - "одно целая восемь сотых" - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="decimal", kind="verbalize", deterministic=deterministic) - - optional_sign = pynini.closure(pynini.cross("negative: \"true\" ", "минус "), 0, 1) - integer = pynutil.delete(" \"") + pynini.closure(NEMO_NOT_QUOTE, 1) + pynutil.delete("\"") - integer_part = pynutil.delete("integer_part:") + integer - fractional_part = pynutil.delete("fractional_part:") + integer - optional_quantity_part = pynini.closure( - pynini.accep(" ") - + pynutil.delete("quantity: \"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\""), - 0, - 1, - ) - - self.graph = ( - optional_sign + integer_part + pynini.accep(" ") + fractional_part + optional_quantity_part + delete_space - ) - delete_tokens = self.delete_tokens(self.graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/ru/verbalizers/electronic.py b/nemo_text_processing/text_normalization/ru/verbalizers/electronic.py deleted file mode 100644 index 5bbfe008d6c5..000000000000 --- a/nemo_text_processing/text_normalization/ru/verbalizers/electronic.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst -from nemo_text_processing.text_normalization.ru.alphabet import RU_ALPHA -from pynini.lib import pynutil - - -class ElectronicFst(GraphFst): - """ - Finite state transducer for verbalizing electronic - e.g. electronic { username: "эй би собака эн ди точка ру" } -> "эй би собака эн ди точка ру" - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="electronic", kind="verbalize", deterministic=deterministic) - - graph = pynutil.delete("username: \"") + pynini.closure(RU_ALPHA | " ") + pynutil.delete("\"") - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/ru/verbalizers/measure.py b/nemo_text_processing/text_normalization/ru/verbalizers/measure.py deleted file mode 100644 index 27c95b9ee71e..000000000000 --- a/nemo_text_processing/text_normalization/ru/verbalizers/measure.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - NEMO_NON_BREAKING_SPACE, - NEMO_SPACE, - GraphFst, - delete_space, -) -from nemo_text_processing.text_normalization.ru.alphabet import RU_ALPHA -from pynini.lib import pynutil - - -class MeasureFst(GraphFst): - """ - Finite state transducer for verbalizing measure, e.g. - measure { cardinal { integer: "два килограма" } } -> "два килограма" - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="measure", kind="verbalize", deterministic=deterministic) - - graph = ( - pynutil.delete(" cardinal { integer: \"") - + pynini.closure(RU_ALPHA | NEMO_SPACE | NEMO_NON_BREAKING_SPACE) - + pynutil.delete("\"") - + delete_space - + pynutil.delete("}") - ) - - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/ru/verbalizers/money.py b/nemo_text_processing/text_normalization/ru/verbalizers/money.py deleted file mode 100644 index 3f5a5e9365e1..000000000000 --- a/nemo_text_processing/text_normalization/ru/verbalizers/money.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst -from nemo_text_processing.text_normalization.ru.alphabet import RU_ALPHA -from pynini.lib import pynutil - - -class MoneyFst(GraphFst): - """ - Finite state transducer for verbalizing money, e.g. - money { "пять рублей" } -> пять рублей - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="money", kind="verbalize", deterministic=deterministic) - - graph = pynini.closure(RU_ALPHA | " ") - delete_tokens = self.delete_tokens(pynutil.delete("integer_part: \"") + graph + pynutil.delete("\"")) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/ru/verbalizers/ordinal.py b/nemo_text_processing/text_normalization/ru/verbalizers/ordinal.py deleted file mode 100644 index 00700d9ec984..000000000000 --- a/nemo_text_processing/text_normalization/ru/verbalizers/ordinal.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst -from pynini.lib import pynutil - - -class OrdinalFst(GraphFst): - """ - Finite state transducer for verbalizing roman numerals - e.g. ordinal { integer: "второе" } } -> "второе" - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="ordinal", kind="verbalize", deterministic=deterministic) - - value = pynini.closure(NEMO_NOT_QUOTE) - graph = pynutil.delete("integer: \"") + value + pynutil.delete("\"") - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/ru/verbalizers/telephone.py b/nemo_text_processing/text_normalization/ru/verbalizers/telephone.py deleted file mode 100644 index bbd2d29f0e5e..000000000000 --- a/nemo_text_processing/text_normalization/ru/verbalizers/telephone.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst -from nemo_text_processing.text_normalization.ru.alphabet import RU_ALPHA -from pynini.lib import pynutil - - -class TelephoneFst(GraphFst): - """ - Finite state transducer for verbalizing telephone, e.g. - telephone { number_part: "восемь девятьсот тринадцать девятьсот восемьдесят три пятьдесят шесть ноль один" } -> "восемь девятьсот тринадцать девятьсот восемьдесят три пятьдесят шесть ноль один" - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="telephone", kind="verbalize", deterministic=deterministic) - - graph = pynutil.delete("number_part: \"") + pynini.closure(RU_ALPHA | " ", 1) + pynutil.delete("\"") - delete_tokens = self.delete_tokens(graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/ru/verbalizers/time.py b/nemo_text_processing/text_normalization/ru/verbalizers/time.py deleted file mode 100644 index 4f72879d2d48..000000000000 --- a/nemo_text_processing/text_normalization/ru/verbalizers/time.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space, insert_space -from pynini.lib import pynutil - - -class TimeFst(GraphFst): - """ - Finite state transducer for verbalizing electronic - e.g. time { hours: "два часа пятнадцать минут" } -> "два часа пятнадцать минут" - - Args: - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="time", kind="verbalize", deterministic=deterministic) - - hour = ( - pynutil.delete("hours:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - minutes = ( - pynutil.delete("minutes:") - + delete_space - + pynutil.delete("\"") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - ) - - self.graph = ( - hour + delete_space + insert_space + minutes + delete_space + pynutil.delete("preserve_order: true") - ) - self.graph |= hour + delete_space - self.graph |= minutes + delete_space + insert_space + hour + delete_space - - delete_tokens = self.delete_tokens(self.graph) - self.fst = delete_tokens.optimize() diff --git a/nemo_text_processing/text_normalization/ru/verbalizers/verbalize.py b/nemo_text_processing/text_normalization/ru/verbalizers/verbalize.py deleted file mode 100644 index 0780c855b438..000000000000 --- a/nemo_text_processing/text_normalization/ru/verbalizers/verbalize.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from nemo_text_processing.text_normalization.en.graph_utils import GraphFst -from nemo_text_processing.text_normalization.en.verbalizers.whitelist import WhiteListFst -from nemo_text_processing.text_normalization.ru.verbalizers.cardinal import CardinalFst -from nemo_text_processing.text_normalization.ru.verbalizers.date import DateFst -from nemo_text_processing.text_normalization.ru.verbalizers.decimal import DecimalFst -from nemo_text_processing.text_normalization.ru.verbalizers.electronic import ElectronicFst -from nemo_text_processing.text_normalization.ru.verbalizers.measure import MeasureFst -from nemo_text_processing.text_normalization.ru.verbalizers.money import MoneyFst -from nemo_text_processing.text_normalization.ru.verbalizers.ordinal import OrdinalFst -from nemo_text_processing.text_normalization.ru.verbalizers.telephone import TelephoneFst -from nemo_text_processing.text_normalization.ru.verbalizers.time import TimeFst - - -class VerbalizeFst(GraphFst): - """ - Composes other verbalizer grammars. - For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - - Args: - deterministic: if True will provide a single transduction option, - for False multiple options (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="verbalize", kind="verbalize", deterministic=deterministic) - cardinal = CardinalFst() - cardinal_graph = cardinal.fst - ordinal_graph = OrdinalFst().fst - decimal = DecimalFst() - decimal_graph = decimal.fst - date = DateFst() - date_graph = date.fst - measure = MeasureFst() - measure_graph = measure.fst - electronic = ElectronicFst() - electronic_graph = electronic.fst - whitelist_graph = WhiteListFst().fst - money_graph = MoneyFst().fst - telephone_graph = TelephoneFst().fst - time_graph = TimeFst().fst - - graph = ( - measure_graph - | cardinal_graph - | decimal_graph - | ordinal_graph - | date_graph - | electronic_graph - | money_graph - | whitelist_graph - | telephone_graph - | time_graph - ) - self.fst = graph diff --git a/nemo_text_processing/text_normalization/ru/verbalizers/verbalize_final.py b/nemo_text_processing/text_normalization/ru/verbalizers/verbalize_final.py deleted file mode 100644 index 9c0c5f2eab68..000000000000 --- a/nemo_text_processing/text_normalization/ru/verbalizers/verbalize_final.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import ( - GraphFst, - delete_extra_space, - delete_space, - generator_main, -) -from nemo_text_processing.text_normalization.en.verbalizers.word import WordFst -from nemo_text_processing.text_normalization.ru.verbalizers.verbalize import VerbalizeFst -from pynini.lib import pynutil - -from nemo.utils import logging - - -class VerbalizeFinalFst(GraphFst): - """ - Finite state transducer that verbalizes an entire sentence, e.g. - tokens { name: "its" } tokens { time { hours: "12" minutes: "30" } } tokens { name: "now" } -> its 12:30 now - - Args: - deterministic: if True will provide a single transduction option, - for False multiple options (used for audio-based normalization) - cache_dir: path to a dir with .far grammar file. Set to None to avoid using cache. - overwrite_cache: set to True to overwrite .far files - """ - - def __init__(self, deterministic: bool = True, cache_dir: str = None, overwrite_cache: bool = False): - super().__init__(name="verbalize_final", kind="verbalize", deterministic=deterministic) - - far_file = None - if cache_dir is not None and cache_dir != "None": - os.makedirs(cache_dir, exist_ok=True) - far_file = os.path.join(cache_dir, f"ru_tn_{deterministic}_deterministic_verbalizer.far") - if not overwrite_cache and far_file and os.path.exists(far_file): - self.fst = pynini.Far(far_file, mode="r")["verbalize"] - logging.info(f'VerbalizeFinalFst graph was restored from {far_file}.') - else: - - verbalize = VerbalizeFst().fst - word = WordFst().fst - types = verbalize | word - graph = ( - pynutil.delete("tokens") - + delete_space - + pynutil.delete("{") - + delete_space - + types - + delete_space - + pynutil.delete("}") - ) - graph = delete_space + pynini.closure(graph + delete_extra_space) + graph + delete_space - self.fst = graph.optimize() - - if far_file: - generator_main(far_file, {"verbalize": self.fst}) - logging.info(f"VerbalizeFinalFst grammars are saved to {far_file}.") diff --git a/nemo_text_processing/text_normalization/run_evaluate.py b/nemo_text_processing/text_normalization/run_evaluate.py deleted file mode 100644 index 5f23dbd7f8af..000000000000 --- a/nemo_text_processing/text_normalization/run_evaluate.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from argparse import ArgumentParser - -from nemo_text_processing.text_normalization.data_loader_utils import ( - evaluate, - known_types, - load_files, - training_data_to_sentences, - training_data_to_tokens, -) -from nemo_text_processing.text_normalization.normalize import Normalizer - - -''' -Runs Evaluation on data in the format of : \t\t<`self` if trivial class or normalized text> -like the Google text normalization data https://www.kaggle.com/richardwilliamsproat/text-normalization-for-english-russian-and-polish -''' - - -def parse_args(): - parser = ArgumentParser() - parser.add_argument("--input", help="input file path", type=str) - parser.add_argument("--lang", help="language", choices=['en'], default="en", type=str) - parser.add_argument( - "--input_case", help="input capitalization", choices=["lower_cased", "cased"], default="cased", type=str - ) - parser.add_argument( - "--cat", - dest="category", - help="focus on class only (" + ", ".join(known_types) + ")", - type=str, - default=None, - choices=known_types, - ) - parser.add_argument("--filter", action='store_true', help="clean data for normalization purposes") - return parser.parse_args() - - -if __name__ == "__main__": - # Example usage: - # python run_evaluate.py --input= --cat= --filter - args = parse_args() - if args.lang == 'en': - from nemo_text_processing.text_normalization.en.clean_eval_data import filter_loaded_data - file_path = args.input - normalizer = Normalizer(input_case=args.input_case, lang=args.lang) - - print("Loading training data: " + file_path) - training_data = load_files([file_path]) - - if args.filter: - training_data = filter_loaded_data(training_data) - - if args.category is None: - print("Sentence level evaluation...") - sentences_un_normalized, sentences_normalized, _ = training_data_to_sentences(training_data) - print("- Data: " + str(len(sentences_normalized)) + " sentences") - sentences_prediction = normalizer.normalize_list(sentences_un_normalized) - print("- Normalized. Evaluating...") - sentences_accuracy = evaluate( - preds=sentences_prediction, labels=sentences_normalized, input=sentences_un_normalized - ) - print("- Accuracy: " + str(sentences_accuracy)) - - print("Token level evaluation...") - tokens_per_type = training_data_to_tokens(training_data, category=args.category) - token_accuracy = {} - for token_type in tokens_per_type: - print("- Token type: " + token_type) - tokens_un_normalized, tokens_normalized = tokens_per_type[token_type] - print(" - Data: " + str(len(tokens_normalized)) + " tokens") - tokens_prediction = normalizer.normalize_list(tokens_un_normalized) - print(" - Denormalized. Evaluating...") - token_accuracy[token_type] = evaluate( - preds=tokens_prediction, labels=tokens_normalized, input=tokens_un_normalized - ) - print(" - Accuracy: " + str(token_accuracy[token_type])) - token_count_per_type = {token_type: len(tokens_per_type[token_type][0]) for token_type in tokens_per_type} - token_weighted_accuracy = [ - token_count_per_type[token_type] * accuracy for token_type, accuracy in token_accuracy.items() - ] - print("- Accuracy: " + str(sum(token_weighted_accuracy) / sum(token_count_per_type.values()))) - print(" - Total: " + str(sum(token_count_per_type.values())), '\n') - - print(" - Total: " + str(sum(token_count_per_type.values())), '\n') - - for token_type in token_accuracy: - if token_type not in known_types: - raise ValueError("Unexpected token type: " + token_type) - - if args.category is None: - c1 = ['Class', 'sent level'] + known_types - c2 = ['Num Tokens', len(sentences_normalized)] + [ - token_count_per_type[known_type] if known_type in tokens_per_type else '0' for known_type in known_types - ] - c3 = ['Normalization', sentences_accuracy] + [ - token_accuracy[known_type] if known_type in token_accuracy else '0' for known_type in known_types - ] - - for i in range(len(c1)): - print(f'{str(c1[i]):10s} | {str(c2[i]):10s} | {str(c3[i]):5s}') - else: - print(f'numbers\t{token_count_per_type[args.category]}') - print(f'Normalization\t{token_accuracy[args.category]}') diff --git a/nemo_text_processing/text_normalization/token_parser.py b/nemo_text_processing/text_normalization/token_parser.py deleted file mode 100644 index 638b71bbf142..000000000000 --- a/nemo_text_processing/text_normalization/token_parser.py +++ /dev/null @@ -1,193 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import string -from collections import OrderedDict -from typing import Dict, List, Union - -PRESERVE_ORDER_KEY = "preserve_order" -EOS = "" - - -class TokenParser: - """ - Parses tokenized/classified text, e.g. 'tokens { money { integer: "20" currency: "$" } } tokens { name: "left"}' - - Args - text: tokenized text - """ - - def __call__(self, text): - """ - Setup function - - Args: - text: text to be parsed - - """ - self.text = text - self.len_text = len(text) - self.char = text[0] # cannot handle empty string - self.index = 0 - - def parse(self) -> List[dict]: - """ - Main function. Implements grammar: - A -> space F space F space F ... space - - Returns list of dictionaries - """ - l = list() - while self.parse_ws(): - token = self.parse_token() - if not token: - break - l.append(token) - return l - - def parse_token(self) -> Dict[str, Union[str, dict]]: - """ - Implements grammar: - F-> no_space KG no_space - - Returns: K, G as dictionary values - """ - d = OrderedDict() - key = self.parse_string_key() - if key is None: - return None - self.parse_ws() - if key == PRESERVE_ORDER_KEY: - self.parse_char(":") - self.parse_ws() - value = self.parse_chars("true") - else: - value = self.parse_token_value() - - d[key] = value - return d - - def parse_token_value(self) -> Union[str, dict]: - """ - Implements grammar: - G-> no_space :"VALUE" no_space | no_space {A} no_space - - Returns: string or dictionary - """ - if self.char == ":": - self.parse_char(":") - self.parse_ws() - self.parse_char("\"") - value_string = self.parse_string_value() - self.parse_char("\"") - return value_string - elif self.char == "{": - d = OrderedDict() - self.parse_char("{") - list_token_dicts = self.parse() - # flatten tokens - for tok_dict in list_token_dicts: - for k, v in tok_dict.items(): - d[k] = v - self.parse_char("}") - return d - else: - raise ValueError() - - def parse_char(self, exp) -> bool: - """ - Parses character - - Args: - exp: character to read in - - Returns true if successful - """ - assert self.char == exp - self.read() - return True - - def parse_chars(self, exp) -> bool: - """ - Parses characters - - Args: - exp: characters to read in - - Returns true if successful - """ - ok = False - for x in exp: - ok |= self.parse_char(x) - return ok - - def parse_string_key(self) -> str: - """ - Parses string key, can only contain ascii and '_' characters - - Returns parsed string key - """ - assert self.char not in string.whitespace and self.char != EOS - incl_criterium = string.ascii_letters + "_" - l = [] - while self.char in incl_criterium: - l.append(self.char) - if not self.read(): - raise ValueError() - - if not l: - return None - return "".join(l) - - def parse_string_value(self) -> str: - """ - Parses string value, ends with quote followed by space - - Returns parsed string value - """ - # assert self.char not in string.whitespace and self.char != EOS - assert self.char != EOS - l = [] - while self.char != "\"" or self.text[self.index + 1] != " ": - l.append(self.char) - if not self.read(): - raise ValueError() - - if not l: - return None - return "".join(l) - - def parse_ws(self): - """ - Deletes whitespaces. - - Returns true if not EOS after parsing - """ - not_eos = self.char != EOS - while not_eos and self.char == " ": - not_eos = self.read() - return not_eos - - def read(self): - """ - Reads in next char. - - Returns true if not EOS - """ - if self.index < self.len_text - 1: # should be unique - self.index += 1 - self.char = self.text[self.index] - return True - self.char = EOS - return False diff --git a/nemo_text_processing/text_normalization/zh/README.md b/nemo_text_processing/text_normalization/zh/README.md deleted file mode 100644 index 0eedd574d839..000000000000 --- a/nemo_text_processing/text_normalization/zh/README.md +++ /dev/null @@ -1,138 +0,0 @@ -# Chinese Text Normalization - -## 1. How To Use -``` -python normalize.py --language "zh" --text "text to be normalized" -``` - -## 2. TN Pipeline -There are 3 components in TN pipeline: -* pre-processing (before tagger) -* non-standard word normalization -* post-processing (after verbalizer) - -### 2.1 Pre-Processing -#### Char Width Conversion (全角 -> 半角) -``` -苹果CEO宣布发布新IPHONE -> 苹果CEO宣布发布新IPHONE -他说:“我们已经吃过了!”。 -> 他说:"我们已经吃过了!". -``` -* covers English letters, digits, punctuations and some symbols -* the complete mapping table `data/char/fullwidth_to_halfwidth.tsv` - -#### Denylist (Removal) -Sometime you may want to remove certain things like interjections/fillers "啊", "呃" etc -``` -呃这个呃啊我不知道 -> 这个我不知道 -``` -* customizable via `data/denylist/denylist.tsv` - - -### 2.2 Non-Standard-Words(NSW) normalization -#### Numbers -``` -共465篇,约315万字 -> 共四百六十五篇,约三百一十五万字 -共计6.42万人 -> 共计六点四二万人 -同比升高0.6个百分点 -> 同比升高零点六个百分点 -``` - -#### Fraction -``` -总量的1/5以上 -> 总量的五分之一以上 -相当于头发丝的1/16 -> 相当于头发丝的十六分之一 -3/2是一个假分数 -> 二分之三是一个假分数 -``` - -#### Percentage -``` -同比增长6.3% -> 同比增长百分之六点三 -增幅0.4% -> 增幅百分之零点四 -``` - -#### Date -``` -2002/01/28 -> 二零零二年一月二十八日 -2002-01-28 -> 二零零二年一月二十八日 -2002.01.28 -> 二零零二年一月二十八日 -2002/01 -> 二零零二年一月 -``` - -#### Time -``` -8月16号12:00之前 -> 八月十六号十二点之前 -我是5:02开始的 -> 我是五点零二分开始的 -于5:35:36发射 -> 于五点三十五分三十六秒发射 -8:00am准时开会 -> 上午八点准时开会 -``` - -#### Math -``` -比分定格在78:96 -> 比分定格在七十八比九十六 -计算-2的绝对值是2 -> 计算负二的绝对值是二 -±2的平方都是4 -> 正负二的平方都是四 -``` - -#### Money -``` -价格是¥13.5 -> 价格是十三点五元 -价格是$13.5 -> 价格是十三点五美元 -价格是A$13.5 -> 价格是十三点五澳元 -价格是HKD13.5 -> 价格是十三点五港元 -``` - -#### Measure -``` -重达25kg -> 二十五千克 -最高气温38°C -> 最高气温三十八摄氏度 -实际面积120m² -> 实际面积一百二十平方米 -渲染速度10ms一帧 -> 渲染速度十毫秒一帧 -``` - -#### Number series (phone, mobile numbers) -``` -可以打我手机13501234567 -> 可以打我手机一三五零一二三四五六七 -可以拨打12306来咨询 -> 可以拨打一二三零六来咨询 -``` - -#### Erhua(儿化音) Removal -``` -这儿有只鸟儿 -> 这有只鸟 -这事儿好办 -> 这事好办 -我儿子喜欢这地儿 -> 我儿子喜欢这地 -``` -* erhua whitelist is customizable via `data/erhua/whitelist.tsv` - -#### Whitelist(Replacement) -a set of user-defined hard mapping, i.e. exact-string matching & replacement -``` -C E O -> CEO -G P U -> GPU -O2O -> O to O -B2B -> B to B -``` -* customizable via `data/whitelist/default.tsv` - -### 2.3 Post-Processing -#### Punctuation Removal -If enabled, punctuations are removed. - -#### Uppercase or Lowercase Conversion -If enabled, English letters are converted to uppercases / lowercases - -#### Out-Of-Vocabulary(OOV) Tagger -If enabled, OOV chars are tagged with `` and ``, e.g.: -``` -我们안녕 -> 我们 -雪の花 -> 雪花 -``` -* default charset (national standard) [通用规范汉字表](https://zh.wikipedia.org/wiki/通用规范汉字表) -* you can extend charset via `data/char/charset_extension.tsv` - -## 3. Credits -Author: Zhenxiang MA @ Tsinghua University - -Advisors: [SpeechColab](https://github.com/SpeechColab) organization - -The authors of this work would like to thank: -* The authors of foundational libraries like OpenFst & Pynini -* NeMo team and NeMo open-source community diff --git a/nemo_text_processing/text_normalization/zh/__init__.py b/nemo_text_processing/text_normalization/zh/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/zh/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/zh/data/__init__.py b/nemo_text_processing/text_normalization/zh/data/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/zh/data/char/__init__.py b/nemo_text_processing/text_normalization/zh/data/char/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/char/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/zh/data/char/charset_extension.tsv b/nemo_text_processing/text_normalization/zh/data/char/charset_extension.tsv deleted file mode 100644 index de3ac0a83e76..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/char/charset_extension.tsv +++ /dev/null @@ -1,6 +0,0 @@ -吶 -诶 -屌 -囧 -飚 -屄 diff --git a/nemo_text_processing/text_normalization/zh/data/char/charset_national_standard_2013_8105.tsv b/nemo_text_processing/text_normalization/zh/data/char/charset_national_standard_2013_8105.tsv deleted file mode 100644 index 06b46c630588..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/char/charset_national_standard_2013_8105.tsv +++ /dev/null @@ -1,8105 +0,0 @@ -一 -丁 -七 -万 -丈 -三 -上 -下 -不 -与 -丏 -丐 -丑 -专 -且 -丕 -世 -丘 -丙 -业 -丛 -东 -丝 -丞 -丢 -两 -严 -丧 -个 -丫 -中 -丰 -串 -临 -丸 -丹 -为 -主 -丽 -举 -乂 -乃 -久 -么 -义 -之 -乌 -乍 -乎 -乏 -乐 -乒 -乓 -乔 -乖 -乘 -乙 -乜 -九 -乞 -也 -习 -乡 -书 -乩 -买 -乱 -乳 -乸 -乾 -了 -予 -争 -事 -二 -亍 -于 -亏 -云 -互 -亓 -五 -井 -亘 -亚 -些 -亟 -亡 -亢 -交 -亥 -亦 -产 -亨 -亩 -享 -京 -亭 -亮 -亲 -亳 -亵 -亶 -亸 -亹 -人 -亿 -什 -仁 -仂 -仃 -仄 -仅 -仆 -仇 -仉 -今 -介 -仍 -从 -仑 -仓 -仔 -仕 -他 -仗 -付 -仙 -仝 -仞 -仟 -仡 -代 -令 -以 -仨 -仪 -仫 -们 -仰 -仲 -仳 -仵 -件 -价 -任 -份 -仿 -企 -伈 -伉 -伊 -伋 -伍 -伎 -伏 -伐 -休 -众 -优 -伙 -会 -伛 -伞 -伟 -传 -伢 -伣 -伤 -伥 -伦 -伧 -伪 -伫 -伭 -伯 -估 -伲 -伴 -伶 -伸 -伺 -似 -伽 -伾 -佁 -佃 -但 -位 -低 -住 -佐 -佑 -体 -何 -佖 -佗 -佘 -余 -佚 -佛 -作 -佝 -佞 -佟 -你 -佣 -佤 -佥 -佩 -佬 -佯 -佰 -佳 -佴 -佶 -佸 -佺 -佻 -佼 -佽 -佾 -使 -侁 -侂 -侃 -侄 -侈 -侉 -例 -侍 -侏 -侑 -侔 -侗 -侘 -供 -依 -侠 -侣 -侥 -侦 -侧 -侨 -侩 -侪 -侬 -侮 -侯 -侴 -侵 -侹 -便 -促 -俄 -俅 -俊 -俍 -俎 -俏 -俐 -俑 -俗 -俘 -俙 -俚 -俜 -保 -俞 -俟 -信 -俣 -俦 -俨 -俩 -俪 -俫 -俭 -修 -俯 -俱 -俳 -俵 -俶 -俸 -俺 -俾 -倌 -倍 -倏 -倒 -倓 -倔 -倕 -倘 -候 -倚 -倜 -倞 -借 -倡 -倥 -倦 -倧 -倨 -倩 -倪 -倬 -倭 -倮 -倴 -债 -倻 -值 -倾 -偁 -偃 -假 -偈 -偌 -偎 -偏 -偓 -偕 -做 -停 -偡 -健 -偬 -偭 -偰 -偲 -偶 -偷 -偻 -偾 -偿 -傀 -傃 -傅 -傈 -傉 -傍 -傒 -傕 -傣 -傥 -傧 -储 -傩 -催 -傲 -傺 -傻 -僇 -僎 -像 -僔 -僖 -僚 -僦 -僧 -僬 -僭 -僮 -僰 -僳 -僵 -僻 -儆 -儇 -儋 -儒 -儡 -儦 -儳 -儴 -儿 -兀 -允 -元 -兄 -充 -兆 -先 -光 -克 -免 -兑 -兔 -兕 -兖 -党 -兜 -兢 -入 -全 -八 -公 -六 -兮 -兰 -共 -关 -兴 -兵 -其 -具 -典 -兹 -养 -兼 -兽 -冀 -冁 -内 -冈 -冉 -册 -再 -冏 -冒 -冔 -冕 -冗 -写 -军 -农 -冠 -冢 -冤 -冥 -冬 -冮 -冯 -冰 -冱 -冲 -决 -况 -冶 -冷 -冻 -冼 -冽 -净 -凄 -准 -凇 -凉 -凋 -凌 -减 -凑 -凓 -凘 -凛 -凝 -几 -凡 -凤 -凫 -凭 -凯 -凰 -凳 -凶 -凸 -凹 -出 -击 -凼 -函 -凿 -刀 -刁 -刃 -分 -切 -刈 -刊 -刍 -刎 -刑 -划 -刖 -列 -刘 -则 -刚 -创 -初 -删 -判 -刨 -利 -别 -刬 -刭 -刮 -到 -刳 -制 -刷 -券 -刹 -刺 -刻 -刽 -刿 -剀 -剁 -剂 -剃 -剅 -削 -剋 -剌 -前 -剐 -剑 -剔 -剕 -剖 -剜 -剞 -剟 -剡 -剥 -剧 -剩 -剪 -副 -割 -剽 -剿 -劁 -劂 -劄 -劈 -劐 -劓 -力 -劝 -办 -功 -加 -务 -劢 -劣 -动 -助 -努 -劫 -劬 -劭 -励 -劲 -劳 -劼 -劾 -势 -勃 -勇 -勉 -勋 -勍 -勐 -勒 -勔 -勖 -勘 -勚 -募 -勠 -勤 -勰 -勺 -勾 -勿 -匀 -包 -匆 -匈 -匍 -匏 -匐 -匕 -化 -北 -匙 -匜 -匝 -匠 -匡 -匣 -匦 -匪 -匮 -匹 -区 -医 -匼 -匾 -匿 -十 -千 -卅 -升 -午 -卉 -半 -华 -协 -卑 -卒 -卓 -单 -卖 -南 -博 -卜 -卞 -卟 -占 -卡 -卢 -卣 -卤 -卦 -卧 -卫 -卬 -卮 -卯 -印 -危 -即 -却 -卵 -卷 -卸 -卺 -卿 -厂 -厄 -厅 -历 -厉 -压 -厌 -厍 -厕 -厖 -厘 -厚 -厝 -原 -厢 -厣 -厥 -厦 -厨 -厩 -厮 -去 -厾 -县 -叁 -参 -叆 -叇 -又 -叉 -及 -友 -双 -反 -发 -叔 -叕 -取 -受 -变 -叙 -叚 -叛 -叟 -叠 -口 -古 -句 -另 -叨 -叩 -只 -叫 -召 -叭 -叮 -可 -台 -叱 -史 -右 -叵 -叶 -号 -司 -叹 -叻 -叼 -叽 -吁 -吃 -各 -吆 -合 -吉 -吊 -同 -名 -后 -吏 -吐 -向 -吒 -吓 -吕 -吖 -吗 -君 -吝 -吞 -吟 -吠 -吡 -吣 -否 -吧 -吨 -吩 -含 -听 -吭 -吮 -启 -吱 -吲 -吴 -吵 -吸 -吹 -吻 -吼 -吽 -吾 -呀 -呃 -呆 -呇 -呈 -告 -呋 -呐 -呒 -呓 -呔 -呕 -呖 -呗 -员 -呙 -呛 -呜 -呢 -呣 -呤 -呦 -周 -呱 -呲 -味 -呵 -呶 -呷 -呸 -呻 -呼 -命 -咀 -咂 -咄 -咆 -咇 -咉 -咋 -和 -咍 -咎 -咏 -咐 -咒 -咔 -咕 -咖 -咙 -咚 -咛 -咝 -咡 -咣 -咤 -咥 -咦 -咧 -咨 -咩 -咪 -咫 -咬 -咯 -咱 -咳 -咴 -咸 -咺 -咻 -咽 -咿 -哀 -品 -哂 -哃 -哄 -哆 -哇 -哈 -哉 -哌 -响 -哎 -哏 -哐 -哑 -哒 -哓 -哔 -哕 -哗 -哙 -哚 -哝 -哞 -哟 -哢 -哥 -哦 -哧 -哨 -哩 -哪 -哭 -哮 -哱 -哲 -哳 -哺 -哼 -哽 -哿 -唁 -唆 -唇 -唉 -唏 -唐 -唑 -唔 -唛 -唝 -唠 -唢 -唣 -唤 -唧 -唪 -唬 -售 -唯 -唰 -唱 -唳 -唵 -唷 -唼 -唾 -唿 -啁 -啃 -啄 -商 -啉 -啊 -啐 -啕 -啖 -啜 -啡 -啤 -啥 -啦 -啧 -啪 -啫 -啬 -啭 -啮 -啰 -啴 -啵 -啶 -啷 -啸 -啻 -啼 -啾 -喀 -喁 -喂 -喃 -善 -喆 -喇 -喈 -喉 -喊 -喋 -喏 -喑 -喔 -喘 -喙 -喜 -喝 -喟 -喤 -喧 -喱 -喳 -喵 -喷 -喹 -喻 -喽 -喾 -嗄 -嗅 -嗉 -嗌 -嗍 -嗐 -嗑 -嗒 -嗓 -嗔 -嗖 -嗜 -嗝 -嗞 -嗟 -嗡 -嗣 -嗤 -嗥 -嗦 -嗨 -嗪 -嗫 -嗬 -嗯 -嗲 -嗳 -嗵 -嗷 -嗽 -嗾 -嘀 -嘁 -嘈 -嘉 -嘌 -嘎 -嘏 -嘘 -嘚 -嘛 -嘞 -嘟 -嘡 -嘣 -嘤 -嘧 -嘬 -嘭 -嘱 -嘲 -嘴 -嘶 -嘹 -嘻 -嘿 -噀 -噂 -噇 -噌 -噍 -噎 -噔 -噗 -噘 -噙 -噜 -噢 -噤 -器 -噩 -噪 -噫 -噬 -噱 -噶 -噻 -噼 -嚄 -嚅 -嚆 -嚎 -嚏 -嚓 -嚚 -嚣 -嚭 -嚯 -嚷 -嚼 -囊 -囔 -囚 -四 -回 -囟 -因 -囡 -团 -囤 -囫 -园 -困 -囱 -围 -囵 -囷 -囹 -固 -国 -图 -囿 -圃 -圄 -圆 -圈 -圉 -圊 -圌 -圐 -圙 -圜 -土 -圢 -圣 -在 -圩 -圪 -圫 -圬 -圭 -圮 -圯 -地 -圲 -圳 -圹 -场 -圻 -圾 -址 -坂 -均 -坉 -坊 -坋 -坌 -坍 -坎 -坏 -坐 -坑 -坒 -块 -坚 -坛 -坜 -坝 -坞 -坟 -坠 -坡 -坤 -坥 -坦 -坨 -坩 -坪 -坫 -坬 -坭 -坯 -坰 -坳 -坷 -坻 -坼 -坽 -垂 -垃 -垄 -垆 -垈 -型 -垌 -垍 -垎 -垏 -垒 -垓 -垕 -垙 -垚 -垛 -垞 -垟 -垠 -垡 -垢 -垣 -垤 -垦 -垧 -垩 -垫 -垭 -垮 -垯 -垱 -垲 -垴 -垵 -垸 -垺 -垾 -垿 -埂 -埃 -埆 -埇 -埋 -埌 -城 -埏 -埒 -埔 -埕 -埗 -埘 -埙 -埚 -埝 -域 -埠 -埤 -埪 -埫 -埭 -埯 -埴 -埵 -埸 -培 -基 -埼 -埽 -堂 -堃 -堆 -堇 -堉 -堋 -堌 -堍 -堎 -堐 -堑 -堕 -堙 -堞 -堠 -堡 -堤 -堧 -堨 -堪 -堰 -堲 -堵 -堼 -堽 -堾 -塄 -塅 -塆 -塌 -塍 -塑 -塔 -塘 -塝 -塞 -塥 -填 -塬 -塱 -塾 -墀 -墁 -境 -墅 -墈 -墉 -墐 -墒 -墓 -墕 -墘 -墙 -墚 -增 -墟 -墡 -墣 -墦 -墨 -墩 -墼 -壁 -壅 -壑 -壕 -壤 -士 -壬 -壮 -声 -壳 -壶 -壸 -壹 -处 -备 -复 -夏 -夐 -夔 -夕 -外 -夙 -多 -夜 -够 -夤 -夥 -大 -天 -太 -夫 -夬 -夭 -央 -夯 -失 -头 -夷 -夸 -夹 -夺 -夼 -奁 -奂 -奄 -奇 -奈 -奉 -奋 -奎 -奏 -契 -奓 -奔 -奕 -奖 -套 -奘 -奚 -奠 -奡 -奢 -奥 -奭 -女 -奴 -奶 -奸 -她 -好 -妁 -如 -妃 -妄 -妆 -妇 -妈 -妊 -妍 -妒 -妓 -妖 -妗 -妘 -妙 -妞 -妣 -妤 -妥 -妧 -妨 -妩 -妪 -妫 -妭 -妮 -妯 -妲 -妹 -妻 -妾 -姆 -姈 -姊 -始 -姐 -姑 -姒 -姓 -委 -姗 -姘 -姚 -姜 -姝 -姞 -姣 -姤 -姥 -姨 -姬 -姮 -姱 -姶 -姹 -姻 -姽 -姿 -娀 -威 -娃 -娄 -娅 -娆 -娇 -娈 -娉 -娌 -娑 -娓 -娘 -娜 -娟 -娠 -娣 -娥 -娩 -娱 -娲 -娴 -娵 -娶 -娼 -婀 -婆 -婉 -婊 -婌 -婍 -婕 -婘 -婚 -婞 -婠 -婢 -婤 -婧 -婪 -婫 -婳 -婴 -婵 -婶 -婷 -婺 -婻 -婼 -婿 -媂 -媄 -媆 -媒 -媓 -媖 -媚 -媛 -媞 -媪 -媭 -媱 -媲 -媳 -媵 -媸 -媾 -嫁 -嫂 -嫄 -嫉 -嫌 -嫒 -嫔 -嫕 -嫖 -嫘 -嫚 -嫜 -嫠 -嫡 -嫣 -嫦 -嫩 -嫪 -嫫 -嫭 -嫱 -嫽 -嬉 -嬖 -嬗 -嬛 -嬥 -嬬 -嬴 -嬷 -嬿 -孀 -孅 -子 -孑 -孓 -孔 -孕 -孖 -字 -存 -孙 -孚 -孛 -孜 -孝 -孟 -孢 -季 -孤 -孥 -学 -孩 -孪 -孬 -孰 -孱 -孳 -孵 -孺 -孽 -宁 -它 -宄 -宅 -宇 -守 -安 -宋 -完 -宏 -宓 -宕 -宗 -官 -宙 -定 -宛 -宜 -宝 -实 -宠 -审 -客 -宣 -室 -宥 -宦 -宧 -宪 -宫 -宬 -宰 -害 -宴 -宵 -家 -宸 -容 -宽 -宾 -宿 -寁 -寂 -寄 -寅 -密 -寇 -富 -寐 -寒 -寓 -寝 -寞 -察 -寡 -寤 -寥 -寨 -寮 -寰 -寸 -对 -寺 -寻 -导 -寿 -封 -射 -将 -尉 -尊 -小 -少 -尔 -尕 -尖 -尘 -尚 -尜 -尝 -尢 -尤 -尥 -尧 -尨 -尪 -尬 -就 -尴 -尸 -尹 -尺 -尻 -尼 -尽 -尾 -尿 -局 -屁 -层 -屃 -居 -屈 -屉 -届 -屋 -屎 -屏 -屐 -屑 -展 -屙 -属 -屠 -屡 -屣 -履 -屦 -屯 -山 -屹 -屺 -屼 -屾 -屿 -岁 -岂 -岈 -岊 -岌 -岍 -岐 -岑 -岔 -岖 -岗 -岘 -岙 -岚 -岛 -岜 -岞 -岠 -岢 -岣 -岨 -岩 -岫 -岬 -岭 -岱 -岳 -岵 -岷 -岸 -岽 -岿 -峁 -峂 -峃 -峄 -峋 -峒 -峗 -峘 -峙 -峛 -峡 -峣 -峤 -峥 -峦 -峧 -峨 -峪 -峭 -峰 -峱 -峻 -峿 -崀 -崁 -崂 -崃 -崄 -崆 -崇 -崌 -崎 -崒 -崔 -崖 -崚 -崛 -崞 -崟 -崡 -崤 -崦 -崧 -崩 -崭 -崮 -崴 -崶 -崽 -崾 -崿 -嵁 -嵅 -嵇 -嵊 -嵋 -嵌 -嵎 -嵖 -嵘 -嵚 -嵛 -嵝 -嵩 -嵫 -嵬 -嵯 -嵲 -嵴 -嶂 -嶅 -嶍 -嶒 -嶓 -嶙 -嶝 -嶟 -嶦 -嶲 -嶷 -巅 -巇 -巉 -巍 -川 -州 -巡 -巢 -工 -左 -巧 -巨 -巩 -巫 -差 -巯 -己 -已 -巳 -巴 -巷 -巽 -巾 -币 -市 -布 -帅 -帆 -师 -希 -帏 -帐 -帑 -帔 -帕 -帖 -帘 -帙 -帚 -帛 -帜 -帝 -帡 -带 -帧 -帨 -席 -帮 -帱 -帷 -常 -帻 -帼 -帽 -幂 -幄 -幅 -幌 -幔 -幕 -幖 -幛 -幞 -幡 -幢 -幪 -干 -平 -年 -并 -幸 -幺 -幻 -幼 -幽 -广 -庄 -庆 -庇 -床 -庋 -序 -庐 -庑 -库 -应 -底 -庖 -店 -庙 -庚 -府 -庞 -废 -庠 -庤 -庥 -度 -座 -庭 -庱 -庳 -庵 -庶 -康 -庸 -庹 -庼 -庾 -廆 -廉 -廊 -廋 -廑 -廒 -廓 -廖 -廙 -廛 -廨 -廪 -延 -廷 -建 -廿 -开 -弁 -异 -弃 -弄 -弆 -弇 -弈 -弊 -弋 -式 -弑 -弓 -引 -弗 -弘 -弛 -弟 -张 -弢 -弥 -弦 -弧 -弨 -弩 -弭 -弯 -弱 -弶 -弸 -弹 -强 -弼 -彀 -归 -当 -录 -彖 -彗 -彘 -彝 -彟 -形 -彤 -彦 -彧 -彩 -彪 -彬 -彭 -彰 -影 -彳 -彷 -役 -彻 -彼 -往 -征 -徂 -径 -待 -徇 -很 -徉 -徊 -律 -徐 -徒 -徕 -得 -徘 -徙 -徛 -徜 -御 -徨 -循 -徭 -微 -徵 -德 -徼 -徽 -心 -必 -忆 -忉 -忌 -忍 -忏 -忐 -忑 -忒 -忖 -志 -忘 -忙 -忝 -忞 -忠 -忡 -忤 -忧 -忪 -快 -忭 -忮 -忱 -忳 -念 -忸 -忺 -忻 -忽 -忾 -忿 -怀 -态 -怂 -怃 -怄 -怅 -怆 -怊 -怍 -怎 -怏 -怒 -怔 -怕 -怖 -怙 -怛 -怜 -思 -怠 -怡 -急 -怦 -性 -怨 -怩 -怪 -怫 -怯 -怵 -总 -怼 -怿 -恁 -恂 -恃 -恋 -恍 -恐 -恒 -恓 -恔 -恕 -恙 -恚 -恝 -恢 -恣 -恤 -恧 -恨 -恩 -恪 -恫 -恬 -恭 -息 -恰 -恳 -恶 -恸 -恹 -恺 -恻 -恼 -恽 -恿 -悃 -悄 -悆 -悈 -悉 -悌 -悍 -悒 -悔 -悖 -悚 -悛 -悝 -悟 -悠 -悢 -患 -悦 -您 -悫 -悬 -悭 -悯 -悰 -悱 -悲 -悴 -悸 -悻 -悼 -情 -惆 -惇 -惊 -惋 -惎 -惑 -惔 -惕 -惘 -惙 -惚 -惛 -惜 -惝 -惟 -惠 -惦 -惧 -惨 -惩 -惫 -惬 -惭 -惮 -惯 -惰 -想 -惴 -惶 -惹 -惺 -愀 -愁 -愃 -愆 -愈 -愉 -愍 -愎 -意 -愐 -愔 -愕 -愚 -感 -愠 -愣 -愤 -愦 -愧 -愫 -愭 -愿 -慆 -慈 -慊 -慌 -慎 -慑 -慕 -慝 -慢 -慥 -慧 -慨 -慬 -慭 -慰 -慵 -慷 -憋 -憎 -憔 -憕 -憙 -憧 -憨 -憩 -憬 -憭 -憷 -憺 -憾 -懂 -懈 -懊 -懋 -懑 -懒 -懔 -懦 -懵 -懿 -戆 -戈 -戊 -戋 -戌 -戍 -戎 -戏 -成 -我 -戒 -戕 -或 -戗 -战 -戚 -戛 -戟 -戡 -戢 -戣 -戤 -戥 -截 -戬 -戭 -戮 -戳 -戴 -户 -戽 -戾 -房 -所 -扁 -扂 -扃 -扅 -扆 -扇 -扈 -扉 -扊 -手 -才 -扎 -扑 -扒 -打 -扔 -托 -扛 -扞 -扣 -扦 -执 -扩 -扪 -扫 -扬 -扭 -扮 -扯 -扰 -扳 -扶 -批 -扺 -扼 -扽 -找 -承 -技 -抃 -抄 -抉 -把 -抑 -抒 -抓 -抔 -投 -抖 -抗 -折 -抚 -抛 -抟 -抠 -抡 -抢 -护 -报 -抨 -披 -抬 -抱 -抵 -抹 -抻 -押 -抽 -抿 -拂 -拃 -拄 -担 -拆 -拇 -拈 -拉 -拊 -拌 -拍 -拎 -拐 -拒 -拓 -拔 -拖 -拗 -拘 -拙 -招 -拜 -拟 -拢 -拣 -拤 -拥 -拦 -拧 -拨 -择 -括 -拭 -拮 -拯 -拱 -拳 -拴 -拶 -拷 -拼 -拽 -拾 -拿 -持 -挂 -指 -挈 -按 -挎 -挑 -挓 -挖 -挚 -挛 -挝 -挞 -挟 -挠 -挡 -挣 -挤 -挥 -挦 -挨 -挪 -挫 -振 -挲 -挹 -挺 -挽 -捂 -捃 -捅 -捆 -捉 -捋 -捌 -捍 -捎 -捏 -捐 -捕 -捞 -损 -捡 -换 -捣 -捧 -捩 -捭 -据 -捯 -捶 -捷 -捺 -捻 -捽 -掀 -掂 -掇 -授 -掉 -掊 -掌 -掎 -掏 -掐 -排 -掖 -掘 -掞 -掠 -探 -掣 -接 -控 -推 -掩 -措 -掬 -掭 -掮 -掰 -掳 -掴 -掷 -掸 -掺 -掼 -掾 -揄 -揆 -揉 -揍 -描 -提 -插 -揕 -揖 -揠 -握 -揣 -揩 -揪 -揭 -揳 -援 -揶 -揸 -揽 -揿 -搀 -搁 -搂 -搅 -搋 -搌 -搏 -搐 -搒 -搓 -搔 -搛 -搜 -搞 -搠 -搡 -搦 -搪 -搬 -搭 -搴 -携 -搽 -摁 -摄 -摅 -摆 -摇 -摈 -摊 -摏 -摒 -摔 -摘 -摛 -摞 -摧 -摩 -摭 -摴 -摸 -摹 -摽 -撂 -撄 -撅 -撇 -撑 -撒 -撕 -撖 -撙 -撞 -撤 -撩 -撬 -播 -撮 -撰 -撵 -撷 -撸 -撺 -撼 -擀 -擂 -擅 -操 -擎 -擐 -擒 -擘 -擞 -擢 -擤 -擦 -擿 -攀 -攉 -攒 -攘 -攥 -攫 -攮 -支 -收 -攸 -改 -攻 -攽 -放 -政 -故 -效 -敉 -敌 -敏 -救 -敔 -敕 -敖 -教 -敛 -敝 -敞 -敢 -散 -敦 -敩 -敫 -敬 -数 -敲 -整 -敷 -文 -斋 -斌 -斐 -斑 -斓 -斗 -料 -斛 -斜 -斝 -斟 -斠 -斡 -斤 -斥 -斧 -斩 -斫 -断 -斯 -新 -斶 -方 -於 -施 -旁 -旃 -旄 -旅 -旆 -旋 -旌 -旎 -族 -旐 -旒 -旖 -旗 -旞 -无 -既 -日 -旦 -旧 -旨 -早 -旬 -旭 -旮 -旯 -旰 -旱 -旴 -旵 -时 -旷 -旸 -旺 -旻 -旿 -昀 -昂 -昃 -昄 -昆 -昇 -昈 -昉 -昊 -昌 -明 -昏 -昒 -易 -昔 -昕 -昙 -昝 -星 -映 -昡 -昣 -昤 -春 -昧 -昨 -昪 -昫 -昭 -是 -昱 -昳 -昴 -昵 -昶 -昺 -昼 -昽 -显 -晁 -晃 -晅 -晊 -晋 -晌 -晏 -晐 -晒 -晓 -晔 -晕 -晖 -晗 -晙 -晚 -晞 -晟 -晡 -晢 -晤 -晦 -晨 -晪 -晫 -普 -景 -晰 -晱 -晴 -晶 -晷 -智 -晾 -暂 -暄 -暅 -暇 -暌 -暑 -暕 -暖 -暗 -暝 -暧 -暨 -暮 -暲 -暴 -暵 -暶 -暹 -暾 -暿 -曈 -曌 -曙 -曛 -曜 -曝 -曦 -曩 -曰 -曲 -曳 -更 -曷 -曹 -曼 -曾 -替 -最 -月 -有 -朋 -服 -朏 -朐 -朓 -朔 -朕 -朗 -望 -朝 -期 -朦 -木 -未 -末 -本 -札 -术 -朱 -朳 -朴 -朵 -朸 -机 -朽 -杀 -杂 -权 -杄 -杆 -杈 -杉 -杌 -李 -杏 -材 -村 -杓 -杕 -杖 -杙 -杜 -杞 -束 -杠 -条 -来 -杧 -杨 -杩 -杪 -杭 -杯 -杰 -杲 -杳 -杵 -杷 -杻 -杼 -松 -板 -极 -构 -枅 -枇 -枉 -枋 -枍 -析 -枕 -林 -枘 -枚 -果 -枝 -枞 -枢 -枣 -枥 -枧 -枨 -枪 -枫 -枭 -枯 -枰 -枲 -枳 -枵 -架 -枷 -枸 -枹 -柁 -柃 -柄 -柈 -柊 -柏 -某 -柑 -柒 -染 -柔 -柖 -柘 -柙 -柚 -柜 -柝 -柞 -柠 -柢 -查 -柩 -柬 -柯 -柰 -柱 -柳 -柴 -柷 -柽 -柿 -栀 -栅 -标 -栈 -栉 -栊 -栋 -栌 -栎 -栏 -栐 -树 -栒 -栓 -栖 -栗 -栝 -栟 -校 -栩 -株 -栲 -栳 -栴 -样 -核 -根 -栻 -格 -栽 -栾 -桀 -桁 -桂 -桃 -桄 -桅 -框 -案 -桉 -桊 -桌 -桎 -桐 -桑 -桓 -桔 -桕 -桠 -桡 -桢 -档 -桤 -桥 -桦 -桧 -桨 -桩 -桫 -桯 -桲 -桴 -桶 -桷 -桹 -梁 -梃 -梅 -梆 -梌 -梏 -梓 -梗 -梠 -梢 -梣 -梦 -梧 -梨 -梭 -梯 -械 -梳 -梴 -梵 -梼 -梽 -梾 -梿 -检 -棁 -棂 -棉 -棋 -棍 -棐 -棒 -棓 -棕 -棘 -棚 -棠 -棣 -棤 -棨 -棪 -棫 -棬 -森 -棰 -棱 -棵 -棹 -棺 -棻 -棼 -棽 -椀 -椁 -椅 -椆 -椋 -植 -椎 -椐 -椑 -椒 -椓 -椟 -椠 -椤 -椪 -椭 -椰 -椴 -椸 -椹 -椽 -椿 -楂 -楒 -楔 -楗 -楙 -楚 -楝 -楞 -楠 -楣 -楦 -楩 -楪 -楫 -楮 -楯 -楷 -楸 -楹 -楼 -概 -榃 -榄 -榅 -榆 -榇 -榈 -榉 -榍 -榑 -榔 -榕 -榖 -榛 -榜 -榧 -榨 -榫 -榭 -榰 -榱 -榴 -榷 -榻 -槁 -槃 -槊 -槌 -槎 -槐 -槔 -槚 -槛 -槜 -槟 -槠 -槭 -槱 -槲 -槽 -槿 -樊 -樗 -樘 -樟 -模 -樨 -横 -樯 -樱 -樵 -樽 -樾 -橄 -橇 -橐 -橑 -橘 -橙 -橛 -橞 -橡 -橥 -橦 -橱 -橹 -橼 -檀 -檄 -檎 -檐 -檑 -檗 -檞 -檠 -檩 -檫 -檬 -櫆 -欂 -欠 -次 -欢 -欣 -欤 -欧 -欲 -欸 -欹 -欺 -欻 -款 -歃 -歅 -歆 -歇 -歉 -歌 -歙 -止 -正 -此 -步 -武 -歧 -歪 -歹 -死 -歼 -殁 -殂 -殃 -殄 -殆 -殇 -殉 -殊 -残 -殍 -殒 -殓 -殖 -殚 -殛 -殡 -殣 -殪 -殳 -殴 -段 -殷 -殿 -毁 -毂 -毅 -毋 -毌 -母 -每 -毐 -毒 -毓 -比 -毕 -毖 -毗 -毙 -毛 -毡 -毪 -毫 -毯 -毳 -毵 -毹 -毽 -氅 -氆 -氇 -氍 -氏 -氐 -民 -氓 -气 -氕 -氖 -氘 -氙 -氚 -氛 -氟 -氡 -氢 -氤 -氦 -氧 -氨 -氩 -氪 -氮 -氯 -氰 -氲 -水 -永 -氾 -氿 -汀 -汁 -求 -汆 -汇 -汈 -汉 -汊 -汋 -汐 -汔 -汕 -汗 -汛 -汜 -汝 -汞 -江 -池 -污 -汤 -汧 -汨 -汩 -汪 -汫 -汭 -汰 -汲 -汴 -汶 -汹 -汽 -汾 -沁 -沂 -沃 -沄 -沅 -沆 -沇 -沈 -沉 -沌 -沏 -沐 -沓 -沔 -沘 -沙 -沚 -沛 -沟 -没 -沣 -沤 -沥 -沦 -沧 -沨 -沩 -沪 -沫 -沭 -沮 -沱 -河 -沸 -油 -沺 -治 -沼 -沽 -沾 -沿 -泂 -泃 -泄 -泅 -泇 -泉 -泊 -泌 -泐 -泓 -泔 -法 -泖 -泗 -泙 -泚 -泛 -泜 -泞 -泠 -泡 -波 -泣 -泥 -注 -泪 -泫 -泮 -泯 -泰 -泱 -泳 -泵 -泷 -泸 -泺 -泻 -泼 -泽 -泾 -洁 -洄 -洇 -洈 -洋 -洌 -洎 -洑 -洒 -洓 -洗 -洘 -洙 -洚 -洛 -洞 -洢 -洣 -津 -洧 -洨 -洪 -洫 -洭 -洮 -洱 -洲 -洳 -洴 -洵 -洸 -洹 -洺 -活 -洼 -洽 -派 -洿 -流 -浃 -浅 -浆 -浇 -浈 -浉 -浊 -测 -浍 -济 -浏 -浐 -浑 -浒 -浓 -浔 -浕 -浙 -浚 -浛 -浜 -浞 -浟 -浠 -浡 -浣 -浥 -浦 -浩 -浪 -浬 -浭 -浮 -浯 -浰 -浲 -浴 -海 -浸 -浼 -涂 -涄 -涅 -消 -涉 -涌 -涍 -涎 -涐 -涑 -涓 -涔 -涕 -涘 -涛 -涝 -涞 -涟 -涠 -涡 -涢 -涣 -涤 -润 -涧 -涨 -涩 -涪 -涫 -涮 -涯 -液 -涴 -涵 -涸 -涿 -淀 -淄 -淅 -淆 -淇 -淋 -淌 -淏 -淑 -淖 -淘 -淙 -淜 -淝 -淞 -淟 -淠 -淡 -淤 -淦 -淫 -淬 -淮 -淯 -深 -淳 -淴 -混 -淹 -添 -淼 -清 -渊 -渌 -渍 -渎 -渐 -渑 -渔 -渗 -渚 -渝 -渟 -渠 -渡 -渣 -渤 -渥 -温 -渫 -渭 -港 -渰 -渲 -渴 -游 -渺 -渼 -湃 -湄 -湉 -湍 -湎 -湑 -湓 -湔 -湖 -湘 -湛 -湜 -湝 -湟 -湣 -湫 -湮 -湲 -湴 -湾 -湿 -溁 -溃 -溅 -溆 -溇 -溉 -溍 -溏 -源 -溘 -溚 -溜 -溞 -溟 -溠 -溢 -溥 -溦 -溧 -溪 -溯 -溱 -溲 -溴 -溵 -溶 -溷 -溹 -溺 -溻 -溽 -滁 -滂 -滃 -滆 -滇 -滉 -滋 -滍 -滏 -滑 -滓 -滔 -滕 -滗 -滘 -滚 -滞 -滟 -滠 -满 -滢 -滤 -滥 -滦 -滧 -滨 -滩 -滪 -滫 -滴 -滹 -漂 -漆 -漈 -漉 -漋 -漏 -漓 -演 -漕 -漖 -漠 -漤 -漦 -漩 -漪 -漫 -漭 -漯 -漱 -漳 -漴 -漶 -漷 -漹 -漻 -漼 -漾 -潆 -潇 -潋 -潍 -潏 -潖 -潘 -潜 -潞 -潟 -潢 -潦 -潩 -潭 -潮 -潲 -潴 -潵 -潸 -潺 -潼 -潽 -潾 -澂 -澄 -澈 -澉 -澌 -澍 -澎 -澛 -澜 -澡 -澥 -澧 -澪 -澭 -澳 -澴 -澶 -澹 -澼 -澽 -激 -濂 -濉 -濋 -濑 -濒 -濞 -濠 -濡 -濩 -濮 -濯 -瀌 -瀍 -瀑 -瀔 -瀚 -瀛 -瀣 -瀱 -瀵 -瀹 -瀼 -灈 -灌 -灏 -灞 -火 -灭 -灯 -灰 -灵 -灶 -灸 -灼 -灾 -灿 -炀 -炅 -炆 -炉 -炊 -炌 -炎 -炒 -炔 -炕 -炖 -炘 -炙 -炜 -炝 -炟 -炣 -炫 -炬 -炭 -炮 -炯 -炱 -炳 -炷 -炸 -点 -炻 -炼 -炽 -烀 -烁 -烂 -烃 -烈 -烊 -烔 -烘 -烙 -烛 -烜 -烝 -烟 -烠 -烤 -烦 -烧 -烨 -烩 -烫 -烬 -热 -烯 -烶 -烷 -烹 -烺 -烻 -烽 -焆 -焉 -焊 -焌 -焐 -焓 -焕 -焖 -焗 -焘 -焙 -焚 -焜 -焞 -焦 -焯 -焰 -焱 -然 -煁 -煃 -煅 -煊 -煋 -煌 -煎 -煓 -煜 -煞 -煟 -煤 -煦 -照 -煨 -煮 -煲 -煳 -煴 -煸 -煺 -煽 -熄 -熇 -熊 -熏 -熔 -熘 -熙 -熛 -熜 -熟 -熠 -熥 -熨 -熬 -熵 -熹 -熻 -燃 -燊 -燋 -燎 -燏 -燔 -燕 -燚 -燠 -燥 -燧 -燮 -燹 -爆 -爇 -爔 -爚 -爝 -爟 -爨 -爪 -爬 -爰 -爱 -爵 -父 -爷 -爸 -爹 -爻 -爽 -爿 -牁 -牂 -片 -版 -牌 -牍 -牒 -牖 -牙 -牚 -牛 -牝 -牟 -牡 -牢 -牤 -牥 -牦 -牧 -物 -牮 -牯 -牲 -牵 -特 -牺 -牻 -牾 -牿 -犀 -犁 -犄 -犇 -犊 -犋 -犍 -犏 -犒 -犟 -犨 -犬 -犯 -犰 -犴 -状 -犷 -犸 -犹 -狁 -狂 -狃 -狄 -狈 -狉 -狍 -狎 -狐 -狒 -狗 -狙 -狝 -狞 -狠 -狡 -狨 -狩 -独 -狭 -狮 -狯 -狰 -狱 -狲 -狳 -狴 -狷 -狸 -狺 -狻 -狼 -猁 -猃 -猄 -猇 -猊 -猎 -猕 -猖 -猗 -猛 -猜 -猝 -猞 -猡 -猢 -猥 -猩 -猪 -猫 -猬 -献 -猯 -猰 -猱 -猴 -猷 -猹 -猺 -猾 -猿 -獍 -獐 -獒 -獗 -獠 -獬 -獭 -獯 -獴 -獾 -玃 -玄 -率 -玉 -王 -玎 -玑 -玒 -玓 -玕 -玖 -玘 -玙 -玚 -玛 -玞 -玟 -玠 -玡 -玢 -玤 -玥 -玦 -玩 -玫 -玭 -玮 -环 -现 -玱 -玲 -玳 -玶 -玷 -玹 -玺 -玻 -玼 -玿 -珀 -珂 -珅 -珇 -珈 -珉 -珊 -珋 -珌 -珍 -珏 -珐 -珑 -珒 -珕 -珖 -珙 -珛 -珝 -珞 -珠 -珢 -珣 -珥 -珦 -珧 -珩 -珪 -珫 -班 -珰 -珲 -珵 -珷 -珸 -珹 -珺 -珽 -琀 -球 -琄 -琅 -理 -琇 -琈 -琉 -琊 -琎 -琏 -琐 -琔 -琚 -琛 -琟 -琡 -琢 -琤 -琥 -琦 -琨 -琪 -琫 -琬 -琭 -琮 -琯 -琰 -琲 -琳 -琴 -琵 -琶 -琼 -瑀 -瑁 -瑂 -瑃 -瑄 -瑅 -瑆 -瑑 -瑓 -瑔 -瑕 -瑖 -瑗 -瑙 -瑚 -瑛 -瑜 -瑝 -瑞 -瑟 -瑢 -瑧 -瑨 -瑬 -瑭 -瑰 -瑱 -瑳 -瑶 -瑷 -瑾 -璀 -璁 -璃 -璆 -璇 -璈 -璋 -璎 -璐 -璒 -璘 -璜 -璞 -璟 -璠 -璥 -璧 -璨 -璩 -璪 -璬 -璮 -璱 -璲 -璺 -瓀 -瓒 -瓖 -瓘 -瓜 -瓞 -瓠 -瓢 -瓣 -瓤 -瓦 -瓮 -瓯 -瓴 -瓶 -瓷 -瓻 -瓿 -甄 -甍 -甏 -甑 -甓 -甗 -甘 -甚 -甜 -生 -甡 -甥 -甦 -用 -甩 -甪 -甫 -甬 -甭 -甯 -田 -由 -甲 -申 -电 -男 -甸 -町 -画 -甾 -畀 -畅 -畈 -畋 -界 -畎 -畏 -畔 -畖 -留 -畚 -畛 -畜 -畤 -略 -畦 -番 -畬 -畯 -畲 -畴 -畸 -畹 -畿 -疁 -疃 -疆 -疍 -疏 -疐 -疑 -疔 -疖 -疗 -疙 -疚 -疝 -疟 -疠 -疡 -疢 -疣 -疤 -疥 -疫 -疬 -疭 -疮 -疯 -疰 -疱 -疲 -疳 -疴 -疵 -疸 -疹 -疼 -疽 -疾 -痂 -痃 -痄 -病 -症 -痈 -痉 -痊 -痍 -痒 -痓 -痔 -痕 -痘 -痛 -痞 -痢 -痣 -痤 -痦 -痧 -痨 -痪 -痫 -痰 -痱 -痴 -痹 -痼 -痿 -瘀 -瘁 -瘃 -瘅 -瘆 -瘊 -瘌 -瘐 -瘕 -瘗 -瘘 -瘙 -瘛 -瘟 -瘠 -瘢 -瘤 -瘥 -瘦 -瘩 -瘪 -瘫 -瘭 -瘰 -瘳 -瘴 -瘵 -瘸 -瘼 -瘾 -瘿 -癀 -癃 -癌 -癍 -癔 -癖 -癗 -癜 -癞 -癣 -癫 -癯 -癸 -登 -白 -百 -癿 -皂 -的 -皆 -皇 -皈 -皋 -皎 -皑 -皓 -皕 -皖 -皙 -皛 -皞 -皤 -皦 -皭 -皮 -皱 -皲 -皴 -皿 -盂 -盅 -盆 -盈 -盉 -益 -盍 -盎 -盏 -盐 -监 -盒 -盔 -盖 -盗 -盘 -盛 -盟 -盥 -盦 -目 -盯 -盱 -盲 -直 -盷 -相 -盹 -盼 -盾 -省 -眄 -眇 -眈 -眉 -眊 -看 -眍 -眙 -眚 -真 -眠 -眢 -眦 -眨 -眩 -眬 -眭 -眯 -眵 -眶 -眷 -眸 -眺 -眼 -着 -睁 -睃 -睄 -睇 -睎 -睐 -睑 -睚 -睛 -睡 -睢 -督 -睥 -睦 -睨 -睫 -睬 -睹 -睽 -睾 -睿 -瞀 -瞄 -瞅 -瞋 -瞌 -瞍 -瞎 -瞑 -瞒 -瞟 -瞠 -瞢 -瞥 -瞧 -瞩 -瞪 -瞫 -瞬 -瞭 -瞰 -瞳 -瞵 -瞻 -瞽 -瞿 -矍 -矗 -矛 -矜 -矞 -矢 -矣 -知 -矧 -矩 -矫 -矬 -短 -矮 -矰 -石 -矶 -矸 -矻 -矼 -矾 -矿 -砀 -码 -砂 -砄 -砆 -砉 -砌 -砍 -砑 -砒 -研 -砖 -砗 -砘 -砚 -砜 -砝 -砟 -砠 -砣 -砥 -砧 -砫 -砬 -砭 -砮 -砰 -破 -砵 -砷 -砸 -砹 -砺 -砻 -砼 -砾 -础 -硁 -硅 -硇 -硊 -硌 -硍 -硎 -硐 -硒 -硔 -硕 -硖 -硗 -硙 -硚 -硝 -硪 -硫 -硬 -硭 -确 -硼 -硿 -碃 -碇 -碈 -碉 -碌 -碍 -碎 -碏 -碑 -碓 -碗 -碘 -碚 -碛 -碜 -碟 -碡 -碣 -碥 -碧 -碨 -碰 -碱 -碲 -碳 -碴 -碶 -碹 -碾 -磁 -磅 -磉 -磊 -磋 -磏 -磐 -磔 -磕 -磙 -磜 -磡 -磨 -磬 -磲 -磴 -磷 -磹 -磻 -礁 -礅 -礌 -礓 -礞 -礴 -礵 -示 -礼 -社 -祀 -祁 -祃 -祆 -祇 -祈 -祉 -祊 -祋 -祎 -祏 -祐 -祓 -祕 -祖 -祗 -祚 -祛 -祜 -祝 -神 -祟 -祠 -祢 -祥 -祧 -票 -祭 -祯 -祲 -祷 -祸 -祺 -祼 -祾 -禀 -禁 -禄 -禅 -禊 -禋 -福 -禒 -禔 -禘 -禚 -禛 -禤 -禧 -禳 -禹 -禺 -离 -禽 -禾 -秀 -私 -秃 -秆 -秉 -秋 -种 -科 -秒 -秕 -秘 -租 -秣 -秤 -秦 -秧 -秩 -秫 -秬 -秭 -积 -称 -秸 -移 -秽 -秾 -稀 -稂 -稃 -稆 -程 -稌 -稍 -税 -稑 -稔 -稗 -稙 -稚 -稞 -稠 -稣 -稳 -稷 -稹 -稻 -稼 -稽 -稿 -穄 -穆 -穑 -穗 -穙 -穜 -穟 -穰 -穴 -究 -穷 -穸 -穹 -空 -穿 -窀 -突 -窃 -窄 -窅 -窈 -窊 -窍 -窎 -窑 -窒 -窕 -窖 -窗 -窘 -窜 -窝 -窟 -窠 -窣 -窥 -窦 -窨 -窬 -窭 -窳 -窸 -窿 -立 -竑 -竖 -竘 -站 -竞 -竟 -章 -竣 -童 -竦 -竫 -竭 -端 -竹 -竺 -竽 -竿 -笃 -笄 -笆 -笈 -笊 -笋 -笏 -笑 -笔 -笕 -笙 -笛 -笞 -笠 -笤 -笥 -符 -笨 -笪 -笫 -第 -笮 -笯 -笱 -笳 -笸 -笺 -笼 -笾 -筀 -筅 -筇 -等 -筋 -筌 -筏 -筐 -筑 -筒 -答 -策 -筘 -筚 -筛 -筜 -筝 -筠 -筢 -筤 -筥 -筦 -筮 -筱 -筲 -筵 -筶 -筷 -筹 -筻 -筼 -签 -简 -箅 -箍 -箐 -箓 -箔 -箕 -箖 -算 -箜 -管 -箢 -箦 -箧 -箨 -箩 -箪 -箫 -箬 -箭 -箱 -箴 -箸 -篁 -篆 -篇 -篌 -篑 -篓 -篙 -篚 -篝 -篡 -篥 -篦 -篪 -篮 -篯 -篱 -篷 -篼 -篾 -簃 -簇 -簉 -簋 -簌 -簏 -簕 -簖 -簝 -簟 -簠 -簧 -簪 -簰 -簸 -簿 -籀 -籁 -籍 -籥 -米 -籴 -类 -籼 -籽 -粉 -粑 -粒 -粕 -粗 -粘 -粜 -粝 -粞 -粟 -粢 -粤 -粥 -粪 -粮 -粱 -粲 -粳 -粹 -粼 -粽 -精 -粿 -糁 -糅 -糇 -糈 -糊 -糌 -糍 -糒 -糕 -糖 -糗 -糙 -糜 -糟 -糠 -糨 -糯 -糵 -系 -紊 -素 -索 -紧 -紫 -累 -絜 -絮 -絷 -綦 -綮 -縠 -縢 -縻 -繁 -繄 -繇 -纂 -纛 -纠 -纡 -红 -纣 -纤 -纥 -约 -级 -纨 -纩 -纪 -纫 -纬 -纭 -纮 -纯 -纰 -纱 -纲 -纳 -纴 -纵 -纶 -纷 -纸 -纹 -纺 -纻 -纼 -纽 -纾 -线 -绀 -绁 -绂 -练 -组 -绅 -细 -织 -终 -绉 -绊 -绋 -绌 -绍 -绎 -经 -绐 -绑 -绒 -结 -绔 -绕 -绖 -绗 -绘 -给 -绚 -绛 -络 -绝 -绞 -统 -绠 -绡 -绢 -绣 -绤 -绥 -绦 -继 -绨 -绩 -绪 -绫 -续 -绮 -绯 -绰 -绱 -绲 -绳 -维 -绵 -绶 -绷 -绸 -绹 -绺 -绻 -综 -绽 -绾 -绿 -缀 -缁 -缂 -缃 -缄 -缅 -缆 -缇 -缈 -缉 -缊 -缌 -缎 -缐 -缑 -缒 -缓 -缔 -缕 -编 -缗 -缘 -缙 -缚 -缛 -缜 -缝 -缞 -缟 -缠 -缡 -缢 -缣 -缤 -缥 -缦 -缧 -缨 -缩 -缪 -缫 -缬 -缭 -缮 -缯 -缰 -缱 -缲 -缳 -缴 -缵 -缶 -缸 -缺 -罂 -罄 -罅 -罍 -罐 -网 -罔 -罕 -罗 -罘 -罚 -罟 -罡 -罢 -罨 -罩 -罪 -置 -罱 -署 -罴 -罶 -罹 -罽 -罾 -羁 -羊 -羌 -美 -羑 -羓 -羔 -羕 -羖 -羚 -羝 -羞 -羟 -羡 -群 -羧 -羯 -羰 -羱 -羲 -羸 -羹 -羼 -羽 -羿 -翀 -翁 -翂 -翃 -翅 -翈 -翊 -翌 -翎 -翔 -翕 -翘 -翙 -翚 -翛 -翟 -翠 -翡 -翥 -翦 -翩 -翮 -翯 -翰 -翱 -翳 -翷 -翻 -翼 -翾 -耀 -老 -考 -耄 -者 -耆 -耇 -耋 -而 -耍 -耏 -耐 -耑 -耒 -耔 -耕 -耖 -耗 -耘 -耙 -耜 -耠 -耢 -耤 -耥 -耦 -耧 -耨 -耩 -耪 -耰 -耱 -耳 -耵 -耶 -耷 -耸 -耻 -耽 -耿 -聂 -聃 -聆 -聊 -聋 -职 -聍 -聒 -联 -聘 -聚 -聩 -聪 -聱 -聿 -肃 -肄 -肆 -肇 -肉 -肋 -肌 -肓 -肖 -肘 -肚 -肛 -肝 -肟 -肠 -股 -肢 -肤 -肥 -肩 -肪 -肫 -肭 -肮 -肯 -肱 -育 -肴 -肷 -肸 -肺 -肼 -肽 -肾 -肿 -胀 -胁 -胂 -胃 -胄 -胆 -胈 -背 -胍 -胎 -胖 -胗 -胙 -胚 -胛 -胜 -胝 -胞 -胠 -胡 -胣 -胤 -胥 -胧 -胨 -胩 -胪 -胫 -胬 -胭 -胯 -胰 -胱 -胲 -胳 -胴 -胶 -胸 -胺 -胼 -能 -脂 -脆 -脉 -脊 -脍 -脎 -脏 -脐 -脑 -脒 -脓 -脔 -脖 -脘 -脚 -脞 -脟 -脩 -脬 -脯 -脱 -脲 -脶 -脸 -脾 -脿 -腆 -腈 -腊 -腋 -腌 -腐 -腑 -腒 -腓 -腔 -腕 -腘 -腙 -腚 -腠 -腥 -腧 -腨 -腩 -腭 -腮 -腯 -腰 -腱 -腴 -腹 -腺 -腻 -腼 -腽 -腾 -腿 -膀 -膂 -膈 -膊 -膏 -膑 -膘 -膙 -膛 -膜 -膝 -膦 -膨 -膳 -膺 -膻 -臀 -臂 -臃 -臆 -臊 -臌 -臑 -臜 -臣 -臧 -自 -臬 -臭 -至 -致 -臻 -臼 -臾 -舀 -舁 -舂 -舄 -舅 -舆 -舌 -舍 -舐 -舒 -舔 -舛 -舜 -舞 -舟 -舠 -舢 -舣 -舥 -航 -舫 -般 -舭 -舯 -舰 -舱 -舲 -舳 -舴 -舵 -舶 -舷 -舸 -船 -舻 -舾 -艄 -艅 -艇 -艉 -艋 -艎 -艏 -艘 -艚 -艟 -艨 -艮 -良 -艰 -色 -艳 -艴 -艺 -艽 -艾 -艿 -节 -芃 -芄 -芈 -芊 -芋 -芍 -芎 -芏 -芑 -芒 -芗 -芘 -芙 -芜 -芝 -芟 -芠 -芡 -芣 -芤 -芥 -芦 -芨 -芩 -芪 -芫 -芬 -芭 -芮 -芯 -芰 -花 -芳 -芴 -芷 -芸 -芹 -芼 -芽 -芾 -苁 -苄 -苇 -苈 -苉 -苊 -苋 -苌 -苍 -苎 -苏 -苑 -苒 -苓 -苔 -苕 -苗 -苘 -苛 -苜 -苞 -苟 -苠 -苡 -苣 -苤 -若 -苦 -苧 -苫 -苯 -英 -苴 -苷 -苹 -苻 -苾 -茀 -茁 -茂 -范 -茄 -茅 -茆 -茈 -茉 -茋 -茌 -茎 -茏 -茑 -茓 -茔 -茕 -茗 -茚 -茛 -茜 -茝 -茧 -茨 -茫 -茬 -茭 -茯 -茱 -茳 -茴 -茵 -茶 -茸 -茹 -茺 -茼 -茽 -荀 -荁 -荃 -荄 -荆 -荇 -草 -荏 -荐 -荑 -荒 -荓 -荔 -荖 -荙 -荚 -荛 -荜 -荞 -荟 -荠 -荡 -荣 -荤 -荥 -荦 -荧 -荨 -荩 -荪 -荫 -荬 -荭 -荮 -药 -荷 -荸 -荻 -荼 -荽 -莅 -莆 -莉 -莎 -莒 -莓 -莘 -莙 -莛 -莜 -莝 -莞 -莠 -莨 -莩 -莪 -莫 -莰 -莱 -莲 -莳 -莴 -莶 -获 -莸 -莹 -莺 -莼 -莽 -莿 -菀 -菁 -菂 -菅 -菇 -菉 -菊 -菌 -菍 -菏 -菔 -菖 -菘 -菜 -菝 -菟 -菠 -菡 -菥 -菩 -菪 -菰 -菱 -菲 -菹 -菼 -菽 -萁 -萃 -萄 -萆 -萋 -萌 -萍 -萎 -萏 -萑 -萘 -萚 -萜 -萝 -萣 -萤 -营 -萦 -萧 -萨 -萩 -萱 -萳 -萸 -萹 -萼 -落 -葆 -葎 -葑 -葖 -著 -葙 -葚 -葛 -葜 -葡 -董 -葩 -葫 -葬 -葭 -葰 -葱 -葳 -葴 -葵 -葶 -葸 -葺 -蒂 -蒄 -蒇 -蒈 -蒉 -蒋 -蒌 -蒎 -蒐 -蒗 -蒙 -蒜 -蒟 -蒡 -蒨 -蒯 -蒱 -蒲 -蒴 -蒸 -蒹 -蒺 -蒻 -蒽 -蒿 -蓁 -蓂 -蓄 -蓇 -蓉 -蓊 -蓍 -蓏 -蓐 -蓑 -蓓 -蓖 -蓝 -蓟 -蓠 -蓢 -蓣 -蓥 -蓦 -蓬 -蓰 -蓼 -蓿 -蔀 -蔃 -蔈 -蔊 -蔌 -蔑 -蔓 -蔗 -蔚 -蔟 -蔡 -蔫 -蔬 -蔷 -蔸 -蔹 -蔺 -蔻 -蔼 -蔽 -蕃 -蕈 -蕉 -蕊 -蕖 -蕗 -蕙 -蕞 -蕤 -蕨 -蕰 -蕲 -蕴 -蕹 -蕺 -蕻 -蕾 -薁 -薄 -薅 -薇 -薏 -薛 -薜 -薢 -薤 -薨 -薪 -薮 -薯 -薰 -薳 -薷 -薸 -薹 -薿 -藁 -藉 -藏 -藐 -藓 -藕 -藜 -藟 -藠 -藤 -藦 -藨 -藩 -藻 -藿 -蘅 -蘑 -蘖 -蘘 -蘧 -蘩 -蘸 -蘼 -虎 -虏 -虐 -虑 -虒 -虓 -虔 -虚 -虞 -虢 -虤 -虫 -虬 -虮 -虱 -虷 -虸 -虹 -虺 -虻 -虼 -虽 -虾 -虿 -蚀 -蚁 -蚂 -蚄 -蚆 -蚊 -蚋 -蚌 -蚍 -蚓 -蚕 -蚜 -蚝 -蚣 -蚤 -蚧 -蚨 -蚩 -蚪 -蚬 -蚯 -蚰 -蚱 -蚲 -蚴 -蚶 -蚺 -蛀 -蛃 -蛄 -蛆 -蛇 -蛉 -蛊 -蛋 -蛎 -蛏 -蛐 -蛑 -蛔 -蛘 -蛙 -蛛 -蛞 -蛟 -蛤 -蛩 -蛭 -蛮 -蛰 -蛱 -蛲 -蛳 -蛴 -蛸 -蛹 -蛾 -蜀 -蜂 -蜃 -蜇 -蜈 -蜉 -蜊 -蜍 -蜎 -蜐 -蜒 -蜓 -蜕 -蜗 -蜘 -蜚 -蜜 -蜞 -蜡 -蜢 -蜣 -蜥 -蜩 -蜮 -蜱 -蜴 -蜷 -蜻 -蜾 -蜿 -蝇 -蝈 -蝉 -蝌 -蝎 -蝓 -蝗 -蝘 -蝙 -蝠 -蝣 -蝤 -蝥 -蝮 -蝰 -蝲 -蝴 -蝶 -蝻 -蝼 -蝽 -蝾 -螂 -螃 -螅 -螈 -螋 -融 -螗 -螟 -螠 -螣 -螨 -螫 -螬 -螭 -螯 -螱 -螳 -螵 -螺 -螽 -蟀 -蟆 -蟊 -蟋 -蟏 -蟑 -蟒 -蟛 -蟠 -蟥 -蟪 -蟫 -蟮 -蟹 -蟾 -蠃 -蠊 -蠋 -蠓 -蠕 -蠖 -蠡 -蠢 -蠲 -蠹 -蠼 -血 -衃 -衄 -衅 -行 -衍 -衎 -衒 -衔 -街 -衙 -衠 -衡 -衢 -衣 -补 -表 -衩 -衫 -衬 -衮 -衰 -衲 -衷 -衽 -衾 -衿 -袁 -袂 -袄 -袅 -袆 -袈 -袋 -袍 -袒 -袖 -袗 -袜 -袢 -袤 -袪 -被 -袭 -袯 -袱 -袷 -袼 -裁 -裂 -装 -裆 -裈 -裉 -裎 -裒 -裔 -裕 -裘 -裙 -裛 -裟 -裢 -裣 -裤 -裥 -裨 -裰 -裱 -裳 -裴 -裸 -裹 -裼 -裾 -褂 -褊 -褐 -褒 -褓 -褕 -褙 -褚 -褛 -褟 -褡 -褥 -褪 -褫 -褯 -褰 -褴 -褶 -襁 -襄 -襕 -襚 -襜 -襞 -襟 -襦 -襫 -襻 -西 -要 -覃 -覆 -见 -观 -觃 -规 -觅 -视 -觇 -览 -觉 -觊 -觋 -觌 -觎 -觏 -觐 -觑 -角 -觖 -觚 -觜 -觞 -觟 -解 -觥 -触 -觫 -觭 -觯 -觱 -觳 -觿 -言 -訄 -訇 -訚 -訾 -詈 -詟 -詹 -誉 -誊 -誓 -謇 -警 -譬 -计 -订 -讣 -认 -讥 -讦 -讧 -讨 -让 -讪 -讫 -训 -议 -讯 -记 -讱 -讲 -讳 -讴 -讵 -讶 -讷 -许 -讹 -论 -讻 -讼 -讽 -设 -访 -诀 -证 -诂 -诃 -评 -诅 -识 -诇 -诈 -诉 -诊 -诋 -诌 -词 -诎 -诏 -诐 -译 -诒 -诓 -诔 -试 -诖 -诗 -诘 -诙 -诚 -诛 -诜 -话 -诞 -诟 -诠 -诡 -询 -诣 -诤 -该 -详 -诧 -诨 -诩 -诫 -诬 -语 -诮 -误 -诰 -诱 -诲 -诳 -说 -诵 -请 -诸 -诹 -诺 -读 -诼 -诽 -课 -诿 -谀 -谁 -谂 -调 -谄 -谅 -谆 -谇 -谈 -谊 -谋 -谌 -谍 -谎 -谏 -谐 -谑 -谒 -谓 -谔 -谕 -谖 -谗 -谙 -谚 -谛 -谜 -谝 -谞 -谟 -谠 -谡 -谢 -谣 -谤 -谥 -谦 -谧 -谨 -谩 -谪 -谫 -谬 -谭 -谮 -谯 -谰 -谱 -谲 -谳 -谴 -谵 -谶 -谷 -谼 -谿 -豁 -豆 -豇 -豉 -豌 -豕 -豚 -象 -豢 -豨 -豪 -豫 -豮 -豳 -豸 -豹 -豺 -貂 -貅 -貆 -貉 -貊 -貌 -貔 -貘 -贝 -贞 -负 -贡 -财 -责 -贤 -败 -账 -货 -质 -贩 -贪 -贫 -贬 -购 -贮 -贯 -贰 -贱 -贲 -贳 -贴 -贵 -贶 -贷 -贸 -费 -贺 -贻 -贼 -贽 -贾 -贿 -赀 -赁 -赂 -赃 -资 -赅 -赆 -赇 -赈 -赉 -赊 -赋 -赌 -赍 -赎 -赏 -赐 -赑 -赒 -赓 -赔 -赕 -赖 -赗 -赘 -赙 -赚 -赛 -赜 -赝 -赞 -赟 -赠 -赡 -赢 -赣 -赤 -赦 -赧 -赪 -赫 -赭 -走 -赳 -赴 -赵 -赶 -起 -趁 -趄 -超 -越 -趋 -趑 -趔 -趟 -趣 -趯 -趱 -足 -趴 -趵 -趸 -趺 -趼 -趾 -趿 -跂 -跃 -跄 -跆 -跋 -跌 -跎 -跏 -跐 -跑 -跖 -跗 -跚 -跛 -距 -跞 -跟 -跣 -跤 -跨 -跪 -跬 -路 -跱 -跳 -践 -跶 -跷 -跸 -跹 -跺 -跻 -跽 -踅 -踉 -踊 -踌 -踏 -踒 -踔 -踝 -踞 -踟 -踢 -踣 -踦 -踩 -踪 -踬 -踮 -踯 -踱 -踵 -踶 -踹 -踺 -踽 -蹀 -蹁 -蹂 -蹄 -蹅 -蹇 -蹈 -蹉 -蹊 -蹋 -蹐 -蹑 -蹒 -蹙 -蹚 -蹜 -蹢 -蹦 -蹩 -蹬 -蹭 -蹯 -蹰 -蹲 -蹴 -蹶 -蹼 -蹽 -蹾 -蹿 -躁 -躅 -躇 -躏 -躐 -躔 -躜 -躞 -身 -躬 -躯 -躲 -躺 -车 -轧 -轨 -轩 -轪 -轫 -转 -轭 -轮 -软 -轰 -轱 -轲 -轳 -轴 -轵 -轶 -轷 -轸 -轹 -轺 -轻 -轼 -载 -轾 -轿 -辀 -辁 -辂 -较 -辄 -辅 -辆 -辇 -辈 -辉 -辊 -辋 -辌 -辍 -辎 -辏 -辐 -辑 -辒 -输 -辔 -辕 -辖 -辗 -辘 -辙 -辚 -辛 -辜 -辞 -辟 -辣 -辨 -辩 -辫 -辰 -辱 -边 -辽 -达 -辿 -迁 -迂 -迄 -迅 -过 -迈 -迎 -运 -近 -迓 -返 -迕 -还 -这 -进 -远 -违 -连 -迟 -迢 -迤 -迥 -迦 -迨 -迩 -迪 -迫 -迭 -迮 -述 -迳 -迷 -迸 -迹 -迺 -追 -退 -送 -适 -逃 -逄 -逅 -逆 -选 -逊 -逋 -逍 -透 -逐 -逑 -递 -途 -逖 -逗 -通 -逛 -逝 -逞 -速 -造 -逡 -逢 -逦 -逭 -逮 -逯 -逴 -逵 -逶 -逸 -逻 -逼 -逾 -遁 -遂 -遄 -遆 -遇 -遍 -遏 -遐 -遑 -遒 -道 -遗 -遘 -遛 -遢 -遣 -遥 -遨 -遭 -遮 -遴 -遵 -遹 -遽 -避 -邀 -邂 -邃 -邈 -邋 -邑 -邓 -邕 -邗 -邘 -邙 -邛 -邝 -邠 -邡 -邢 -那 -邦 -邨 -邪 -邬 -邮 -邯 -邰 -邱 -邲 -邳 -邴 -邵 -邶 -邸 -邹 -邺 -邻 -邽 -邾 -邿 -郁 -郃 -郄 -郅 -郇 -郈 -郊 -郎 -郏 -郐 -郑 -郓 -郗 -郚 -郛 -郜 -郝 -郡 -郢 -郤 -郦 -郧 -部 -郪 -郫 -郭 -郯 -郴 -郸 -都 -郾 -郿 -鄀 -鄂 -鄃 -鄄 -鄅 -鄌 -鄑 -鄗 -鄘 -鄙 -鄚 -鄜 -鄞 -鄠 -鄢 -鄣 -鄫 -鄯 -鄱 -鄹 -酂 -酃 -酅 -酆 -酉 -酊 -酋 -酌 -配 -酎 -酏 -酐 -酒 -酗 -酚 -酝 -酞 -酡 -酢 -酣 -酤 -酥 -酦 -酩 -酪 -酬 -酮 -酯 -酰 -酱 -酲 -酴 -酵 -酶 -酷 -酸 -酹 -酺 -酽 -酾 -酿 -醅 -醇 -醉 -醋 -醌 -醍 -醐 -醑 -醒 -醚 -醛 -醢 -醨 -醪 -醭 -醮 -醯 -醴 -醵 -醺 -醾 -采 -釉 -释 -里 -重 -野 -量 -釐 -金 -釜 -鉴 -銎 -銮 -鋆 -鋈 -錾 -鍪 -鎏 -鏊 -鏖 -鐾 -鑫 -钆 -钇 -针 -钉 -钊 -钋 -钌 -钍 -钎 -钏 -钐 -钒 -钓 -钔 -钕 -钖 -钗 -钘 -钙 -钚 -钛 -钜 -钝 -钞 -钟 -钠 -钡 -钢 -钣 -钤 -钥 -钦 -钧 -钨 -钩 -钪 -钫 -钬 -钭 -钮 -钯 -钰 -钱 -钲 -钳 -钴 -钵 -钷 -钹 -钺 -钻 -钼 -钽 -钾 -钿 -铀 -铁 -铂 -铃 -铄 -铅 -铆 -铈 -铉 -铊 -铋 -铌 -铍 -铎 -铏 -铐 -铑 -铒 -铕 -铖 -铗 -铘 -铙 -铚 -铛 -铜 -铝 -铞 -铟 -铠 -铡 -铢 -铣 -铤 -铥 -铧 -铨 -铩 -铪 -铫 -铬 -铭 -铮 -铯 -铰 -铱 -铲 -铳 -铴 -铵 -银 -铷 -铸 -铹 -铺 -铻 -铼 -铽 -链 -铿 -销 -锁 -锂 -锃 -锄 -锅 -锆 -锇 -锈 -锉 -锊 -锋 -锌 -锍 -锎 -锏 -锐 -锑 -锒 -锓 -锔 -锕 -锖 -锗 -锘 -错 -锚 -锛 -锜 -锝 -锞 -锟 -锡 -锢 -锣 -锤 -锥 -锦 -锧 -锨 -锩 -锪 -锫 -锬 -锭 -键 -锯 -锰 -锱 -锲 -锳 -锴 -锵 -锶 -锷 -锸 -锹 -锺 -锻 -锼 -锽 -锾 -锿 -镀 -镁 -镂 -镃 -镄 -镅 -镆 -镇 -镈 -镉 -镊 -镋 -镌 -镍 -镎 -镏 -镐 -镑 -镒 -镓 -镔 -镕 -镖 -镗 -镘 -镚 -镛 -镜 -镝 -镞 -镠 -镡 -镢 -镣 -镤 -镥 -镦 -镧 -镨 -镩 -镪 -镫 -镬 -镭 -镮 -镯 -镰 -镱 -镲 -镳 -镴 -镵 -镶 -长 -门 -闩 -闪 -闫 -闭 -问 -闯 -闰 -闱 -闲 -闳 -间 -闵 -闶 -闷 -闸 -闹 -闺 -闻 -闼 -闽 -闾 -闿 -阀 -阁 -阂 -阃 -阄 -阅 -阆 -阇 -阈 -阉 -阊 -阋 -阌 -阍 -阎 -阏 -阐 -阑 -阒 -阔 -阕 -阖 -阗 -阘 -阙 -阚 -阜 -队 -阡 -阪 -阮 -阱 -防 -阳 -阴 -阵 -阶 -阻 -阼 -阽 -阿 -陀 -陂 -附 -际 -陆 -陇 -陈 -陉 -陋 -陌 -降 -陎 -限 -陑 -陔 -陕 -陛 -陞 -陟 -陡 -院 -除 -陧 -陨 -险 -陪 -陬 -陲 -陴 -陵 -陶 -陷 -隃 -隅 -隆 -隈 -隋 -隍 -随 -隐 -隔 -隗 -隘 -隙 -障 -隧 -隩 -隰 -隳 -隶 -隹 -隺 -隼 -隽 -难 -雀 -雁 -雄 -雅 -集 -雇 -雉 -雊 -雌 -雍 -雎 -雏 -雒 -雕 -雠 -雨 -雩 -雪 -雯 -雱 -雳 -零 -雷 -雹 -雾 -需 -霁 -霄 -霅 -霆 -震 -霈 -霉 -霍 -霎 -霏 -霓 -霖 -霜 -霞 -霨 -霪 -霭 -霰 -露 -霸 -霹 -霾 -青 -靓 -靖 -静 -靛 -非 -靠 -靡 -面 -靥 -革 -靬 -靰 -靳 -靴 -靶 -靸 -靺 -靼 -靽 -靿 -鞁 -鞅 -鞋 -鞍 -鞑 -鞒 -鞔 -鞘 -鞠 -鞡 -鞣 -鞧 -鞨 -鞫 -鞬 -鞭 -鞮 -鞯 -鞲 -鞳 -鞴 -韂 -韦 -韧 -韨 -韩 -韪 -韫 -韬 -韭 -音 -韵 -韶 -页 -顶 -顷 -顸 -项 -顺 -须 -顼 -顽 -顾 -顿 -颀 -颁 -颂 -颃 -预 -颅 -领 -颇 -颈 -颉 -颊 -颋 -颌 -颍 -颎 -颏 -颐 -频 -颓 -颔 -颖 -颗 -题 -颙 -颚 -颛 -颜 -额 -颞 -颟 -颠 -颡 -颢 -颤 -颥 -颦 -颧 -风 -飏 -飐 -飑 -飒 -飓 -飔 -飕 -飗 -飘 -飙 -飞 -食 -飧 -飨 -餍 -餐 -餮 -饔 -饕 -饥 -饧 -饨 -饩 -饪 -饫 -饬 -饭 -饮 -饯 -饰 -饱 -饲 -饳 -饴 -饵 -饶 -饷 -饸 -饹 -饺 -饻 -饼 -饽 -饿 -馁 -馃 -馄 -馅 -馆 -馇 -馈 -馉 -馊 -馋 -馌 -馍 -馏 -馐 -馑 -馒 -馓 -馔 -馕 -首 -馗 -馘 -香 -馝 -馞 -馥 -馧 -馨 -马 -驭 -驮 -驯 -驰 -驱 -驲 -驳 -驴 -驵 -驶 -驷 -驸 -驹 -驺 -驻 -驼 -驽 -驾 -驿 -骀 -骁 -骂 -骃 -骄 -骅 -骆 -骇 -骈 -骉 -骊 -骋 -验 -骍 -骎 -骏 -骐 -骑 -骒 -骓 -骕 -骖 -骗 -骘 -骙 -骚 -骛 -骜 -骝 -骞 -骟 -骠 -骡 -骢 -骣 -骤 -骥 -骦 -骧 -骨 -骰 -骱 -骶 -骷 -骸 -骺 -骼 -髀 -髁 -髂 -髃 -髅 -髋 -髌 -髎 -髑 -髓 -高 -髡 -髢 -髦 -髫 -髭 -髯 -髹 -髻 -髽 -鬃 -鬈 -鬏 -鬒 -鬓 -鬘 -鬟 -鬣 -鬯 -鬲 -鬶 -鬷 -鬻 -鬼 -魁 -魂 -魃 -魄 -魅 -魆 -魇 -魈 -魉 -魋 -魍 -魏 -魑 -魔 -鱼 -鱽 -鱾 -鱿 -鲀 -鲁 -鲂 -鲃 -鲅 -鲆 -鲇 -鲈 -鲉 -鲊 -鲋 -鲌 -鲍 -鲎 -鲏 -鲐 -鲑 -鲒 -鲔 -鲕 -鲖 -鲗 -鲘 -鲙 -鲚 -鲛 -鲜 -鲝 -鲞 -鲟 -鲠 -鲡 -鲢 -鲣 -鲤 -鲥 -鲦 -鲧 -鲨 -鲩 -鲪 -鲫 -鲬 -鲭 -鲮 -鲯 -鲰 -鲱 -鲲 -鲳 -鲴 -鲵 -鲷 -鲸 -鲹 -鲺 -鲻 -鲼 -鲽 -鲾 -鲿 -鳀 -鳁 -鳂 -鳃 -鳄 -鳅 -鳇 -鳈 -鳉 -鳊 -鳌 -鳍 -鳎 -鳏 -鳐 -鳑 -鳒 -鳓 -鳔 -鳕 -鳖 -鳗 -鳘 -鳙 -鳚 -鳛 -鳜 -鳝 -鳞 -鳟 -鳠 -鳡 -鳢 -鳣 -鳤 -鸟 -鸠 -鸡 -鸢 -鸣 -鸤 -鸥 -鸦 -鸧 -鸨 -鸩 -鸪 -鸫 -鸬 -鸭 -鸮 -鸯 -鸰 -鸱 -鸲 -鸳 -鸵 -鸶 -鸷 -鸸 -鸹 -鸺 -鸻 -鸼 -鸽 -鸾 -鸿 -鹀 -鹁 -鹂 -鹃 -鹄 -鹅 -鹆 -鹇 -鹈 -鹉 -鹊 -鹋 -鹌 -鹍 -鹎 -鹏 -鹐 -鹑 -鹒 -鹔 -鹕 -鹖 -鹗 -鹘 -鹙 -鹚 -鹛 -鹜 -鹝 -鹞 -鹟 -鹠 -鹡 -鹢 -鹣 -鹤 -鹦 -鹧 -鹨 -鹩 -鹪 -鹫 -鹬 -鹭 -鹮 -鹯 -鹰 -鹱 -鹲 -鹳 -鹴 -鹾 -鹿 -麀 -麂 -麇 -麈 -麋 -麑 -麒 -麓 -麖 -麝 -麟 -麦 -麸 -麹 -麻 -麽 -麾 -黄 -黇 -黉 -黍 -黎 -黏 -黑 -黔 -默 -黛 -黜 -黝 -黟 -黠 -黡 -黢 -黥 -黧 -黩 -黪 -黯 -黹 -黻 -黼 -黾 -鼋 -鼍 -鼎 -鼐 -鼒 -鼓 -鼗 -鼙 -鼠 -鼢 -鼩 -鼫 -鼬 -鼯 -鼱 -鼷 -鼹 -鼻 -鼽 -鼾 -齁 -齇 -齉 -齐 -齑 -齿 -龀 -龁 -龂 -龃 -龄 -龅 -龆 -龇 -龈 -龉 -龊 -龋 -龌 -龙 -龚 -龛 -龟 -龠 -龢 -鿍 -鿎 -鿏 -㑇 -㑊 -㕮 -㘎 -㙍 -㙘 -㙦 -㛃 -㛚 -㛹 -㟃 -㠇 -㠓 -㤘 -㥄 -㧐 -㧑 -㧟 -㫰 -㬊 -㬎 -㬚 -㭎 -㭕 -㮾 -㰀 -㳇 -㳘 -㳚 -㴔 -㵐 -㶲 -㸆 -㸌 -㺄 -㻬 -㽏 -㿠 -䁖 -䂮 -䃅 -䃎 -䅟 -䌹 -䎃 -䎖 -䏝 -䏡 -䏲 -䐃 -䓖 -䓛 -䓨 -䓫 -䓬 -䗖 -䗛 -䗪 -䗴 -䜣 -䝙 -䢺 -䢼 -䣘 -䥽 -䦃 -䲟 -䲠 -䲢 -䴓 -䴔 -䴕 -䴖 -䴗 -䴘 -䴙 -䶮 -𠅤 -𠙶 -𠳐 -𡎚 -𡐓 -𣗋 -𣲗 -𣲘 -𣸣 -𤧛 -𤩽 -𤫉 -𥔲 -𥕢 -𥖨 -𥻗 -𦈡 -𦒍 -𦙶 -𦝼 -𦭜 -𦰡 -𧿹 -𨐈 -𨙸 -𨚕 -𨟠 -𨭉 -𨱇 -𨱏 -𨱑 -𨱔 -𨺙 -𩽾 -𩾃 -𩾌 -𪟝 -𪣻 -𪤗 -𪨰 -𪨶 -𪩘 -𪾢 -𫄧 -𫄨 -𫄷 -𫄸 -𫇭 -𫌀 -𫍣 -𫍯 -𫍲 -𫍽 -𫐄 -𫐐 -𫐓 -𫑡 -𫓧 -𫓯 -𫓶 -𫓹 -𫔍 -𫔎 -𫔶 -𫖮 -𫖯 -𫖳 -𫗧 -𫗴 -𫘜 -𫘝 -𫘦 -𫘧 -𫘨 -𫘪 -𫘬 -𫚕 -𫚖 -𫚭 -𫛭 -𫞩 -𫟅 -𫟦 -𫟹 -𫟼 -𫠆 -𫠊 -𫠜 -𫢸 -𫫇 -𫭟 -𫭢 -𫭼 -𫮃 -𫰛 -𫵷 -𫶇 -𫷷 -𫸩 -𬀩 -𬀪 -𬂩 -𬃊 -𬇕 -𬇙 -𬇹 -𬉼 -𬊈 -𬊤 -𬌗 -𬍛 -𬍡 -𬍤 -𬒈 -𬒔 -𬒗 -𬕂 -𬘓 -𬘘 -𬘡 -𬘩 -𬘫 -𬘬 -𬘭 -𬘯 -𬙂 -𬙊 -𬙋 -𬜬 -𬜯 -𬞟 -𬟁 -𬟽 -𬣙 -𬣞 -𬣡 -𬣳 -𬤇 -𬤊 -𬤝 -𬨂 -𬨎 -𬩽 -𬪩 -𬬩 -𬬭 -𬬮 -𬬱 -𬬸 -𬬹 -𬬻 -𬬿 -𬭁 -𬭊 -𬭎 -𬭚 -𬭛 -𬭤 -𬭩 -𬭬 -𬭯 -𬭳 -𬭶 -𬭸 -𬭼 -𬮱 -𬮿 -𬯀 -𬯎 -𬱖 -𬱟 -𬳵 -𬳶 -𬳽 -𬳿 -𬴂 -𬴃 -𬴊 -𬶋 -𬶍 -𬶏 -𬶐 -𬶟 -𬶠 -𬶨 -𬶭 -𬶮 -𬷕 -𬸘 -𬸚 -𬸣 -𬸦 -𬸪 -𬹼 -𬺈 -𬺓 diff --git a/nemo_text_processing/text_normalization/zh/data/char/fullwidth_to_halfwidth.tsv b/nemo_text_processing/text_normalization/zh/data/char/fullwidth_to_halfwidth.tsv deleted file mode 100644 index 7b3fd042c8d6..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/char/fullwidth_to_halfwidth.tsv +++ /dev/null @@ -1,92 +0,0 @@ -! ! -" " -# # -$ $ -% % -& & -' ' -( ( -) ) -* * -+ + -, , -- - -. . -/ / -0 0 -1 1 -2 2 -3 3 -4 4 -5 5 -6 6 -7 7 -8 8 -9 9 -; ; -< < -= = -> > -? ? -@ @ -A A -B B -C C -D D -E E -F F -G G -H H -I I -J J -K K -L L -M M -N N -O O -P P -Q Q -R R -S S -T T -U U -V V -W W -X X -Y Y -Z Z -\ \ -^ ^ -_ _ -` ` -a a -b b -c c -d d -e e -f f -g g -h h -i i -j j -k k -l l -m m -n n -o o -p p -q q -r r -s s -t t -u u -v v -w w -x x -y y -z z -{ { -| | -: : -} } -~ ~ diff --git a/nemo_text_processing/text_normalization/zh/data/char/oov_tags.tsv b/nemo_text_processing/text_normalization/zh/data/char/oov_tags.tsv deleted file mode 100644 index cd6c56f53700..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/char/oov_tags.tsv +++ /dev/null @@ -1 +0,0 @@ - diff --git a/nemo_text_processing/text_normalization/zh/data/char/punctuations_zh.tsv b/nemo_text_processing/text_normalization/zh/data/char/punctuations_zh.tsv deleted file mode 100644 index 963b07d12b44..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/char/punctuations_zh.tsv +++ /dev/null @@ -1,72 +0,0 @@ -! -? -。 -。 -" -# -$ -% -& -' -( -) -* -+ -, -- -/ -: -; -< -= -> -@ -[ -\ -] -^ -_ -` -{ -| -} -~ -⦅ -⦆ -「 -」 -、 -、 -〃 -》 -「 -」 -『 -』 -【 -】 -〔 -〕 -〖 -〗 -〘 -〙 -〚 -〛 -〜 -〝 -〞 -〟 -〰 -– -— -‘ -’ -‛ -“ -” -„ -‟ -… -‧ -﹏ diff --git a/nemo_text_processing/text_normalization/zh/data/char/upper_to_lower.tsv b/nemo_text_processing/text_normalization/zh/data/char/upper_to_lower.tsv deleted file mode 100644 index 1b603c530f08..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/char/upper_to_lower.tsv +++ /dev/null @@ -1,9 +0,0 @@ -壹 一 -贰 二 -叁 三 -肆 四 -伍 五 -陆 六 -柒 七 -捌 八 -玖 九 diff --git a/nemo_text_processing/text_normalization/zh/data/date/__init__.py b/nemo_text_processing/text_normalization/zh/data/date/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/date/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/zh/data/date/year_suffix.tsv b/nemo_text_processing/text_normalization/zh/data/date/year_suffix.tsv deleted file mode 100644 index 2ae02ddf63e5..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/date/year_suffix.tsv +++ /dev/null @@ -1,6 +0,0 @@ -来 -前 -后 -内 -之后 - diff --git a/nemo_text_processing/text_normalization/zh/data/denylist/__init__.py b/nemo_text_processing/text_normalization/zh/data/denylist/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/denylist/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/zh/data/denylist/denylist.tsv b/nemo_text_processing/text_normalization/zh/data/denylist/denylist.tsv deleted file mode 100644 index b419181407b9..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/denylist/denylist.tsv +++ /dev/null @@ -1,2 +0,0 @@ -啊 -呃 \ No newline at end of file diff --git a/nemo_text_processing/text_normalization/zh/data/erhua/__init__.py b/nemo_text_processing/text_normalization/zh/data/erhua/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/erhua/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/zh/data/erhua/whitelist.tsv b/nemo_text_processing/text_normalization/zh/data/erhua/whitelist.tsv deleted file mode 100644 index f00ae31a5bff..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/erhua/whitelist.tsv +++ /dev/null @@ -1,35 +0,0 @@ -儿女 -儿子 -儿孙 -女儿 -儿媳 -妻儿 -胎儿 -婴儿 -新生儿 -婴幼儿 -幼儿 -少儿 -小儿 -儿歌 -儿童 -儿科 -托儿所 -孤儿 -儿戏 -儿化 -台儿庄 -鹿儿岛 -正儿八经 -吊儿郎当 -生儿育女 -托儿带女 -养儿防老 -痴儿呆女 -佳儿佳妇 -儿怜兽扰 -儿无常父 -儿不嫌母丑 -儿行千里母担忧 -儿大不由爷 -苏乞儿 diff --git a/nemo_text_processing/text_normalization/zh/data/math/__init__.py b/nemo_text_processing/text_normalization/zh/data/math/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/math/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/zh/data/math/score.tsv b/nemo_text_processing/text_normalization/zh/data/math/score.tsv deleted file mode 100644 index 0647248d5c49..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/math/score.tsv +++ /dev/null @@ -1,2 +0,0 @@ -: 比 -: 比 diff --git a/nemo_text_processing/text_normalization/zh/data/math/symbol.tsv b/nemo_text_processing/text_normalization/zh/data/math/symbol.tsv deleted file mode 100644 index 7f16f52a486d..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/math/symbol.tsv +++ /dev/null @@ -1,7 +0,0 @@ -+ 加 -~ 到 -± 正负 -= 等于 -× 乘 -÷ 除 -° 度 diff --git a/nemo_text_processing/text_normalization/zh/data/measure/__init__.py b/nemo_text_processing/text_normalization/zh/data/measure/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/measure/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/zh/data/measure/units_en.tsv b/nemo_text_processing/text_normalization/zh/data/measure/units_en.tsv deleted file mode 100644 index 09c62fb5757f..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/measure/units_en.tsv +++ /dev/null @@ -1,90 +0,0 @@ -amu 原子质量 -bar 巴 -° 度 -º 度 -°c 摄氏度 -°C 摄氏度 -ºc 摄氏度 -ºC 摄氏度 -℃ 摄氏度 -cm2 平方厘米 -cm² 平方厘米 -cm3 立方厘米 -cm³ 立方厘米 -cm 厘米 -cwt 美担 -db 分贝 -dm3 立方分米 -dm³ 立方分米 -dm 分米 -ds 毫秒 -°f 华氏度 -°F 华氏度 -℉ 华氏度 -ft 英尺 -ghz 吉赫兹 -gw 吉瓦 -gwh 吉瓦时 -hz 赫兹 -kbps 千比特每秒 -kcal 千卡 -kgf 千克力 -kg 千克 -khz 千赫兹 -km2 平方千米 -km² 平方千米 -km 千米 -kpa 千帕 -kwh 千瓦时 -kw 千瓦 -kW 千瓦 -lb 磅 -lbs 磅 -m2 平方米 -m² 平方米 -m3 立方米 -m³ 立方米 -mbps 兆比特每秒 -mg 毫克 -mhz 兆赫兹 -mi2 平方英里 -mi² 平方英里 -mi 英里 -min 分钟哦 -ml 毫升 -mm2 平方毫米 -mm² 平方毫米 -mol 摩尔 -mpa 兆帕 -mph 英里每小时 -ng 纳克 -nm 纳米 -ns 纳秒 -oz 盎司 -pa 帕斯卡 -rad 弧度 -rpm 转每分 -sq ft 平方英尺 -sq mi 平方英里 -sv 系沃特 -tb 太字节 -tj 万亿焦耳 -tl 台两 -v 伏特 -yd 码 -μg 微克 -μm 微米 -μs 微秒 -ω 欧米茄 -gb 吉字节 -gpa 吉帕斯卡 -gy 戈瑞 -ha 公顷 -m 米 -mm 毫米 -ms 毫秒 -mv 毫伏 -mw 毫瓦 -pg 皮克 -ps 皮秒 -s 秒 diff --git a/nemo_text_processing/text_normalization/zh/data/measure/units_zh.tsv b/nemo_text_processing/text_normalization/zh/data/measure/units_zh.tsv deleted file mode 100644 index 5ca1dd9ab9a1..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/measure/units_zh.tsv +++ /dev/null @@ -1,211 +0,0 @@ -匹 -张 -座 -回 -场 -尾 -条 -个 -首 -阙 -阵 -网 -炮 -顶 -丘 -棵 -只 -支 -袭 -辆 -挑 -担 -颗 -壳 -窠 -曲 -墙 -群 -腔 -砣 -座 -客 -贯 -扎 -捆 -刀 -令 -打 -手 -罗 -坡 -山 -岭 -江 -溪 -钟 -队 -单 -双 -对 -口 -头 -脚 -板 -跳 -枝 -件 -贴 -针 -线 -管 -名 -位 -身 -堂 -课 -本 -页 -家 -户 -层 -丝 -毫 -厘 -分 -钱 -两 -斤 -担 -铢 -石 -钧 -锱 -忽 -克 -毫 -厘 -分 -寸 -尺 -丈 -里 -寻 -常 -铺 -程 -米 -撮 -勺 -合 -升 -斗 -石 -盘 -碗 -碟 -叠 -桶 -笼 -盆 -盒 -杯 -钟 -斛 -锅 -簋 -篮 -盘 -桶 -罐 -瓶 -壶 -卮 -盏 -箩 -箱 -煲 -啖 -袋 -钵 -年 -月 -日 -季 -刻 -时 -周 -天 -秒 -分 -旬 -纪 -岁 -世 -更 -夜 -春 -夏 -秋 -冬 -代 -伏 -辈 -丸 -泡 -粒 -颗 -幢 -堆 -条 -根 -支 -道 -面 -片 -张 -颗 -块 -架 -千米 -分米 -厘米 -毫米 -微米 -纳米 -亿 -千万 -百万 -万 -千 -百 -亿块 -千万块 -百万块 -万块 -千块 -百块 -亿角 -千万角 -百万角 -万角 -千角 -百角 -亿毛 -千万毛 -百万毛 -万毛 -千毛 -百毛 -亿分 -千万分 -百万分 -万分 -千分 -百分 -亿元 -千万元 -百万元 -万元 -千元 -百元 diff --git a/nemo_text_processing/text_normalization/zh/data/money/__init__.py b/nemo_text_processing/text_normalization/zh/data/money/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/money/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/zh/data/money/currency_code.tsv b/nemo_text_processing/text_normalization/zh/data/money/currency_code.tsv deleted file mode 100644 index 238c01b48456..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/money/currency_code.tsv +++ /dev/null @@ -1,117 +0,0 @@ -ALL 阿尔巴尼亚列克 -AFN 阿富汗 阿富汗尼 -ARS 阿根廷比索 -AWG 阿鲁巴盾 -AUD 澳元 -AZN 阿塞拜疆马纳特 -BSD 巴哈马元 -BBD 巴巴多斯元 -BYN 白俄罗斯卢布 -BZD 伯利兹元 -BMD 百慕大元 -BOB 玻利维亚玻利维亚诺 -BAM 波斯尼亚和黑塞哥维那可兑换马克 -BWP 博茨瓦纳普拉 -BGN 保加利亚列弗 -BRL 巴西雷亚尔 -BND 文莱达鲁萨兰国元 -KHR 柬埔寨瑞尔 -CAD 加元 -KYD 开曼群岛元 -CLP 智利比索 -CNY 人民币 -COP 哥伦比亚比索 -CRC 哥斯达黎加科隆 -HRK 克罗地亚库纳 -CUP 古巴比索 -CZK 捷克克朗 -DKK 丹麦克朗 -DOP 多米尼加共和国比索 -XCD 东加勒比元 -EGP 埃及镑 -SVC 萨尔瓦多科隆 -EUR 欧元成员国 -FKP 福克兰群岛(马尔维纳斯)镑 -FJD 斐济元 -GHS 加纳塞地 -GIP 直布罗陀镑 -GTQ 危地马拉格查尔 -GGP 根西岛镑 -GYD 圭亚那元 -HNL 洪都拉斯伦皮拉 -HKD 港元 -HUF 匈牙利福林 -ISK 冰岛克朗 -INR 印度卢比 -IDR 印尼盾 -IRR 伊朗里亚尔 -IMP 马恩岛英镑 -ILS 以色列谢克尔 -JMD 牙买加元 -JPY 日元 -JEP 泽西镑 -KZT 哈萨克斯坦腾格 -KPW 朝鲜园 -KRW 韩元 -KGS 吉尔吉斯斯坦索姆 -LAK 老挝基普 -LBP 黎巴嫩镑 -LRD 利比里亚元 -MKD 马其顿代纳尔 -MYR 马来西亚令吉 -MUR 毛里求斯卢比 -MXN 墨西哥比索 -MNT 蒙古图格里克 -MNT 摩洛哥迪拉姆 -MZN 莫桑比克梅蒂卡尔 -NAD 纳米比亚元 -NPR 尼泊尔卢比 -ANG 荷属安的列斯盾 -NZD 新西兰元 -NIO 尼加拉瓜科尔多瓦 -NGN 尼日利亚奈拉 -NOK 挪威克朗 -OMR 阿曼里亚尔 -PKR 巴基斯坦卢比 -PAB 巴拿马巴尔博亚 -PYG 巴拉圭瓜拉尼 -PEN 秘鲁索尔 -PHP 菲律宾比索 -PLN 波兰兹罗提 -QAR 卡塔尔里亚尔 -RON 罗马尼亚列伊 -RUB 俄罗斯卢布 -SHP 圣赫勒拿镑 -SAR 沙特阿拉伯里亚尔 -RSD 塞尔维亚第纳尔 -SCR 塞舌尔卢比 -SGD 新加坡元 -SBD 所罗门群岛元 -SOS 索马里先令 -KRW 韩元 -ZAR 南非兰特 -LKR 斯里兰卡卢比 -SEK 瑞典克朗 -CHF 瑞士法郎 -SRD 苏里南元 -SYP 叙利亚镑 -TWD 新台币 -THB 泰铢 -TTD 特立尼达和多巴哥元 -TRY 土耳其里拉 -TVD 图瓦卢元 -UAH 乌克兰格里夫纳 -AED 阿联酋迪拉姆 -GBP 英镑 -USD 美元 -UYU 乌拉圭比索 -UZS 乌兹别克斯坦索姆 -VEF 委内瑞拉玻利瓦尔 -VND 越南东 -YER 也门里亚尔 -ZWD 津巴布韦元 -J¥ 日元 -JPY¥ 日元 -HK$ 港元 -A$ 澳元 -CAD$ 加元 diff --git a/nemo_text_processing/text_normalization/zh/data/money/currency_symbol.tsv b/nemo_text_processing/text_normalization/zh/data/money/currency_symbol.tsv deleted file mode 100644 index 18e2af41e3a1..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/money/currency_symbol.tsv +++ /dev/null @@ -1,63 +0,0 @@ -Lek 阿尔巴尼亚列克 -ƒ 阿鲁巴盾 -Br 白俄罗斯卢布 -BZ$ 伯利兹元 -$b 玻利维亚玻利维亚诺 -KM 波斯尼亚和黑塞哥维那可兑换马克 -P 博茨瓦纳普拉 -лв 保加利亚列弗 -R$ 巴西雷亚尔 -៛ 柬埔寨瑞尔 -¥ 人民币 -₡ 哥斯达黎加科隆 -kn 克罗地亚库纳 -₱ 古巴比索 -Kč 捷克克朗 -kr 丹麦克朗 -RD$ 多米尼加共和国比索 -€ 欧元 -¢ 加纳塞地 -Q 危地马拉格查尔 -L 洪都拉斯伦皮拉 -Ft 匈牙利福林 -₹ 印度卢比 -Rp 印尼盾 -£ 英镑 -£ 英镑 -₪ 以色列谢克尔 -J$ 牙买加元 -лв 哈萨克斯坦腾格 -₩ 朝鲜园 -лв 吉尔吉斯斯坦索姆 -₭ 老挝基普 -ден 马其顿代纳尔 -RM 马来西亚令吉 -₨ 毛里求斯卢比 -₮ 蒙古图格里克 -MT 莫桑比克梅蒂卡尔 -C$ 尼加拉瓜科尔多瓦 -₦ 尼日利亚奈拉 -₨ 巴基斯坦卢比 -B/. 巴拿马巴尔博亚 -Gs 巴拉圭瓜拉尼 -S/. 秘鲁索尔 -₱ 菲律宾比索 -zł 波兰兹罗提 -lei 罗马尼亚列伊 -₽ 卢布 -Дин. 塞尔维亚第纳尔 -S 索马里先令 -R 南非兰特 -CHF 瑞士法郎 -NT$ 新台币 -฿ 泰铢 -TT$ 特立尼达和多巴哥元 -₺ 土耳其里拉 -₴ 乌克兰格里夫纳 -$ 美元 -$U 乌拉圭比索 -лв 乌兹别克斯坦索姆 -Bs 委内瑞拉玻利瓦尔 -₫ 越南东 -Z$ 津巴布韦元 -¥ 人民币 diff --git a/nemo_text_processing/text_normalization/zh/data/number/__init__.py b/nemo_text_processing/text_normalization/zh/data/number/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/number/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/zh/data/number/digit.tsv b/nemo_text_processing/text_normalization/zh/data/number/digit.tsv deleted file mode 100644 index 3a578d35897c..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/number/digit.tsv +++ /dev/null @@ -1,9 +0,0 @@ -1 一 -2 二 -3 三 -4 四 -5 五 -6 六 -7 七 -8 八 -9 九 diff --git a/nemo_text_processing/text_normalization/zh/data/number/digit_teen.tsv b/nemo_text_processing/text_normalization/zh/data/number/digit_teen.tsv deleted file mode 100644 index 04c85dd7885a..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/number/digit_teen.tsv +++ /dev/null @@ -1,9 +0,0 @@ -1 -2 二 -3 三 -4 四 -5 五 -6 六 -7 七 -8 八 -9 九 diff --git a/nemo_text_processing/text_normalization/zh/data/number/sign.tsv b/nemo_text_processing/text_normalization/zh/data/number/sign.tsv deleted file mode 100644 index 645adbdf1996..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/number/sign.tsv +++ /dev/null @@ -1,2 +0,0 @@ -- 负 -+ 正 diff --git a/nemo_text_processing/text_normalization/zh/data/number/zero.tsv b/nemo_text_processing/text_normalization/zh/data/number/zero.tsv deleted file mode 100644 index d6b9cece55a4..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/number/zero.tsv +++ /dev/null @@ -1 +0,0 @@ -0 零 diff --git a/nemo_text_processing/text_normalization/zh/data/time/__init__.py b/nemo_text_processing/text_normalization/zh/data/time/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/time/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/zh/data/time/digit.tsv b/nemo_text_processing/text_normalization/zh/data/time/digit.tsv deleted file mode 100644 index c0ce4d77693c..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/time/digit.tsv +++ /dev/null @@ -1,10 +0,0 @@ -1 -2 -3 -4 -5 -6 -7 -8 -9 - diff --git a/nemo_text_processing/text_normalization/zh/data/time/hour.tsv b/nemo_text_processing/text_normalization/zh/data/time/hour.tsv deleted file mode 100644 index 7ed2844ca58a..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/time/hour.tsv +++ /dev/null @@ -1,24 +0,0 @@ -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 diff --git a/nemo_text_processing/text_normalization/zh/data/time/suffix.tsv b/nemo_text_processing/text_normalization/zh/data/time/suffix.tsv deleted file mode 100644 index 68ced35e3915..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/time/suffix.tsv +++ /dev/null @@ -1,10 +0,0 @@ -am 上午 -a.m. 上午 -a m 上午 -AM 上午 -A M 上午 -pm 下午 -p.m. 下午 -p m 下午 -P M 下午 -PM 下午 diff --git a/nemo_text_processing/text_normalization/zh/data/time/tens.tsv b/nemo_text_processing/text_normalization/zh/data/time/tens.tsv deleted file mode 100644 index 09c277aa6689..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/time/tens.tsv +++ /dev/null @@ -1,7 +0,0 @@ -0 -1 -2 -3 -4 -5 -6 diff --git a/nemo_text_processing/text_normalization/zh/data/time/zero.tsv b/nemo_text_processing/text_normalization/zh/data/time/zero.tsv deleted file mode 100644 index 573541ac9702..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/time/zero.tsv +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/nemo_text_processing/text_normalization/zh/data/whitelist/__init__.py b/nemo_text_processing/text_normalization/zh/data/whitelist/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/whitelist/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/zh/data/whitelist/default.tsv b/nemo_text_processing/text_normalization/zh/data/whitelist/default.tsv deleted file mode 100644 index fd7b54dfffda..000000000000 --- a/nemo_text_processing/text_normalization/zh/data/whitelist/default.tsv +++ /dev/null @@ -1,79 +0,0 @@ -O 2 O O to O -O2O O to O -P 2 P P to P -P2P P to P -CD C D -a.m. AM -am. AM -A M AM -Am AM - am AM -p.m. PM -pm. PM -Pm PM - pm PM -atm ATM -A T M ATM -Atm ATM -ufo UFO -Ufo UFO -U F O UFO -u f o UFO -m v MV -mv MV -MV MV -F I F A fifa -f i f a fifa -FIFA fifa -nba NBA -Nba NBA -NBA NBA -n b a NBA -x L XL -X L XL -x x L XXL -X X L XXL -x x x L XXXL -X X X L XXXL -Q Q QQ -qq QQ -Qq QQ -gpu GPU -GPU GPU -cpu CPU -C P U CPU -WIFI wifi -ms MS -ceo CEO -c e o CEO -C E O CEO -dv DV -d v DV -D V DV -wto WTO -w t o WTO -W T O WTO -pc PC -p c PC -P C PC -gps GPS -G P S GPS -M.V.P MVP -mvp MVP -M V P MVP -vip VIP -V I P VIP -cba CBA -c b a CBA -CBA CBA -mba MBA -m b a MBA -M B A MBA -iq IQ -IQ IQ -EQ EQ -cctv CCTV -C C t v CCTV -kfc KFC -K F C KFC -Steam steam diff --git a/nemo_text_processing/text_normalization/zh/graph_utils.py b/nemo_text_processing/text_normalization/zh/graph_utils.py deleted file mode 100644 index 4ca62dbe63e6..000000000000 --- a/nemo_text_processing/text_normalization/zh/graph_utils.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os -import string -from pathlib import Path -from typing import Dict - -import pynini -from pynini import Far -from pynini.export import export -from pynini.lib import byte, pynutil, utf8 - -NEMO_CHAR = utf8.VALID_UTF8_CHAR - -NEMO_DIGIT = byte.DIGIT -NEMO_LOWER = pynini.union(*string.ascii_lowercase).optimize() -NEMO_UPPER = pynini.union(*string.ascii_uppercase).optimize() -NEMO_ALPHA = pynini.union(NEMO_LOWER, NEMO_UPPER).optimize() - -NEMO_SPACE = " " -NEMO_WHITE_SPACE = pynini.union(" ", "\t", "\n", "\r", u"\u00A0").optimize() -NEMO_NOT_SPACE = pynini.difference(NEMO_CHAR, NEMO_WHITE_SPACE).optimize() -NEMO_NOT_QUOTE = pynini.difference(NEMO_CHAR, r'"').optimize() - -NEMO_PUNCT = pynini.union(*map(pynini.escape, string.punctuation)).optimize() - - -NEMO_SIGMA = pynini.closure(NEMO_CHAR) - -delete_space = pynutil.delete(pynini.closure(NEMO_WHITE_SPACE)) -delete_zero_or_one_space = pynutil.delete(pynini.closure(NEMO_WHITE_SPACE, 0, 1)) -insert_space = pynutil.insert(" ") -delete_extra_space = pynini.cross(pynini.closure(NEMO_WHITE_SPACE, 1), " ") - - -def generator_main(file_name: str, graphs: Dict[str, 'pynini.FstLike']): - """ - Exports graph as OpenFst finite state archive (FAR) file with given file name and rule name. - - Args: - file_name: exported file name - graphs: Mapping of a rule name and Pynini WFST graph to be exported - """ - exporter = export.Exporter(file_name) - for rule, graph in graphs.items(): - exporter[rule] = graph.optimize() - exporter.close() - print(f'Created {file_name}') - - -class GraphFst: - """ - Base class for all grammar fsts. - - Args: - name: name of grammar class - kind: either 'classify' or 'verbalize' - deterministic: if True will provide a single transduction option, - for False multiple transduction are generated (used for audio-based normalization) - """ - - def __init__(self, name: str, kind: str, deterministic: bool = True): - self.name = name - self.kind = kind - self._fst = None - self.deterministic = deterministic - - self.far_path = Path(os.path.dirname(__file__) + '/grammars/' + kind + '/' + name + '.far') - if self.far_exist(): - self._fst = Far(self.far_path, mode="r", arc_type="standard", far_type="default").get_fst() - - def far_exist(self) -> bool: - """ - Returns true if FAR can be loaded - """ - return self.far_path.exists() - - @property - def fst(self) -> 'pynini.FstLike': - return self._fst - - @fst.setter - def fst(self, fst): - self._fst = fst - - def add_tokens(self, fst) -> 'pynini.FstLike': - """ - Wraps class name around to given fst - - Args: - fst: input fst - - Returns: - Fst: fst - """ - return pynutil.insert(f"{self.name} {{ ") + fst + pynutil.insert(" }") - - def delete_tokens(self, fst) -> 'pynini.FstLike': - """ - Deletes class name wrap around output of given fst - - Args: - fst: input fst - - Returns: - Fst: fst - """ - res = ( - pynutil.delete(f"{self.name}") - + delete_space - + pynutil.delete("{") - + delete_space - + fst - + delete_space - + pynutil.delete("}") - ) - return res @ pynini.cdrewrite(pynini.cross(u"\u00A0", " "), "", "", NEMO_SIGMA) diff --git a/nemo_text_processing/text_normalization/zh/taggers/__init__.py b/nemo_text_processing/text_normalization/zh/taggers/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/zh/taggers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/zh/taggers/cardinal.py b/nemo_text_processing/text_normalization/zh/taggers/cardinal.py deleted file mode 100644 index c593340c6e7a..000000000000 --- a/nemo_text_processing/text_normalization/zh/taggers/cardinal.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.zh.graph_utils import GraphFst -from nemo_text_processing.text_normalization.zh.utils import UNIT_1e01, UNIT_1e02, UNIT_1e03, UNIT_1e04, get_abs_path -from pynini.lib import pynutil - - -class Cardinal(GraphFst): - ''' - self.graph_cardinal: - 5 -> 五 - 12 -> 十二 - 213 -> 二百一十三 - 3123 -> 三千一百二十三 - 51234 -> 五万一千二百三十四 - 51,234 -> 五万一千二百三十四 - 0.125 -> 零点一二五 - self.fst: - 123 -> cardinal{ integer: "一二三" } - ''' - - def __init__(self, deterministic: bool = True, lm: bool = False): - super().__init__(name="cardinal", kind="classify", deterministic=deterministic) - # base cardinal from digit to chinese - graph_digit = pynini.string_file(get_abs_path("data/number/digit.tsv")) - graph_zero = pynini.string_file(get_abs_path("data/number/zero.tsv")) - graph_teen = pynini.string_file(get_abs_path("data/number/digit_teen.tsv")) - graph_sign = pynini.string_file(get_abs_path("data/number/sign.tsv")) - - graph_digit_with_zero = graph_digit | graph_zero # from 0(read out) to 9 - graph_no_zero = pynini.cross("0", "") # cardinalzero,in some case we don't read it out - graph_digit_no_zero = graph_digit | graph_no_zero # from 0(not read out) to 9 - insert_zero = pynutil.insert('零') - delete_punct = pynutil.delete(",") # to deal with 5,000,cardinalin english form - - # 15 in 215 - graph_ten_u = (graph_digit + pynutil.insert(UNIT_1e01) + graph_digit_no_zero) | (graph_zero + graph_digit) - # 15 only - graph_ten = (graph_teen + pynutil.insert(UNIT_1e01) + graph_digit_no_zero) | (graph_zero + graph_digit) - # 215 or 200 - graph_hundred = (graph_digit + pynutil.insert(UNIT_1e02) + graph_ten_u) | ( - graph_digit + pynutil.insert(UNIT_1e02) + graph_no_zero ** 2 - ) - # 3000 or 3002 or 3012 or 3123 - # both with 3,000 or 3,002 or 3,012 or 3,123 - graph_thousand_sign = ( - (graph_digit + pynutil.insert(UNIT_1e03) + delete_punct + graph_hundred) - | ( - graph_digit - + pynutil.insert(UNIT_1e03) - + delete_punct.ques - + graph_zero - + graph_digit - + pynutil.insert(UNIT_1e01) - + graph_digit_no_zero - ) - | (graph_digit + pynutil.insert(UNIT_1e03) + delete_punct.ques + graph_zero + graph_no_zero + graph_digit) - | (graph_digit + pynutil.insert(UNIT_1e03) + delete_punct.ques + graph_no_zero ** 3) - ) - # 20001234 or 2001234 or 201234 or 21234 or 20234 or 20023 or 20002 or 20000 - # 8 digits max supported,for 9 digits cardinal,often write with unit instead of read it in only cardinalform - graph_ten_thousand_sign = ( - (graph_thousand_sign | graph_hundred | graph_ten | graph_digit_no_zero) - + pynutil.insert(UNIT_1e04) - + ( - graph_thousand_sign - | (graph_no_zero + delete_punct.ques + insert_zero + graph_hundred) - | ( - graph_no_zero - + delete_punct.ques - + graph_no_zero - + insert_zero - + (graph_digit + pynutil.insert(UNIT_1e01) + graph_digit_no_zero) - ) - | (graph_no_zero + delete_punct.ques + graph_no_zero ** 2 + insert_zero + graph_digit) - | (graph_no_zero ** 4) - ) - ) - - # cardinalstring like 123 or 123.456,used in phone cardinal,ID cardinal,etc. - graph_numstring = pynini.closure(graph_digit_with_zero, 1) | ( - pynini.closure(graph_digit_with_zero, 1) - + pynini.cross(".", "点") - + pynini.closure(graph_digit_with_zero, 1) - ) - - graph = graph_hundred | graph_ten | graph_digit_with_zero | graph_thousand_sign | graph_ten_thousand_sign - # 456.123 - graph_decimal = graph + pynini.cross('.', '点') + pynini.closure(graph_digit_with_zero, 1) - graph_cardinal = graph | graph_decimal - signed_cardinal = graph_sign + graph_cardinal - self.graph_cardinal = (graph_cardinal | signed_cardinal).optimize() - - graph_numstring = self.add_tokens(pynutil.insert("integer: \"") + graph_numstring + pynutil.insert("\"")) - self.fst = graph_numstring.optimize() diff --git a/nemo_text_processing/text_normalization/zh/taggers/char.py b/nemo_text_processing/text_normalization/zh/taggers/char.py deleted file mode 100644 index f9ec516f39e6..000000000000 --- a/nemo_text_processing/text_normalization/zh/taggers/char.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from nemo_text_processing.text_normalization.zh.graph_utils import NEMO_CHAR, GraphFst -from pynini.lib import pynutil - - -class Char(GraphFst): - ''' - 你 -> char { name: "你" } - ''' - - def __init__(self, deterministic: bool = True, lm: bool = False): - super().__init__(name="char", kind="classify", deterministic=deterministic) - - graph = pynutil.insert("name: \"") + NEMO_CHAR + pynutil.insert("\"") - self.fst = graph.optimize() diff --git a/nemo_text_processing/text_normalization/zh/taggers/date.py b/nemo_text_processing/text_normalization/zh/taggers/date.py deleted file mode 100644 index 965f4db57dce..000000000000 --- a/nemo_text_processing/text_normalization/zh/taggers/date.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.zh.graph_utils import NEMO_CHAR, NEMO_DIGIT, GraphFst, insert_space -from nemo_text_processing.text_normalization.zh.utils import get_abs_path -from pynini.lib import pynutil - - -class Date(GraphFst): - ''' - 2002年 -> tokens { date { year: "2002" } } - 2002-01-28 -> tokens { date { year: "2002" month: "01" day: "28"} } - 2002/01/28 -> tokens { date { year: "2002" month: "01" day: "28"} } - 2002.01.28 -> tokens { date { year: "2002" month: "01" day: "28"} } - 2002/02 -> tokens { date { year: "2002" month "02"} } - 02/11 -> tokens { date { year: "02" month "11"} } different with case "fraction 2/11" - ''' - - def __init__(self, deterministic: bool = True, lm: bool = False): - super().__init__(name="date", kind="classify", deterministic=deterministic) - graph_digit = pynini.string_file(get_abs_path("data/number/digit.tsv")) - graph_zero = pynini.string_file(get_abs_path("data/number/zero.tsv")) - year_whitelist = pynini.string_file(get_abs_path("data/date/year_suffix.tsv")) - - delete_date_sign = pynutil.delete("/") | pynutil.delete('-') | pynutil.delete('.') - - # 2012年 - date_type0 = ( - pynutil.insert("year: \"") - + pynini.closure(graph_digit | graph_zero, 2, 4) - + "年" - + pynini.difference(NEMO_CHAR, year_whitelist) - + pynutil.insert("\"") - ) - - year_2_4_digit = pynini.closure(NEMO_DIGIT, 2, 4) + delete_date_sign - year_4_digit = pynini.closure(NEMO_DIGIT, 4, 4) + delete_date_sign - year_2_digit_with_zero = "0" + NEMO_DIGIT + delete_date_sign - month_no_day_with_zero = "0" + NEMO_DIGIT - month_no_day = pynini.closure(NEMO_DIGIT, 2, 2) - month = pynini.closure(NEMO_DIGIT, 1, 2) + delete_date_sign - day = pynini.closure(NEMO_DIGIT, 1, 2) - - # 2012/01/28 - date_type1 = ( - pynutil.insert("year: \"") - + year_2_4_digit - + pynutil.insert("\"") - + insert_space - + pynutil.insert("month: \"") - + month - + pynutil.insert("\"") - + insert_space - + pynutil.insert("day: \"") - + day - + pynutil.insert("\"") - ) - - # 12/01 - date_type2 = ( - pynutil.insert("year: \"") - + year_2_4_digit - + pynutil.insert("\"") - + insert_space - + pynutil.insert("month: \"") - + month_no_day_with_zero - + pynutil.insert("\"") - ) - - # 2012/11 - date_type3 = ( - pynutil.insert("year: \"") - + year_4_digit - + pynutil.insert("\"") - + insert_space - + pynutil.insert("month: \"") - + month_no_day - + pynutil.insert("\"") - ) - - # 02/05 - date_type4 = ( - pynutil.insert("year: \"") - + year_2_digit_with_zero - + pynutil.insert("\"") - + insert_space - + pynutil.insert("month: \"") - + month_no_day - + pynutil.insert("\"") - ) - # add your date type as date_typex here. - graph = date_type0 | date_type1 | date_type2 | date_type3 | date_type4 - - self.fst = self.add_tokens(graph).optimize() diff --git a/nemo_text_processing/text_normalization/zh/taggers/fraction.py b/nemo_text_processing/text_normalization/zh/taggers/fraction.py deleted file mode 100644 index bfae533a1fda..000000000000 --- a/nemo_text_processing/text_normalization/zh/taggers/fraction.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.zh.graph_utils import NEMO_DIGIT, GraphFst, insert_space -from pynini.lib import pynutil - - -class Fraction(GraphFst): - ''' - 1/5 -> tokens { fraction { numerator: "1" denominator: "5" } } - ''' - - def __init__(self, deterministic: bool = True, lm: bool = False): - super().__init__(name="fraction", kind="classify", deterministic=deterministic) - - numerator = pynini.closure(NEMO_DIGIT, 1) + pynutil.delete('/') - denominator = pynini.closure(NEMO_DIGIT, 1) - graph = ( - pynutil.insert("numerator: \"") - + numerator - + pynutil.insert("\"") - + insert_space - + pynutil.insert("denominator: \"") - + denominator - + pynutil.insert("\"") - ) - - self.fst = self.add_tokens(graph).optimize() diff --git a/nemo_text_processing/text_normalization/zh/taggers/math_symbol.py b/nemo_text_processing/text_normalization/zh/taggers/math_symbol.py deleted file mode 100644 index 28d223ae36e5..000000000000 --- a/nemo_text_processing/text_normalization/zh/taggers/math_symbol.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.zh.graph_utils import GraphFst -from nemo_text_processing.text_normalization.zh.taggers.cardinal import Cardinal -from nemo_text_processing.text_normalization.zh.utils import get_abs_path -from pynini.lib import pynutil - - -class MathSymbol(GraphFst): - ''' - + -> tokens { sign: "加" } - ''' - - def __init__(self, deterministic: bool = True, lm: bool = False): - super().__init__(name="sign", kind="classify", deterministic=deterministic) - ''' - add your sign in data/math/symbol.tsv,this graph just convert sigh to character,you can add more - cases with detailed cases - ''' - score_sign = pynini.string_file(get_abs_path("data/math/score.tsv")) - score = ( - pynutil.insert("score: \"") - + Cardinal().graph_cardinal - + score_sign - + Cardinal().graph_cardinal - + pynutil.insert("\"") - ) - graph = score - self.fst = graph.optimize() diff --git a/nemo_text_processing/text_normalization/zh/taggers/measure.py b/nemo_text_processing/text_normalization/zh/taggers/measure.py deleted file mode 100644 index 503bd2b3f5eb..000000000000 --- a/nemo_text_processing/text_normalization/zh/taggers/measure.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.zh.graph_utils import GraphFst, insert_space -from nemo_text_processing.text_normalization.zh.taggers.cardinal import Cardinal -from nemo_text_processing.text_normalization.zh.utils import get_abs_path -from pynini.lib import pynutil - - -class Measure(GraphFst): - ''' - 1kg -> tokens { measure { cardinal { integer: "一" } units: "千克" } } - ''' - - def __init__(self, deterministic: bool = True, lm: bool = False): - super().__init__(name="measure", kind="classify", deterministic=deterministic) - - units_en = pynini.string_file(get_abs_path("data/measure/units_en.tsv")) - units_zh = pynini.string_file(get_abs_path("data/measure/units_zh.tsv")) - graph = ( - pynutil.insert("cardinal { ") - + pynutil.insert("integer: \"") - + Cardinal().graph_cardinal - + pynutil.insert("\"") - + pynutil.insert(" }") - + insert_space - + pynutil.insert("units: \"") - + (units_en | units_zh) - + pynutil.insert("\"") - ) - percent_graph = ( - pynutil.insert("decimal { ") - + pynutil.insert("integer_part: \"") - + Cardinal().graph_cardinal - + pynutil.delete("%") - + pynutil.insert("\"") - + pynutil.insert(" }") - ) - graph |= percent_graph - - self.fst = self.add_tokens(graph).optimize() diff --git a/nemo_text_processing/text_normalization/zh/taggers/money.py b/nemo_text_processing/text_normalization/zh/taggers/money.py deleted file mode 100644 index c81dae22626b..000000000000 --- a/nemo_text_processing/text_normalization/zh/taggers/money.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.zh.graph_utils import GraphFst, insert_space -from nemo_text_processing.text_normalization.zh.taggers.cardinal import Cardinal -from nemo_text_processing.text_normalization.zh.utils import get_abs_path -from pynini.lib import pynutil - - -class Money(GraphFst): - ''' - ¥1.25 -> tokens { money { fractional_part: "元" integer_part: "一点五" } } - ''' - - def __init__(self, deterministic: bool = True, lm: bool = False): - super().__init__(name="money", kind="classify", deterministic=deterministic) - - currency_code = pynini.string_file(get_abs_path("data/money/currency_code.tsv")) - currency_symbol = pynini.string_file(get_abs_path("data/money/currency_symbol.tsv")) - graph = ( - pynutil.insert("fractional_part: \"") - + (currency_code | currency_symbol) - + pynutil.insert("\"") - + insert_space - + pynutil.insert("integer_part: \"") - + Cardinal().graph_cardinal - + pynutil.insert("\"") - ) - - self.fst = self.add_tokens(graph).optimize() diff --git a/nemo_text_processing/text_normalization/zh/taggers/preprocessor.py b/nemo_text_processing/text_normalization/zh/taggers/preprocessor.py deleted file mode 100644 index 57d672dc62e1..000000000000 --- a/nemo_text_processing/text_normalization/zh/taggers/preprocessor.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.zh.graph_utils import NEMO_SIGMA, GraphFst -from nemo_text_processing.text_normalization.zh.utils import get_abs_path -from pynini.lib import pynutil - - -class PreProcessor(GraphFst): - ''' - Preprocessing of TN: - 1. interjections removal such as '啊, 呃' - 2. fullwidth -> halfwidth char conversion - 好啊 -> 好 - 呃对 -> 对 - : -> : - ; -> ; - ''' - - def __init__( - self, remove_interjections: bool = True, fullwidth_to_halfwidth: bool = True, - ): - super().__init__(name="PreProcessor", kind="processor") - - graph = pynini.cdrewrite('', '', '', NEMO_SIGMA) - - if remove_interjections: - remove_interjections_graph = pynutil.delete(pynini.string_file(get_abs_path('data/denylist/denylist.tsv'))) - graph @= pynini.cdrewrite(remove_interjections_graph, '', '', NEMO_SIGMA) - - if fullwidth_to_halfwidth: - fullwidth_to_halfwidth_graph = pynini.string_file(get_abs_path('data/char/fullwidth_to_halfwidth.tsv')) - graph @= pynini.cdrewrite(fullwidth_to_halfwidth_graph, '', '', NEMO_SIGMA) - - self.fst = graph.optimize() diff --git a/nemo_text_processing/text_normalization/zh/taggers/time.py b/nemo_text_processing/text_normalization/zh/taggers/time.py deleted file mode 100644 index f9fadccb9027..000000000000 --- a/nemo_text_processing/text_normalization/zh/taggers/time.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.zh.graph_utils import GraphFst, insert_space -from nemo_text_processing.text_normalization.zh.utils import get_abs_path -from pynini.lib import pynutil - - -class Time(GraphFst): - ''' - 1:02 -> tokens { time { hours: "1" minitus: "02" } } - 1:02:36 -> tokens { time { hours: "1" minutes: "02" seconds: "36" } } - 1:02 am -> tokens { time { hours: "1" minutes: "02" seconds: "36" suffix "am" } } - ''' - - def __init__(self, deterministic: bool = True, lm: bool = False): - super().__init__(name="time", kind="classify", deterministic=deterministic) - - h = pynini.string_file(get_abs_path("data/time/hour.tsv")) - time_tens = pynini.string_file(get_abs_path("data/time/tens.tsv")) - time_digit = pynini.string_file(get_abs_path("data/time/digit.tsv")) - time_zero = pynini.string_file(get_abs_path("data/time/zero.tsv")) - time_digit = time_digit | time_zero - time_suffix = pynini.string_file(get_abs_path("data/time/suffix.tsv")) - - m = time_tens + time_digit - s = (time_tens + time_digit) | time_digit - - delete_colon = pynini.cross(':', ' ') - - # 5:05, 14:30 - h_m = ( - pynutil.insert('hours: \"') - + h - + pynutil.insert('\"') - + delete_colon - + pynutil.insert('minutes: \"') - + m - + pynutil.insert('\"') - ) - - # 1:30:15 - h_m_s = ( - pynutil.insert('hours: \"') - + h - + pynutil.insert('\"') - + delete_colon - + pynutil.insert('minutes: \"') - + m - + pynutil.insert('\"') - + delete_colon - + pynutil.insert('seconds: \"') - + s - + pynutil.insert('\"') - ) - - graph = h_m | h_m_s - graph_suffix = graph + insert_space + pynutil.insert('suffix: \"') + time_suffix + pynutil.insert('\"') - graph |= graph_suffix - self.fst = self.add_tokens(graph).optimize() diff --git a/nemo_text_processing/text_normalization/zh/taggers/tokenize_and_classify.py b/nemo_text_processing/text_normalization/zh/taggers/tokenize_and_classify.py deleted file mode 100644 index 2d3c13e0acbb..000000000000 --- a/nemo_text_processing/text_normalization/zh/taggers/tokenize_and_classify.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import pynini -from nemo_text_processing.text_normalization.zh.graph_utils import GraphFst -from nemo_text_processing.text_normalization.zh.taggers.cardinal import Cardinal -from nemo_text_processing.text_normalization.zh.taggers.char import Char -from nemo_text_processing.text_normalization.zh.taggers.date import Date -from nemo_text_processing.text_normalization.zh.taggers.fraction import Fraction -from nemo_text_processing.text_normalization.zh.taggers.math_symbol import MathSymbol -from nemo_text_processing.text_normalization.zh.taggers.measure import Measure -from nemo_text_processing.text_normalization.zh.taggers.money import Money -from nemo_text_processing.text_normalization.zh.taggers.preprocessor import PreProcessor -from nemo_text_processing.text_normalization.zh.taggers.time import Time -from nemo_text_processing.text_normalization.zh.taggers.whitelist import Whitelist -from pynini.lib import pynutil - - -class ClassifyFst(GraphFst): - """ - Final class that composes all other classification grammars. This class can process an entire sentence including punctuation. - For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - - Args: - input_case: accepting either "lower_cased" or "cased" input. - deterministic: if True will provide a single transduction option, - for False multiple options (used for audio-based normalization) - cache_dir: path to a dir with .far grammar file. Set to None to avoid using cache. - overwrite_cache: set to True to overwrite .far files - whitelist: path to a file with whitelist replacements - """ - - def __init__( - self, - input_case: str, - deterministic: bool = True, - cache_dir: str = None, - overwrite_cache: bool = False, - whitelist: str = None, - ): - super().__init__(name="tokenize_and_classify", kind="classify", deterministic=deterministic) - - far_file = None - if cache_dir is not None and cache_dir != "None": - os.makedirs(cache_dir, exist_ok=True) - whitelist_file = os.path.basename(whitelist) if whitelist else "" - far_file = os.path.join( - cache_dir, f"zh_tn_{deterministic}_deterministic_{input_case}_{whitelist_file}_tokenize.far" - ) - if not overwrite_cache and far_file and os.path.exists(far_file): - self.fst = pynini.Far(far_file, mode="r")["tokenize_and_classify"] - else: - date = Date(deterministic=deterministic) - cardinal = Cardinal(deterministic=deterministic) - char = Char(deterministic=deterministic) - fraction = Fraction(deterministic=deterministic) - math_symbol = MathSymbol(deterministic=deterministic) - money = Money(deterministic=deterministic) - measure = Measure(deterministic=deterministic) - time = Time(deterministic=deterministic) - whitelist = Whitelist(deterministic=deterministic) - - classify = pynini.union( - pynutil.add_weight(date.fst, 1.02), - pynutil.add_weight(fraction.fst, 1.05), - pynutil.add_weight(money.fst, 1.05), - pynutil.add_weight(measure.fst, 1.05), - pynutil.add_weight(time.fst, 1.05), - pynutil.add_weight(whitelist.fst, 1.03), - pynutil.add_weight(cardinal.fst, 1.06), - pynutil.add_weight(math_symbol.fst, 1.08), - pynutil.add_weight(char.fst, 100), - ) - token = pynutil.insert("tokens { ") + classify + pynutil.insert(" } ") - - tagger = token.optimize().star - - preprocessor = PreProcessor(remove_interjections=True, fullwidth_to_halfwidth=True,) - self.fst = preprocessor.fst @ tagger diff --git a/nemo_text_processing/text_normalization/zh/taggers/whitelist.py b/nemo_text_processing/text_normalization/zh/taggers/whitelist.py deleted file mode 100644 index 4bcfa37a7d97..000000000000 --- a/nemo_text_processing/text_normalization/zh/taggers/whitelist.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.zh.graph_utils import GraphFst -from nemo_text_processing.text_normalization.zh.utils import get_abs_path -from pynini.lib import pynutil - - -class Whitelist(GraphFst): - ''' - ATM -> tokens { whitelist: "ATM" } - ''' - - def __init__(self, deterministic: bool = True, lm: bool = False): - super().__init__(name="whitelist", kind="classify", deterministic=deterministic) - - whitelist = pynini.string_file(get_abs_path("data/whitelist/default.tsv")) - erhua = pynutil.insert("erhua: \"") + pynini.accep('儿') + pynutil.insert("\"") - sign = pynini.string_file(get_abs_path("data/math/symbol.tsv")) - whitelist = ( - pynutil.insert("name: \"") - + (pynini.string_file(get_abs_path("data/erhua/whitelist.tsv")) | whitelist | sign) - + pynutil.insert("\"") - ) - graph = pynutil.add_weight(erhua, 0.1) | whitelist - - self.fst = graph.optimize() diff --git a/nemo_text_processing/text_normalization/zh/utils.py b/nemo_text_processing/text_normalization/zh/utils.py deleted file mode 100644 index 707949ac7f0d..000000000000 --- a/nemo_text_processing/text_normalization/zh/utils.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import csv -import os - -UNIT_1e01 = '十' -UNIT_1e02 = '百' -UNIT_1e03 = '千' -UNIT_1e04 = '万' - - -def get_abs_path(rel_path): - """ - Get absolute path - - Args: - rel_path: relative path to this file - - Returns absolute path - """ - return os.path.dirname(os.path.abspath(__file__)) + '/' + rel_path - - -def load_labels(abs_path): - """ - loads relative path file as dictionary - - Args: - abs_path: absolute path - - Returns dictionary of mappings - """ - with open(abs_path, encoding="utf-8") as label_tsv: - labels = list(csv.reader(label_tsv, delimiter="\t")) - return labels - - -def augment_labels_with_punct_at_end(labels): - """ - augments labels: if key ends on a punctuation that value does not have, add a new label - where the value maintains the punctuation - - Args: - labels : input labels - Returns: - additional labels - """ - res = [] - for label in labels: - if len(label) > 1: - if label[0][-1] == "." and label[1][-1] != ".": - res.append([label[0], label[1] + "."] + label[2:]) - return res diff --git a/nemo_text_processing/text_normalization/zh/verbalizers/__init__.py b/nemo_text_processing/text_normalization/zh/verbalizers/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/nemo_text_processing/text_normalization/zh/verbalizers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/nemo_text_processing/text_normalization/zh/verbalizers/cardinal.py b/nemo_text_processing/text_normalization/zh/verbalizers/cardinal.py deleted file mode 100644 index e60b6c341039..000000000000 --- a/nemo_text_processing/text_normalization/zh/verbalizers/cardinal.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.zh.graph_utils import NEMO_NOT_QUOTE, GraphFst -from pynini.lib import pynutil - - -class Cardinal(GraphFst): - ''' - tokens { cardinal { integer: "一二三" } } -> 一二三 - ''' - - def __init__(self, deterministic: bool = True, lm: bool = False): - super().__init__(name="cardinal", kind="verbalize", deterministic=deterministic) - - graph = pynutil.delete('integer: \"') + pynini.closure(NEMO_NOT_QUOTE) + pynutil.delete('\"') - - self.fst = self.delete_tokens(graph).optimize() diff --git a/nemo_text_processing/text_normalization/zh/verbalizers/char.py b/nemo_text_processing/text_normalization/zh/verbalizers/char.py deleted file mode 100644 index 5f241f476b56..000000000000 --- a/nemo_text_processing/text_normalization/zh/verbalizers/char.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from nemo_text_processing.text_normalization.zh.graph_utils import NEMO_NOT_QUOTE, GraphFst -from pynini.lib import pynutil - - -class Char(GraphFst): - ''' - tokens { char: "你" } -> 你 - ''' - - def __init__(self, deterministic: bool = True, lm: bool = False): - super().__init__(name="char", kind="verbalize", deterministic=deterministic) - - graph = pynutil.delete("name: \"") + NEMO_NOT_QUOTE + pynutil.delete("\"") - self.fst = graph.optimize() diff --git a/nemo_text_processing/text_normalization/zh/verbalizers/date.py b/nemo_text_processing/text_normalization/zh/verbalizers/date.py deleted file mode 100644 index f808ae4033f6..000000000000 --- a/nemo_text_processing/text_normalization/zh/verbalizers/date.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.zh.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from nemo_text_processing.text_normalization.zh.utils import UNIT_1e01, get_abs_path -from pynini.lib import pynutil - - -class Date(GraphFst): - ''' - tokens { date { year: "2002" month: "01" day: "28"} } -> 二零零二年一月二十八日 - tokens { date { year: "2002" } } -> 二零零八年 - ''' - - def __init__(self, deterministic: bool = True, lm: bool = False): - super().__init__(name="date", kind="verbalize", deterministic=deterministic) - date_type0 = pynutil.delete('year: \"') + pynini.closure(NEMO_NOT_QUOTE) + pynutil.delete('\"') - graph_digit = pynini.string_file(get_abs_path("data/number/digit.tsv")) - graph_teen = pynini.string_file(get_abs_path("data/number/digit_teen.tsv")) - graph_zero = pynini.string_file(get_abs_path("data/number/zero.tsv")) - graph_no_zero = pynini.cross("0", "") - graph_year = pynini.closure(graph_digit | graph_zero, 2, 4) - graph_digit_no_zero = graph_digit | graph_no_zero - graph_2_digit_date = (graph_teen + pynutil.insert(UNIT_1e01) + graph_digit_no_zero) | ( - graph_no_zero + graph_digit - ) - - date_type1 = ( - pynutil.delete("year: \"") - + graph_year - + pynutil.insert("年") - + pynutil.delete("\"") - + delete_space - + pynutil.delete("month: \"") - + graph_2_digit_date - + pynutil.insert("月") - + pynutil.delete("\"") - + delete_space - + pynutil.delete("day: \"") - + graph_2_digit_date - + pynutil.insert("日") - + pynutil.delete("\"") - ) - - date_type2 = ( - pynutil.delete("year: \"") - + graph_year - + pynutil.insert("年") - + pynutil.delete("\"") - + delete_space - + pynutil.delete("month: \"") - + graph_2_digit_date - + pynutil.insert("月") - + pynutil.delete("\"") - ) - - graph = date_type0 | date_type1 | date_type2 - - self.fst = self.delete_tokens(graph).optimize() diff --git a/nemo_text_processing/text_normalization/zh/verbalizers/fraction.py b/nemo_text_processing/text_normalization/zh/verbalizers/fraction.py deleted file mode 100644 index ba7b0bc0998e..000000000000 --- a/nemo_text_processing/text_normalization/zh/verbalizers/fraction.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from nemo_text_processing.text_normalization.zh.graph_utils import GraphFst -from nemo_text_processing.text_normalization.zh.taggers.cardinal import Cardinal -from pynini.lib import pynutil - - -class Fraction(GraphFst): - ''' - tokens { fraction { denominator: "5" numerator: "1" } } -> 五分之一 - ''' - - def __init__(self, deterministic: bool = True, lm: bool = False): - super().__init__(name="fraction", kind="verbalize", deterministic=deterministic) - - denominator = pynutil.delete("denominator: \"") + Cardinal().graph_cardinal + pynutil.delete("\"") - numerator = pynutil.delete("numerator: \"") + Cardinal().graph_cardinal + pynutil.delete("\"") - graph = denominator + pynutil.delete(" ") + pynutil.insert("分之") + numerator - - self.fst = self.delete_tokens(graph).optimize() diff --git a/nemo_text_processing/text_normalization/zh/verbalizers/math_symbol.py b/nemo_text_processing/text_normalization/zh/verbalizers/math_symbol.py deleted file mode 100644 index 924f9b5abdb9..000000000000 --- a/nemo_text_processing/text_normalization/zh/verbalizers/math_symbol.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.zh.graph_utils import NEMO_NOT_QUOTE, GraphFst -from pynini.lib import pynutil - - -class MathSymbol(GraphFst): - ''' - tokens { sign: "加" } -> 加 - ''' - - def __init__(self, deterministic: bool = True, lm: bool = False): - super().__init__(name="sign", kind="verbalize", deterministic=deterministic) - - graph = pynutil.delete('score: \"') + pynini.closure(NEMO_NOT_QUOTE) + pynutil.delete('\"') - - self.fst = graph.optimize() diff --git a/nemo_text_processing/text_normalization/zh/verbalizers/measure.py b/nemo_text_processing/text_normalization/zh/verbalizers/measure.py deleted file mode 100644 index b7e0d156f848..000000000000 --- a/nemo_text_processing/text_normalization/zh/verbalizers/measure.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.zh.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class Measure(GraphFst): - ''' - tokens { measure { cardinal: "一" } units: "千克" } } -> 一千克 - ''' - - def __init__(self, deterministic: bool = True, lm: bool = False): - super().__init__(name="measure", kind="verbalize", deterministic=deterministic) - - graph = ( - pynutil.delete("cardinal {") - + delete_space - + pynutil.delete("integer: \"") - + pynini.closure(NEMO_NOT_QUOTE) - + pynutil.delete("\"") - + delete_space - + pynutil.delete("}") - + delete_space - + pynutil.delete("units: \"") - + pynini.closure(NEMO_NOT_QUOTE) - + pynutil.delete("\"") - ) - percent_graph = ( - pynutil.delete("decimal { ") - + pynutil.delete("integer_part: \"") - + pynutil.insert("百分之") - + pynini.closure(NEMO_NOT_QUOTE, 1) - + pynutil.delete("\"") - + delete_space - + pynutil.delete("}") - ) - graph |= percent_graph - self.fst = self.delete_tokens(graph).optimize() diff --git a/nemo_text_processing/text_normalization/zh/verbalizers/money.py b/nemo_text_processing/text_normalization/zh/verbalizers/money.py deleted file mode 100644 index 301b7db03638..000000000000 --- a/nemo_text_processing/text_normalization/zh/verbalizers/money.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.zh.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from pynini.lib import pynutil - - -class Money(GraphFst): - ''' - tokens { money { integer_part: "一点五" fractional_part: "元" } } -> 一点五元 - ''' - - def __init__(self, deterministic: bool = True, lm: bool = False): - super().__init__(name="money", kind="verbalize", deterministic=deterministic) - - cur = pynutil.delete("fractional_part: \"") + pynini.closure(NEMO_NOT_QUOTE) + pynutil.delete("\"") - num = pynutil.delete("integer_part: \"") + pynini.closure(NEMO_NOT_QUOTE) + pynutil.delete("\"") + delete_space - graph = num + cur - - self.fst = self.delete_tokens(graph).optimize() diff --git a/nemo_text_processing/text_normalization/zh/verbalizers/postprocessor.py b/nemo_text_processing/text_normalization/zh/verbalizers/postprocessor.py deleted file mode 100644 index 67b0b1954200..000000000000 --- a/nemo_text_processing/text_normalization/zh/verbalizers/postprocessor.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.zh.graph_utils import ( - NEMO_ALPHA, - NEMO_DIGIT, - NEMO_PUNCT, - NEMO_SIGMA, - NEMO_WHITE_SPACE, - GraphFst, -) -from nemo_text_processing.text_normalization.zh.utils import get_abs_path -from pynini.lib import pynutil, utf8 - - -class PostProcessor(GraphFst): - ''' - Postprocessing of TN, now contains: - 1. punctuation removal - 2. letter case conversion - 3. oov tagger - ''' - - def __init__( - self, remove_puncts: bool = False, to_upper: bool = False, to_lower: bool = False, tag_oov: bool = False, - ): - super().__init__(name="PostProcessor", kind="processor") - - graph = pynini.cdrewrite('', '', '', NEMO_SIGMA) - if remove_puncts: - remove_puncts_graph = pynutil.delete( - pynini.union(NEMO_PUNCT, pynini.string_file(get_abs_path('data/char/punctuations_zh.tsv'))) - ) - graph @= pynini.cdrewrite(remove_puncts_graph, "", "", NEMO_SIGMA).optimize() - - if to_upper or to_lower: - if to_upper: - conv_cases_graph = pynini.inverse(pynini.string_file(get_abs_path('data/char/upper_to_lower.tsv'))) - else: - conv_cases_graph = pynini.string_file(get_abs_path('data/char/upper_to_lower.tsv')) - - graph @= pynini.cdrewrite(conv_cases_graph, "", "", NEMO_SIGMA).optimize() - - if tag_oov: - zh_charset_std = pynini.string_file(get_abs_path("data/char/charset_national_standard_2013_8105.tsv")) - zh_charset_ext = pynini.string_file(get_abs_path("data/char/charset_extension.tsv")) - - zh_charset = ( - zh_charset_std | zh_charset_ext | pynini.string_file(get_abs_path("data/char/punctuations_zh.tsv")) - ) - en_charset = NEMO_DIGIT | NEMO_ALPHA | NEMO_PUNCT | NEMO_WHITE_SPACE - charset = zh_charset | en_charset - - with open(get_abs_path("data/char/oov_tags.tsv"), "r") as f: - tags = f.readline().strip().split('\t') - assert len(tags) == 2 - ltag, rtag = tags - - oov_charset = pynini.difference(utf8.VALID_UTF8_CHAR, charset) - tag_oov_graph = pynutil.insert(ltag) + oov_charset + pynutil.insert(rtag) - graph @= pynini.cdrewrite(tag_oov_graph, "", "", NEMO_SIGMA).optimize() - - self.fst = graph.optimize() diff --git a/nemo_text_processing/text_normalization/zh/verbalizers/time.py b/nemo_text_processing/text_normalization/zh/verbalizers/time.py deleted file mode 100644 index 5784d1a693a9..000000000000 --- a/nemo_text_processing/text_normalization/zh/verbalizers/time.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.zh.graph_utils import NEMO_NOT_QUOTE, GraphFst, delete_space -from nemo_text_processing.text_normalization.zh.utils import UNIT_1e01, get_abs_path -from pynini.lib import pynutil - - -class Time(GraphFst): - ''' - tokens { time { h: "1" m: "02" s: "36" } } -> 一点零二分三十六秒 - tokens { time { suffix "am" hours: "1" minutes: "02" seconds: "36" } } -> 上午一点零二分三十六秒 - ''' - - def __init__(self, deterministic: bool = True, lm: bool = False): - super().__init__(name="time", kind="verbalize", deterministic=deterministic) - graph_digit = pynini.string_file(get_abs_path("data/number/digit.tsv")) - graph_teen = pynini.string_file(get_abs_path("data/number/digit_teen.tsv")) - graph_zero = pynini.string_file(get_abs_path("data/number/zero.tsv")) - graph_no_zero = pynini.cross("0", "") - - graph_digit_no_zero = graph_digit | graph_no_zero - - graph_2_digit_zero_none = pynini.cross("0", "") + pynini.cross("0", "") - graph_2_digit_zero = pynini.cross("00", "零") - - graph_2_digit_time = (graph_teen + pynutil.insert(UNIT_1e01) + graph_digit_no_zero) | ( - graph_zero + graph_digit - ) - h = graph_2_digit_time | graph_2_digit_zero | graph_digit - m = graph_2_digit_time | graph_2_digit_zero - s = graph_2_digit_time | graph_2_digit_zero - - # 6:25 - h_m = ( - pynutil.delete("hours: \"") - + h - + pynutil.insert("点") - + pynutil.delete("\"") - + delete_space - + pynutil.delete("minutes: \"") - + (graph_2_digit_time) - + pynutil.insert("分") - + pynutil.delete("\"") - ) - - # 23:00 - h_00 = ( - pynutil.delete("hours: \"") - + h - + pynutil.insert("点") - + pynutil.delete("\"") - + delete_space - + pynutil.delete("minutes: \"") - + (graph_2_digit_zero_none) - + pynutil.delete("\"") - ) - - # 9:12:52 - h_m_s = ( - pynutil.delete("hours: \"") - + h - + pynutil.insert("点") - + pynutil.delete("\"") - + delete_space - + pynutil.delete("minutes: \"") - + m - + pynutil.insert("分") - + pynutil.delete("\"") - + delete_space - + pynutil.delete("seconds: \"") - + s - + pynutil.insert("秒") - + pynutil.delete("\"") - ) - - graph = h_m | h_m_s | h_00 - graph_suffix = ( - pynutil.delete("suffix: \"") + pynini.closure(NEMO_NOT_QUOTE) + pynutil.delete("\"") + delete_space + graph - ) - graph |= graph_suffix - self.fst = self.delete_tokens(graph).optimize() diff --git a/nemo_text_processing/text_normalization/zh/verbalizers/verbalize.py b/nemo_text_processing/text_normalization/zh/verbalizers/verbalize.py deleted file mode 100644 index dd01e63ca162..000000000000 --- a/nemo_text_processing/text_normalization/zh/verbalizers/verbalize.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.zh.graph_utils import GraphFst -from nemo_text_processing.text_normalization.zh.verbalizers.cardinal import Cardinal -from nemo_text_processing.text_normalization.zh.verbalizers.char import Char -from nemo_text_processing.text_normalization.zh.verbalizers.date import Date -from nemo_text_processing.text_normalization.zh.verbalizers.fraction import Fraction -from nemo_text_processing.text_normalization.zh.verbalizers.math_symbol import MathSymbol -from nemo_text_processing.text_normalization.zh.verbalizers.measure import Measure -from nemo_text_processing.text_normalization.zh.verbalizers.money import Money -from nemo_text_processing.text_normalization.zh.verbalizers.time import Time -from nemo_text_processing.text_normalization.zh.verbalizers.whitelist import Whitelist - - -class VerbalizeFst(GraphFst): - """ - Composes other verbalizer grammars. - For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. - More details to deployment at NeMo/tools/text_processing_deployment. - Args: - deterministic: if True will provide a single transduction option, - for False multiple options (used for audio-based normalization) - """ - - def __init__(self, deterministic: bool = True): - super().__init__(name="verbalize", kind="verbalize", deterministic=deterministic) - - date = Date(deterministic=deterministic) - cardinal = Cardinal(deterministic=deterministic) - char = Char(deterministic=deterministic) - fraction = Fraction(deterministic=deterministic) - math_symbol = MathSymbol(deterministic=deterministic) - money = Money(deterministic=deterministic) - measure = Measure(deterministic=deterministic) - time = Time(deterministic=deterministic) - whitelist = Whitelist(deterministic=deterministic) - - graph = pynini.union( - date.fst, - cardinal.fst, - fraction.fst, - char.fst, - math_symbol.fst, - money.fst, - measure.fst, - time.fst, - whitelist.fst, - ) - - self.fst = graph.optimize() diff --git a/nemo_text_processing/text_normalization/zh/verbalizers/verbalize_final.py b/nemo_text_processing/text_normalization/zh/verbalizers/verbalize_final.py deleted file mode 100644 index 2be00adecd25..000000000000 --- a/nemo_text_processing/text_normalization/zh/verbalizers/verbalize_final.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import pynini -from nemo_text_processing.text_normalization.zh.graph_utils import GraphFst, delete_space, generator_main -from nemo_text_processing.text_normalization.zh.verbalizers.postprocessor import PostProcessor -from nemo_text_processing.text_normalization.zh.verbalizers.verbalize import VerbalizeFst -from pynini.lib import pynutil - -# from nemo.utils import logging - - -class VerbalizeFinalFst(GraphFst): - """ - - """ - - def __init__(self, deterministic: bool = True, cache_dir: str = None, overwrite_cache: bool = False): - super().__init__(name="verbalize_final", kind="verbalize", deterministic=deterministic) - far_file = None - if cache_dir is not None and cache_dir != "None": - os.makedirs(cache_dir, exist_ok=True) - far_file = os.path.join(cache_dir, f"zh_tn_{deterministic}_deterministic_verbalizer.far") - if not overwrite_cache and far_file and os.path.exists(far_file): - self.fst = pynini.Far(far_file, mode="r")["verbalize"] - else: - token_graph = VerbalizeFst(deterministic=deterministic) - token_verbalizer = ( - pynutil.delete("tokens {") + delete_space + token_graph.fst + delete_space + pynutil.delete(" }") - ) - verbalizer = pynini.closure(delete_space + token_verbalizer + delete_space) - - postprocessor = PostProcessor(remove_puncts=False, to_upper=False, to_lower=False, tag_oov=False,) - - self.fst = (verbalizer @ postprocessor.fst).optimize() - if far_file: - generator_main(far_file, {"verbalize": self.fst}) diff --git a/nemo_text_processing/text_normalization/zh/verbalizers/whitelist.py b/nemo_text_processing/text_normalization/zh/verbalizers/whitelist.py deleted file mode 100644 index e9780744f07a..000000000000 --- a/nemo_text_processing/text_normalization/zh/verbalizers/whitelist.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pynini -from nemo_text_processing.text_normalization.zh.graph_utils import NEMO_NOT_QUOTE, GraphFst -from pynini.lib import pynutil - - -class Whitelist(GraphFst): - ''' - tokens { whitelist: "ATM" } -> A T M - ''' - - def __init__(self, deterministic: bool = True, lm: bool = False): - super().__init__(name="whitelist", kind="verbalize", deterministic=deterministic) - remove_erhua = pynutil.delete("erhua: \"") + pynutil.delete("儿") + pynutil.delete("\"") - whitelist = pynutil.delete("name: \"") + pynini.closure(NEMO_NOT_QUOTE) + pynutil.delete("\"") - graph = remove_erhua | whitelist - self.fst = graph.optimize() diff --git a/reinstall.sh b/reinstall.sh index b4c532037c3b..f89d86141b62 100755 --- a/reinstall.sh +++ b/reinstall.sh @@ -37,7 +37,4 @@ else ${PIP} install "${DIST_FILE}[all]" fi -echo 'Installing additional pynini dependency' -bash nemo_text_processing/install_pynini.sh > /dev/null 2>&1 && echo "nemo_text_processing installed!" || echo "nemo_text_processing could not be installed!" - echo 'All done!' diff --git a/requirements/requirements_nemo_text_processing.txt b/requirements/requirements_nemo_text_processing.txt index 9b7085f3ede7..989ce0ac03b0 100644 --- a/requirements/requirements_nemo_text_processing.txt +++ b/requirements/requirements_nemo_text_processing.txt @@ -1,2 +1 @@ -inflect -regex +nemo_text_processing==0.1.6rc0 diff --git a/scripts/dataset_processing/g2p/export_wikihomograph_data_to_manifest.py b/scripts/dataset_processing/g2p/export_wikihomograph_data_to_manifest.py index 0819dc8384ce..28f9cdd4e499 100644 --- a/scripts/dataset_processing/g2p/export_wikihomograph_data_to_manifest.py +++ b/scripts/dataset_processing/g2p/export_wikihomograph_data_to_manifest.py @@ -125,8 +125,8 @@ def correct_wikihomograph_data(sentence: str, start: int = None, end: int = None if sentence in corrections: start, end = corrections[sentence] - sentence = sentence.replace("2014Coordinate", "2014 Coordinate") # for normalized data for G2P OOV models - sentence = sentence.replace("AAA", "triple A") # for normalized data for G2P OOV models + sentence = sentence.replace("2014Coordinate", "2014 Coordinate") # for normalized data for G2P_paper OOV models + sentence = sentence.replace("AAA", "triple A") # for normalized data for G2P_paper OOV models return sentence, start, end diff --git a/tests/collections/common/tokenizers/text_to_speech/test_tts_tokenizers.py b/tests/collections/common/tokenizers/text_to_speech/test_tts_tokenizers.py index f81726d44b06..54fc0f061ca8 100644 --- a/tests/collections/common/tokenizers/text_to_speech/test_tts_tokenizers.py +++ b/tests/collections/common/tokenizers/text_to_speech/test_tts_tokenizers.py @@ -13,7 +13,7 @@ # limitations under the License. import pytest -from nemo_text_processing.g2p.modules import IPAG2P +from nemo.collections.tts.g2p.modules import IPAG2P from nemo.collections.common.tokenizers.text_to_speech.tts_tokenizers import ( EnglishCharsTokenizer, diff --git a/tests/nemo_text_processing/g2p/data/test_data_utils.py b/tests/collections/tts/g2p/data/test_data_utils.py similarity index 93% rename from tests/nemo_text_processing/g2p/data/test_data_utils.py rename to tests/collections/tts/g2p/data/test_data_utils.py index c04a4d0f0e13..49faa857526d 100644 --- a/tests/nemo_text_processing/g2p/data/test_data_utils.py +++ b/tests/collections/tts/g2p/data/test_data_utils.py @@ -13,10 +13,10 @@ # limitations under the License. import pytest -from nemo_text_processing.g2p.data.data_utils import ( +from nemo.collections.tts.g2p.data.data_utils import ( any_locale_word_tokenize, english_word_tokenize, - get_homograph_spans, + get_heteronym_spans, ) @@ -124,8 +124,8 @@ def test_any_locale_word_tokenize_with_numbers(self): @pytest.mark.run_only_on('CPU') @pytest.mark.unit - def test_get_homograph_spans(self): - supported_homographs = ["live", "read", "protest", "diffuse", "desert"] + def test_get_heteronym_spans(self): + supported_heteronyms = ["live", "read", "protest", "diffuse", "desert"] sentences = [ "I live in California. I READ a book. Only people who have already gained something are willing to protest. He reads a book!", "Yesterday, I read a book.", @@ -139,13 +139,13 @@ def test_get_homograph_spans(self): [(3, 7), (34, 41), (46, 50), (64, 70)], [(25, 31), (35, 41)], ] - expected_homographs = [ + expected_heteronyms = [ ["live", "read", "protest"], ['read'], ['read', 'diffuse', 'live', 'desert'], ['desert', 'desert'], ] - out_start_end, out_homographs = get_homograph_spans(sentences, supported_homographs) + out_start_end, out_heteronyms = get_heteronym_spans(sentences, supported_heteronyms) assert out_start_end == expected_start_end, "start-end spans do not match" - assert out_homographs == expected_homographs, "homograph spans do not match" + assert out_heteronyms == expected_heteronyms, "heteronym spans do not match" diff --git a/tests/nemo_text_processing/g2p/phoneme_dict/test_dict_de.txt b/tests/collections/tts/g2p/phoneme_dict/test_dict_de.txt similarity index 100% rename from tests/nemo_text_processing/g2p/phoneme_dict/test_dict_de.txt rename to tests/collections/tts/g2p/phoneme_dict/test_dict_de.txt diff --git a/tests/nemo_text_processing/g2p/phoneme_dict/test_dict_en.txt b/tests/collections/tts/g2p/phoneme_dict/test_dict_en.txt similarity index 100% rename from tests/nemo_text_processing/g2p/phoneme_dict/test_dict_en.txt rename to tests/collections/tts/g2p/phoneme_dict/test_dict_en.txt diff --git a/tests/nemo_text_processing/g2p/phoneme_dict/test_dict_es.txt b/tests/collections/tts/g2p/phoneme_dict/test_dict_es.txt similarity index 100% rename from tests/nemo_text_processing/g2p/phoneme_dict/test_dict_es.txt rename to tests/collections/tts/g2p/phoneme_dict/test_dict_es.txt diff --git a/tests/nemo_text_processing/g2p/test_modules.py b/tests/collections/tts/g2p/test_modules.py similarity index 99% rename from tests/nemo_text_processing/g2p/test_modules.py rename to tests/collections/tts/g2p/test_modules.py index 130eb79eb36f..196239c65f7f 100644 --- a/tests/nemo_text_processing/g2p/test_modules.py +++ b/tests/collections/tts/g2p/test_modules.py @@ -16,8 +16,8 @@ import unicodedata import pytest -from nemo_text_processing.g2p.data.data_utils import GRAPHEME_CASE_LOWER, GRAPHEME_CASE_MIXED, GRAPHEME_CASE_UPPER -from nemo_text_processing.g2p.modules import IPAG2P +from nemo.collections.tts.g2p.data.data_utils import GRAPHEME_CASE_LOWER, GRAPHEME_CASE_MIXED, GRAPHEME_CASE_UPPER +from nemo.collections.tts.g2p.modules import IPAG2P class TestIPAG2P: diff --git a/tests/collections/tts/test_torch_tts.py b/tests/collections/tts/test_torch_tts.py index f99ebda8880d..4a1c2a4e0990 100644 --- a/tests/collections/tts/test_torch_tts.py +++ b/tests/collections/tts/test_torch_tts.py @@ -18,7 +18,7 @@ import pytest import torch -from nemo_text_processing.g2p.modules import EnglishG2p +from nemo.collections.tts.g2p.modules import EnglishG2p from nemo.collections.common.tokenizers.text_to_speech.tts_tokenizers import EnglishPhonemesTokenizer from nemo.collections.tts.torch.data import TTSDataset diff --git a/tests/conftest.py b/tests/conftest.py index 206dad62f939..5987416e22e3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -55,15 +55,6 @@ def pytest_addoption(parser): help="numba compatibility checks will be relaxed to just availability of cuda, " "without cuda compatibility matrix check", ) - parser.addoption( - '--tn_cache_dir', - type=str, - default=None, - help="path to a directory with .far grammars for CPU TN/ITN tests, (DEFAULT: None, i.e. no cache)", - ) - parser.addoption( - '--run_audio_based', action='store_true', help="pass this argument to run audio-based TN tests", - ) @pytest.fixture @@ -231,9 +222,3 @@ def pytest_configure(config): print("Setting numba compat :", config.option.relax_numba_compat) numba_utils.set_numba_compat_strictness(strict=config.option.relax_numba_compat) - - # Set cache directory for TN/ITN tests - from .nemo_text_processing.utils import set_audio_based_tests, set_cache_dir - - set_cache_dir(config.option.tn_cache_dir) - set_audio_based_tests(config.option.run_audio_based) diff --git a/tests/nemo_text_processing/__init__.py b/tests/nemo_text_processing/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/tests/nemo_text_processing/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/tests/nemo_text_processing/de/__init__.py b/tests/nemo_text_processing/de/__init__.py deleted file mode 100644 index 9e3250071955..000000000000 --- a/tests/nemo_text_processing/de/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_cardinal.txt b/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_cardinal.txt deleted file mode 100644 index 0b2064296162..000000000000 --- a/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_cardinal.txt +++ /dev/null @@ -1,62 +0,0 @@ -ein hundert~100 -einhundert~100 -ein hundert und zwei~102 -einhundertzwei~102 -ein hundert und zwanzig~120 -ein hundert und elf~111 -ein tausend~1000 -eintausend~1000 -ein hundert zwanzig~120 -ein tausend zwanzig~1020 -eintausendzwanzig~1020 -neun billionen sieben hundert neun und achtzig milliarden drei hundert zwei und achtzig millionen fünf hundert sechs und dreißig tausend ein hundert dreißig~9789382536130 -zwei hundert vier und fünfzig~254 -ein hundert sieben und vierzig tausend vier hundert ein und fünfzig~147451 -eine million ein hundert sechs und fünfzig tausend ein hundert drei und siebzig~1156173 -eine milliarde fünf hundert drei und neunzig millionen zwei und siebzig tausend neun hundert ein und sechzig~1593072961 -sieben und neunzig billiarden acht hundert acht billionen zwei hundert vier und sechzig milliarden sieben hundert zwei und siebzig millionen sieben hundert zwei und neunzig tausend fünf~97808264772792005 -zehn billiarden zehn billionen zehn millionen ein hundert tausend zehn~10010000010100010 -zehn billiarden zehn billionen zehn millionen einhunderttausendzehn~10010000010100010 -minus fünf und zwanzig tausend sieben und dreißig~-25037 -minus fünf und zwanzig tausend sieben und dreißig~-25037 -minus fünfundzwanzigtausendsiebenunddreißig~-25037 -eine billiarde zwei hundert vier und sechzig billionen drei hundert eins milliarden neun hundert acht und dreißig millionen ein hundert vier~1264301938000104 -eine billiarde zweihundertvierundsechzig billionen dreihunderteins milliarden neunhundertachtunddreißig millionen einhundertvier~1264301938000104 -minus sechzig~-60 -sechs und vierzig tausend sechs hundert vier und sechzig~46664 -sechzig~60 -null~null -eins~eins -ein~ein -eine~eine -einer~einer -zwei~zwei -neun~neun -zehn~10 -elf~11 -zwölf~12 -dreizehn~13 -vierzehn~14 -fünfzehn~15 -sechzehn~16 -siebzehn~17 -achtzehn~18 -zwanzig~20 -dreißig~30 -vierzig~40 -fünfzig~50 -sechzig~60 -siebzig~70 -achtzig~80 -neunzig~90 -zwei millionen drei~2000003 -ein tausend dreizehn~1013 -ein tausend eins~1001 -ein tausend ein hundert~1100 -ein tausend sechs und zwanzig~1026 -ein tausend ein hundert sechs und zwanzig~1126 -achtzehn millionen vier hundert fünfzig tausend neun hundert neunzig~18450990 -achtzehn millionen neun hundert vierzig tausend sieben hundert zwei und zwanzig~18940722 -achtzehn millionen sechs hundert neunzig tausend neun hundert sechzehn~18690916 -achtzehn millionen sechshundertneunzigtausendneunhundertsechzehn~18690916 -achtzehn tausend acht hundert achtzig~18880 diff --git a/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_date.txt b/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_date.txt deleted file mode 100644 index e994b936d850..000000000000 --- a/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_date.txt +++ /dev/null @@ -1,22 +0,0 @@ -vierundzwanzigster juli zwei tausend dreizehn~24. Jul. 2013 -vier und zwanzigster juli zwei tausend dreizehn~24. Jul. 2013 -neunzehn achtzig~1980 -neunzehnachtzig~1980 -neunzehnhundertachtzig~1980 -neunzehn hundert achtzig~1980 -neunzehn achtziger~19 achtziger -zwei tausend zwanzig~2020 -zwanzig zwanzig~2020 -zwei tausend neun~2009 -vierzehnter januar~14. Jan. -januarzweitausendneun~januarzweitausendneun -januar zweitausendneun~Jan. 2009 -erster januar~1. Jan. -dreißigster juni~30. Jun. -neunzehn siebzehn~1917 -neunzehn hundert siebzehn~1917 -neunzehn hundert vierundneunzig~1994 -neunzehn hundert vier und neunzig~1994 -neunzehn vierundneunzig~1994 -zwei tausend drei~2003 -ein tausend acht~1008 diff --git a/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_decimal.txt b/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_decimal.txt deleted file mode 100644 index 9db4974a3007..000000000000 --- a/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_decimal.txt +++ /dev/null @@ -1,10 +0,0 @@ -null komma zwei millionen~0,2 millionen -eine million~1 million -eins komma zwei millionen~1,2 millionen -achtzehn milliarden~18 milliarden -vier hundert sechzig millionen~460 millionen -ein hundert zwanzig millionen~120 millionen -zehn millionen~10 millionen -minus sechzig komma zwei vier null null~-60,2400 -acht hundert achtzehn komma drei null drei~818,303 -achthundertachtzehn komma drei null drei~818,303 \ No newline at end of file diff --git a/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_electronic.txt b/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_electronic.txt deleted file mode 100644 index fc9b0fc6666b..000000000000 --- a/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_electronic.txt +++ /dev/null @@ -1,9 +0,0 @@ -c d f at a b c punkt e d u~cdf@abc.edu -a b c at g mail punkt a b c~abc@gmail.abc -a b c at a b c punkt com~abc@abc.com -a s d f eins zwei drei at a b c punkt com~asdf123@abc.com -a eins b zwei at a b c punkt com~a1b2@abc.com -a b drei bindestrich s d d bindestrich drei at g mail punkt com~ab3-sdd-3@gmail.com -h t t p s doppelpunkt slash slash w w w punkt a b c punkt com~https://www.abc.com -w w w punkt a b c punkt com~www.abc.com -h t t p s doppelpunkt slash slash w w w punkt a b c punkt com slash a b fragezeichen gleichheitszeichen drei bindestrich slash a b s slash eins~https://www.abc.com/ab?=3-/abs/1 \ No newline at end of file diff --git a/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_fraction.txt b/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_fraction.txt deleted file mode 100644 index 5802cd481248..000000000000 --- a/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_fraction.txt +++ /dev/null @@ -1,34 +0,0 @@ -null nulltel~0/0 -ein halb~1/2 -vier halbe~4/2 -ein drittel~1/3 -ein viertel~1/4 -ein fünftel~1/5 -ein sechstel~1/6 -ein siebtel~1/7 -ein achtel~1/8 -zwei neuntel~2/9 -ein ein halb~1 1/2 -ein sechstel~1/6 -ein zehntel~1/10 -ein elftel~1/11 -ein zehntel~1/10 -ein zwölftel~1/12 -ein dreizehntel~1/13 -ein vierzehntel~1/14 -ein fünfzehntel~1/15 -ein sechzehntel~1/16 -ein siebzehntel~1/17 -ein achtzehntel~1/18 -ein neunzehntel~1/19 -ein zwanzigstel~1/20 -ein dreißigstel~1/30 -ein vierzigstel~1/40 -ein fünfzigstel~1/50 -ein sechzigstel~1/60 -ein siebzigstel~1/70 -ein achtzigstel~1/80 -ein neunzigstel~1/90 -ein ein hundertstel~1/100 -ein zwei und zwanzigstel~1/22 -minus ein zwei und zwanzigstel~-1/22 \ No newline at end of file diff --git a/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_measure.txt b/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_measure.txt deleted file mode 100644 index f454760917e4..000000000000 --- a/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_measure.txt +++ /dev/null @@ -1,28 +0,0 @@ -zwei hundert meter~200 m -sechs und fünfzig komma drei pro quadrat kilometer~56,3 /km² -zwei hundert kilometer pro stunde~200 km/h -zwei und vierzig tausend zwei hundert neun und fünfzig pro quadrat meter~42259 /m² -minus sechs und sechzig kilogramm~-66 kg -minus sechsundsechzig kilogramm~-66 kg -zwei kilowattstunden~2 kwh -eins komma null null null null zwei acht kubik zentimeter~1,000028 cm³ -eins komma eins zentimeter~1,1 cm -drei stunden~3 h -eine stunde~1 h -ein millivolt~1 mv -eine million millivolt~1 million mv -zwei kubik meter~2 m³ -neunzig gramm~90 g -neunzig millionen gramm~90 millionen g -neunzig komma vier millionen gramm~90,4 millionen g -vier hundert vierzig milliliter~440 ml -drei hundert mikrometer~300 μm -fünf und sechzig tausend quadrat kilometer~65000 km² -zwei kilometer pro stunde~2 km/h -zwei millionen kilometer pro stunde~2 millionen km/h -zwei komma zwei millionen kilometer pro stunde~2,2 millionen km/h -sechzig komma zwei vier null null kilogramm~60,2400 kg -null fuß~0 ft -ein halb fuß~1/2 ft -ein ein halb fuß~1 1/2 ft -minus ein ein halb fuß~-1 1/2 ft \ No newline at end of file diff --git a/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_money.txt b/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_money.txt deleted file mode 100644 index 6e236d81792a..000000000000 --- a/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_money.txt +++ /dev/null @@ -1,23 +0,0 @@ -zwei dollar~$2 -ein dollar~$1 -eine million dollar~$1 million -zwei komma null null null eins dollar~$2,0001 -zwei komma null eins dollar~$2,01 -zwei komma null null dollar~$2,00 -ein cent~€0,01 -zwei cent~€0,02 -zwanzig cent~€0,20 -zweiundzwanzig cent~€0,22 -einhundert cent~100 cent -zwei dollar zwanzig~$2,20 -zweidollarzwanzig~zweidollarzwanzig -zwei dollar und zwanzig cent~$2,20 -zwei euro und zwanzig cent~€2,20 -zwei pfund und zwanzig pence~£2,20 -zwei euro zwanzig cent~€2,20 -zwei millionen euro~€2 millionen -zwei komma zwei null null millionen euro~€2,200 millionen -zwei komma zwei null eins millionen euro~€2,201 millionen -zwei komma zwei eins millionen euro~€2,21 millionen -zwei pfund und ein penny~£2,01 -zwei pfund und ein hundert penny~£2 und 100 penny \ No newline at end of file diff --git a/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_ordinal.txt b/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_ordinal.txt deleted file mode 100644 index c85bb289de15..000000000000 --- a/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_ordinal.txt +++ /dev/null @@ -1,20 +0,0 @@ -ein hundertste~100. -fünf und zwanzig tausend ein hundert elftem~25111. -fünfundzwanzigtausendeinhundertelftem~25111. -zweite~zweite -nullte~nullte -erster~erster -zweiter~zweiter -dritter~dritter -vierter~vierter -zehnter~10. -elftem~11. -dreizehntem~13. -ein und zwanzigstes~21. -drei und zwanzigstes~23. -dreiundzwanzigstes~23. -ein hundert elftes~111. -ein tausendstem~1000. -dem ein tausendstem~dem 1000. -ein hundert ein und zwanzigste~121. -einhunderteinundzwanzigste~121. diff --git a/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_telephone.txt b/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_telephone.txt deleted file mode 100644 index 204d3e487d39..000000000000 --- a/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_telephone.txt +++ /dev/null @@ -1 +0,0 @@ -null vier eins eins eins zwei drei vier eins zwei drei vier~(0411) 1234-1234 \ No newline at end of file diff --git a/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_time.txt b/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_time.txt deleted file mode 100644 index 0eb0bdbfee8b..000000000000 --- a/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_time.txt +++ /dev/null @@ -1,24 +0,0 @@ -acht uhr~8 Uhr -vier und zwanzig uhr~24 Uhr -vierundzwanziguhr~24 Uhr -vierundzwanzig uhr~24 Uhr -vierundzwanziguhrzweiundzwanzigest~24:22 Uhr est -vierundzwanziguhrzweiundzwanzig e s t~24:22 Uhr est -zwölf uhr mittags~12 Uhr mittags -achtzehn uhr~18 Uhr -acht uhr sieben~08:07 Uhr -null uhr siebzehn~00:17 Uhr -halb zwölf~11:30 Uhr -viertel vor zwölf~11:45 Uhr -drei vor zwölf~11:57 Uhr -zwei und zwanzig vor zwölf~11:38 Uhr -zweiundzwanzig vor zwölf~11:38 Uhr -drei nach zwölf~12:03 Uhr -viertel nach zwölf~12:15 Uhr -zehn nach zwölf~12:10 Uhr -zehn vor zwölf~11:50 Uhr -viertel nach zwölf nachts~12:15 Uhr nachts -null uhr null minuten null sekunden~00:00:00 Uhr -ein uhr eine minute eine sekunde e s t~01:01:01 Uhr est -zwei uhr zwei minuten drei und zwanzig sekunden~02:02:23 Uhr -zwei uhr zwei minuten dreiundzwanzig sekunden~02:02:23 Uhr \ No newline at end of file diff --git a/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_whitelist.txt b/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_whitelist.txt deleted file mode 100644 index 4f3767f623c5..000000000000 --- a/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_whitelist.txt +++ /dev/null @@ -1,6 +0,0 @@ -doktor dao~Dr. dao -miss smith~Ms. smith -misses smith~Mrs. smith -mister dao~Mr. dao -ich mag essen zum beispiel eis~ich mag essen z.B. eis -Chanel nummer fünf~Chanel Nr. fünf diff --git a/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_word.txt b/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_word.txt deleted file mode 100644 index 98ddaf3df23a..000000000000 --- a/tests/nemo_text_processing/de/data_inverse_text_normalization/test_cases_word.txt +++ /dev/null @@ -1,49 +0,0 @@ -~ -yahoo!~yahoo! -zwanzig!~20 ! -x ~x -—~— -aaa~aaa -aabach~aabach -aabenraa~aabenraa -aabye~aabye -aaccessed~aaccessed -aach~aach -aachen's~aachen's -aadri~aadri -aafia~aafia -aagaard~aagaard -aagadu~aagadu -aagard~aagard -aagathadi~aagathadi -aaghart's~aaghart's -aagnes~aagnes -aagomoni~aagomoni -aagon~aagon -aagoo~aagoo -aagot~aagot -aahar~aahar -aahh~aahh -aahperd~aahperd -aaibinterstate~aaibinterstate -aajab~aajab -aakasa~aakasa -aakervik~aakervik -aakirkeby~aakirkeby -aalam~aalam -aalbaek~aalbaek -aaldiu~aaldiu -aalem~aalem -a'ali~a'ali -aalilaassamthey~aalilaassamthey -aalin~aalin -aaliyan~aaliyan -aaliyan's~aaliyan's -aamadu~aamadu -aamara~aamara -aambala~aambala -aamera~aamera -aamer's~aamer's -aamina~aamina -aaminah~aaminah -aamjiwnaang~aamjiwnaang diff --git a/tests/nemo_text_processing/de/data_text_normalization/test_cases_cardinal.txt b/tests/nemo_text_processing/de/data_text_normalization/test_cases_cardinal.txt deleted file mode 100644 index a2d211f4c86d..000000000000 --- a/tests/nemo_text_processing/de/data_text_normalization/test_cases_cardinal.txt +++ /dev/null @@ -1,50 +0,0 @@ -null~0 -eins~1 -zwei~2 -drei~3 -vier~4 -sechs~6 -zehn~10 -elf~11 -zwölf~12 -dreizehn~13 -vierzehn~14 -fünfzehn~15 -sechzehn~16 -siebzehn~17 -achtzehn~18 -zwanzig~20 -dreißig~30 -vierzig~40 -fünfzig~50 -sechzig~60 -siebzig~70 -achtzig~80 -neunzig~90 -drei und zwanzig~23 -ein hundert~100 -ein hundert drei und zwanzig~123 -ein hundert eins~101 -ein tausend~1000 -ein hundert tausend~100000 -eine million~1000000 -zehn millionen~10000000 -ein hundert millionen~100000000 -neun billionen sieben hundert neun und achtzig milliarden drei hundert zwei und achtzig millionen fünf hundert sechs und dreißig tausend ein hundert dreißig~9789382536130 -ein hundert sieben und vierzig tausend vier hundert ein und fünfzig~147451 -eine million ein hundert sechs und fünfzig tausend ein hundert drei und siebzig~1156173 -eine milliarde fünf hundert drei und neunzig millionen zwei und siebzig tausend neun hundert ein und sechzig~1593072961 -sieben und neunzig billiarden acht hundert acht billionen zwei hundert vier und sechzig milliarden sieben hundert zwei und siebzig millionen sieben hundert zwei und neunzig tausend fünf~97808264772792005 -zehn billiarden zehn billionen zehn millionen ein hundert tausend zehn~10010000010100010 -minus fünf und zwanzig tausend sieben und dreißig~-25037 -minus fünf und zwanzig tausend sieben und dreißig~-25037 -eine billiarde zwei hundert vier und sechzig billionen drei hundert eins milliarden~1264301000000000 -minus sechzig~-60 -sechs und vierzig tausend sechs hundert vier und sechzig~46664 -zwei millionen drei~2000003 -ein tausend sechs und zwanzig~1026 -ein tausend ein hundert sechs und zwanzig~1126 -achtzehn millionen vier hundert fünfzig tausend neun hundert neunzig~18450990 -achtzehn millionen neun hundert vierzig tausend sieben hundert zwei und zwanzig~18940722 -achtzehn millionen sechs hundert neunzig tausend neun hundert sechzehn~18690916 -achtzehn tausend acht hundert achtzig~18880 diff --git a/tests/nemo_text_processing/de/data_text_normalization/test_cases_date.txt b/tests/nemo_text_processing/de/data_text_normalization/test_cases_date.txt deleted file mode 100644 index 7de5cb7d782c..000000000000 --- a/tests/nemo_text_processing/de/data_text_normalization/test_cases_date.txt +++ /dev/null @@ -1,16 +0,0 @@ -vier und zwanzigster juli zwei tausend dreizehn~24. juli 2013 -vierzehnter januar~14. januar -erster januar~1. januar -dreißigster juni~30. juni -zweiter märz zwei tausend drei~02.03.2003 -zweiter märz zwei tausend drei~02.03.2003 -zweiter märz zwei tausend drei~2.3.2003 -zweiter märz~2.3 -zweiter märz~02.03 -zweiter märz zwei tausend drei~2. März 2003 -zweiter märz zwei tausend drei~2. Mär. 2003 -zweiter märz zwei tausend drei~2. mär. 2003 -zweiter märz zwei tausend drei~2. März 2003 -zweiter märz zwei tausend drei~2003-03-02 -märz zwei tausend drei~2003-03 -zweiter märz zwei tausend drei~2003-3-2 \ No newline at end of file diff --git a/tests/nemo_text_processing/de/data_text_normalization/test_cases_decimal.txt b/tests/nemo_text_processing/de/data_text_normalization/test_cases_decimal.txt deleted file mode 100644 index 551114d8ee85..000000000000 --- a/tests/nemo_text_processing/de/data_text_normalization/test_cases_decimal.txt +++ /dev/null @@ -1,7 +0,0 @@ -null komma zwei millionen~0,2 millionen -achtzehn milliarden~18 milliarden -vier hundert sechzig millionen~460 millionen -ein hundert zwanzig millionen~120 millionen -zehn millionen~10 millionen -minus sechzig komma zwei vier null null~-60,2400 -acht hundert achtzehn komma drei null drei~818,303 \ No newline at end of file diff --git a/tests/nemo_text_processing/de/data_text_normalization/test_cases_electronic.txt b/tests/nemo_text_processing/de/data_text_normalization/test_cases_electronic.txt deleted file mode 100644 index fc9b0fc6666b..000000000000 --- a/tests/nemo_text_processing/de/data_text_normalization/test_cases_electronic.txt +++ /dev/null @@ -1,9 +0,0 @@ -c d f at a b c punkt e d u~cdf@abc.edu -a b c at g mail punkt a b c~abc@gmail.abc -a b c at a b c punkt com~abc@abc.com -a s d f eins zwei drei at a b c punkt com~asdf123@abc.com -a eins b zwei at a b c punkt com~a1b2@abc.com -a b drei bindestrich s d d bindestrich drei at g mail punkt com~ab3-sdd-3@gmail.com -h t t p s doppelpunkt slash slash w w w punkt a b c punkt com~https://www.abc.com -w w w punkt a b c punkt com~www.abc.com -h t t p s doppelpunkt slash slash w w w punkt a b c punkt com slash a b fragezeichen gleichheitszeichen drei bindestrich slash a b s slash eins~https://www.abc.com/ab?=3-/abs/1 \ No newline at end of file diff --git a/tests/nemo_text_processing/de/data_text_normalization/test_cases_fraction.txt b/tests/nemo_text_processing/de/data_text_normalization/test_cases_fraction.txt deleted file mode 100644 index 5802cd481248..000000000000 --- a/tests/nemo_text_processing/de/data_text_normalization/test_cases_fraction.txt +++ /dev/null @@ -1,34 +0,0 @@ -null nulltel~0/0 -ein halb~1/2 -vier halbe~4/2 -ein drittel~1/3 -ein viertel~1/4 -ein fünftel~1/5 -ein sechstel~1/6 -ein siebtel~1/7 -ein achtel~1/8 -zwei neuntel~2/9 -ein ein halb~1 1/2 -ein sechstel~1/6 -ein zehntel~1/10 -ein elftel~1/11 -ein zehntel~1/10 -ein zwölftel~1/12 -ein dreizehntel~1/13 -ein vierzehntel~1/14 -ein fünfzehntel~1/15 -ein sechzehntel~1/16 -ein siebzehntel~1/17 -ein achtzehntel~1/18 -ein neunzehntel~1/19 -ein zwanzigstel~1/20 -ein dreißigstel~1/30 -ein vierzigstel~1/40 -ein fünfzigstel~1/50 -ein sechzigstel~1/60 -ein siebzigstel~1/70 -ein achtzigstel~1/80 -ein neunzigstel~1/90 -ein ein hundertstel~1/100 -ein zwei und zwanzigstel~1/22 -minus ein zwei und zwanzigstel~-1/22 \ No newline at end of file diff --git a/tests/nemo_text_processing/de/data_text_normalization/test_cases_measure.txt b/tests/nemo_text_processing/de/data_text_normalization/test_cases_measure.txt deleted file mode 100644 index faa5ca074789..000000000000 --- a/tests/nemo_text_processing/de/data_text_normalization/test_cases_measure.txt +++ /dev/null @@ -1,29 +0,0 @@ -zwei hundert meter~200 m -sechs und fünfzig komma drei pro quadrat kilometer~56,3 /km² -zwei hundert kilometer pro stunde~200 km/h -zwei und vierzig tausend zwei hundert neun und fünfzig pro quadrat meter~42259 /m² -zwei und vierzig tausend zwei hundert neun und fünfzig pro quadrat meter~42259/m² -minus sechs und sechzig kilogramm~-66 kg -zwei kilowattstunden~2 kwh -eins komma null null null null zwei acht kubik zentimeter~1,000028 cm³ -eins komma eins zentimeter~1,1 cm -drei stunden~3 h -ein stunde~1 h -ein millivolt~1 mv -eins million millivolt~1 million mv -zwei kubik meter~2 m³ -neunzig gramm~90 g -neunzig millionen gramm~90 millionen g -neunzig komma vier millionen gramm~90,4 millionen g -vier hundert vierzig milliliter~440 ml -drei hundert mikrometer~300 μm -fünf und sechzig tausend quadrat kilometer~65000 km² -zwei kilometer pro stunde~2 km/h -zwei kilometer pro stunde~2km/h -zwei millionen kilometer pro stunde~2 millionen km/h -zwei komma zwei millionen kilometer pro stunde~2,2 millionen km/h -sechzig komma zwei vier null null kilogramm~60,2400 kg -null fuß~0 ft -ein halb fuß~1/2 ft -ein ein halb fuß~1 1/2 ft -minus ein ein halb fuß~-1 1/2 ft \ No newline at end of file diff --git a/tests/nemo_text_processing/de/data_text_normalization/test_cases_money.txt b/tests/nemo_text_processing/de/data_text_normalization/test_cases_money.txt deleted file mode 100644 index f487ff8d31bd..000000000000 --- a/tests/nemo_text_processing/de/data_text_normalization/test_cases_money.txt +++ /dev/null @@ -1,21 +0,0 @@ -zwei dollar~$2 -zwei dollar~$2,000 -zwei dollar~$2,0 -zwei dollar~$2,00 -zwei komma null null null eins dollar~$2,0001 -eins million euro~€1 million -ein cent~€0,01 -zwei cent~€0,02 -zwanzig cent~€0,20 -zwei dollar zwanzig cent~$2,20 -zwei dollar zwanzig cent~$2,20000 -zwei dollar zwanzig cent~$2,200 -zwei komma zwei null eins dollar~$2,2010 -zwei komma zwei null eins dollar~$2,201 -zwei euro zwanzig cent~€2,20 -zwei euro zwanzig cent~€2,200 -zwei komma zwei null null millionen euro~€2,200 millionen -zwei komma zwei null eins millionen euro~€2,201 millionen -zwei komma zwei eins millionen euro~€2,21 millionen -zwei pfund ein penny~£2,01 -zwei pfund zwanzig pence~£2,20 \ No newline at end of file diff --git a/tests/nemo_text_processing/de/data_text_normalization/test_cases_normalize_with_audio.txt b/tests/nemo_text_processing/de/data_text_normalization/test_cases_normalize_with_audio.txt deleted file mode 100644 index 08c54738837a..000000000000 --- a/tests/nemo_text_processing/de/data_text_normalization/test_cases_normalize_with_audio.txt +++ /dev/null @@ -1,131 +0,0 @@ -~123 -ein hundert drei und zwanzig -ein hundert und drei und zwanzig -123 -~101 -ein hundert eins -ein hundert eine -ein hundert ein -ein hundert und eins -ein hundert und ein -101 -ein hundert und eine -~1001 -zehn hundert eins -ein tausend eine -eine tausend eins -zehn eins -1001 -ein tausend ein -ein tausend eins -eine tausend eine -eine tausend ein -~1101 -ein tausend ein hundert und eins -ein tausend ein hundert eine -ein tausend ein hundert und ein -eine tausend ein hundert und ein -elf hundert eins -eine tausend ein hundert eine -elf eins -eine tausend ein hundert und eine -ein tausend ein hundert und eine -eine tausend ein hundert und eins -ein tausend ein hundert ein -1101 -eine tausend ein hundert eins -eine tausend ein hundert ein -ein tausend ein hundert eins -~1000000 -eine million -1000000 -ein million -ein millionen -eine millionen -eins million -~t-0t25d12-f -t-0t25d12-f -~133-A -133-A -ein hundert drei und dreißig A -ein hundert und drei und dreißig A -~B2A23C -B2A23C -~25d08A -25d08A -~C24 -C24 -~1970-2010 -1970-2010 -~W26s -W26s -~5-3-Ws -5-3-Ws -~401-ks -401-ks -~The box was 25 x 7 m. -The box was 25 x 7 m. -~4567 -4567 -~$2,20 -zwei us dollar zwanzig -zwei dollar und zwanzig cent -zwei us dollar und zwanzig cent -zwei us dollar zwanzig cent -zwei dollar zwanzig -zwei dollar zwanzig cent -~25.] -25 . ] -25. ] -fünf und zwanzigster ] -~Francis I--test -Francis I--test -~librivox.com -librivox.com -l i b r i v o x punkt com -l i b r i v o x punkt c o m -~1,000,123 -1,000,123 -~1980 -neunzehn achtzig -ein tausend neun hundert achtzig -neunzehn hundert achtzig -eine tausend neun hundert achtzig -ein tausend neun hundert und achtzig -eine tausend neun hundert und achtzig -~02.03.2003 -zweiter dritter zwanzig drei -zweiter märz zwei tausend drei -zweiter dritter zwei tausend drei -zweiter märz zwanzig drei -~abc@gmail.abc -a b c at g mail punkt a b c -a b c at g m a i l punkt a b c -~1 1/2 -1 ein halb -eins ein halb -ein ein halb -ein und ein halb -eins 1/2 -~11:30 Uhr -halb zwölf -elf uhr dreißig -dreißig nach elf -~11:45 Uhr -fünfzehn vor zwölf -elf uhr fünf und vierzig -viertel vor zwölf -~11:57 Uhr -elf uhr sieben und fünfzig -drei vor zwölf -~12:03 Uhr -drei nach zwölf -zwölf uhr drei -~12:15 Uhr -fünfzehn nach zwölf -zwölf uhr fünfzehn -viertel nach zwölf -~24:15 Uhr -viertel nach zwölf -fünfzehn nach zwölf -vier und zwanzig uhr fünfzehn diff --git a/tests/nemo_text_processing/de/data_text_normalization/test_cases_ordinal.txt b/tests/nemo_text_processing/de/data_text_normalization/test_cases_ordinal.txt deleted file mode 100644 index 89bbbf1ec71f..000000000000 --- a/tests/nemo_text_processing/de/data_text_normalization/test_cases_ordinal.txt +++ /dev/null @@ -1,19 +0,0 @@ -ein hundertster Geburtstag~100. Geburtstag -fünf und zwanzig tausend ein hundert elfter~25111. -zweiter~2. -nullter~0. -erster~1. -zweiter~2. -dritter~3. -vierter~4. -elfter~11. -dreizehnter~13. -ein und zwanzigster~21. -drei und zwanzigster~23. -ein hundert elfter~111. -ein tausendster~1000. -dem ein tausendstem~dem 1000. -ein hundert erster~101. -eine millionster~1000000. -ein hundert ein und zwanzigster~121. -ein hundert ein und zwanzigstem~121tem diff --git a/tests/nemo_text_processing/de/data_text_normalization/test_cases_telephone.txt b/tests/nemo_text_processing/de/data_text_normalization/test_cases_telephone.txt deleted file mode 100644 index e7bb731bba44..000000000000 --- a/tests/nemo_text_processing/de/data_text_normalization/test_cases_telephone.txt +++ /dev/null @@ -1,4 +0,0 @@ -null vier eins eins eins zwei drei vier eins zwei drei vier~(0411) 1234-1234 -null vier eins eins null zwei drei vier eins zwei drei vier~(0411) 0234-1234 -plus neun und vierzig eins zwei drei vier eins zwei drei vier~+49 1234-1234 -plus neun und vierzig eins zwei drei vier eins zwei drei vier~+49 12341234 \ No newline at end of file diff --git a/tests/nemo_text_processing/de/data_text_normalization/test_cases_time.txt b/tests/nemo_text_processing/de/data_text_normalization/test_cases_time.txt deleted file mode 100644 index 2b5216d2051e..000000000000 --- a/tests/nemo_text_processing/de/data_text_normalization/test_cases_time.txt +++ /dev/null @@ -1,26 +0,0 @@ -acht uhr~08:00 Uhr -acht uhr~08:00 Uhr -null uhr~00:00 Uhr -null uhr fünfzehn~00:15 Uhr -acht uhr~08:00 Uhr -zwanzig uhr~20:00 Uhr -vier und zwanzig uhr~24:00 Uhr -vierzehn uhr~14:00 Uhr -zwölf uhr~12:00 Uhr -elf uhr dreißig~11:30 Uhr -achtzehn uhr~18:00 Uhr -acht uhr sieben~08:07 Uhr -null uhr siebzehn~00:17 Uhr -zwölf uhr~12:00 Uhr -elf uhr fünf und vierzig~11:45 Uhr -elf uhr sieben und fünfzig~11:57 Uhr -zwölf uhr drei~12:03 Uhr -zwölf uhr zehn~12:10 Uhr -elf uhr fünfzig~11:50 Uhr -drei und zwanzig uhr dreißig~23:30 Uhr -drei und zwanzig uhr fünfzehn~23:15 Uhr -vier und zwanzig uhr fünf und vierzig~24:45 Uhr -vier und zwanzig uhr fünfzehn~24:15 Uhr -null uhr null minuten null sekunden~00:00:00 Uhr -ein uhr eine minute eine sekunde e s t~01:01:01 Uhr est -zwei uhr zwei minuten drei und zwanzig sekunden~02:02:23 Uhr \ No newline at end of file diff --git a/tests/nemo_text_processing/de/data_text_normalization/test_cases_whitelist.txt b/tests/nemo_text_processing/de/data_text_normalization/test_cases_whitelist.txt deleted file mode 100644 index b2df1dd46148..000000000000 --- a/tests/nemo_text_processing/de/data_text_normalization/test_cases_whitelist.txt +++ /dev/null @@ -1,6 +0,0 @@ -doktor dao~Dr. dao -miss smith~Ms. smith -misses smith~Mrs. smith -mister dao~Mr. dao -ich mag essen zum beispiel eis~ich mag essen z.B. eis -Chanel nummer fünf~Chanel Nr. 5 diff --git a/tests/nemo_text_processing/de/data_text_normalization/test_cases_word.txt b/tests/nemo_text_processing/de/data_text_normalization/test_cases_word.txt deleted file mode 100644 index 014a93282e19..000000000000 --- a/tests/nemo_text_processing/de/data_text_normalization/test_cases_word.txt +++ /dev/null @@ -1,49 +0,0 @@ -~ -yahoo!~yahoo! -zwanzig !~20! -x~ x -—~— -aaa~aaa -aabach~aabach -aabenraa~aabenraa -aabye~aabye -aaccessed~aaccessed -aach~aach -aachen's~aachen's -aadri~aadri -aafia~aafia -aagaard~aagaard -aagadu~aagadu -aagard~aagard -aagathadi~aagathadi -aaghart's~aaghart's -aagnes~aagnes -aagomoni~aagomoni -aagon~aagon -aagoo~aagoo -aagot~aagot -aahar~aahar -aahh~aahh -aahperd~aahperd -aaibinterstate~aaibinterstate -aajab~aajab -aakasa~aakasa -aakervik~aakervik -aakirkeby~aakirkeby -aalam~aalam -aalbaek~aalbaek -aaldiu~aaldiu -aalem~aalem -a'ali~a'ali -aalilaassamthey~aalilaassamthey -aalin~aalin -aaliyan~aaliyan -aaliyan's~aaliyan's -aamadu~aamadu -aamara~aamara -aambala~aambala -aamera~aamera -aamer's~aamer's -aamina~aamina -aaminah~aaminah -aamjiwnaang~aamjiwnaang diff --git a/tests/nemo_text_processing/de/test_cardinal.py b/tests/nemo_text_processing/de/test_cardinal.py deleted file mode 100644 index 6aac6435244d..000000000000 --- a/tests/nemo_text_processing/de/test_cardinal.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestCardinal: - - inverse_normalizer = ( - InverseNormalizer(lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('de/data_inverse_text_normalization/test_cases_cardinal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer = ( - Normalizer(input_case='cased', lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - - normalizer_with_audio = ( - NormalizerWithAudio(input_case='cased', lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) - if CACHE_DIR and PYNINI_AVAILABLE and RUN_AUDIO_BASED_TESTS - else None - ) - - @parameterized.expand(parse_test_case_file('de/data_text_normalization/test_cases_cardinal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, expected, test_input): - pred = self.normalizer.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio: - pred_non_deterministic = self.normalizer_with_audio.normalize( - test_input, n_tagged=1000, punct_post_process=False - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/de/test_date.py b/tests/nemo_text_processing/de/test_date.py deleted file mode 100644 index f01bd1a9be05..000000000000 --- a/tests/nemo_text_processing/de/test_date.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestDate: - inverse_normalizer = ( - InverseNormalizer(lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('de/data_inverse_text_normalization/test_cases_date.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer = ( - Normalizer(input_case='cased', lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - - normalizer_with_audio = ( - NormalizerWithAudio(input_case='cased', lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) - if CACHE_DIR and PYNINI_AVAILABLE and RUN_AUDIO_BASED_TESTS - else None - ) - - @parameterized.expand(parse_test_case_file('de/data_text_normalization/test_cases_date.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, expected, test_input): - pred = self.normalizer.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio: - pred_non_deterministic = self.normalizer_with_audio.normalize( - test_input, n_tagged=1000, punct_post_process=False - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/de/test_decimal.py b/tests/nemo_text_processing/de/test_decimal.py deleted file mode 100644 index 0ff1b783ac86..000000000000 --- a/tests/nemo_text_processing/de/test_decimal.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestDecimal: - inverse_normalizer = ( - InverseNormalizer(lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('de/data_inverse_text_normalization/test_cases_decimal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer = ( - Normalizer(input_case='cased', lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - - normalizer_with_audio = ( - NormalizerWithAudio(input_case='cased', lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) - if CACHE_DIR and PYNINI_AVAILABLE and RUN_AUDIO_BASED_TESTS - else None - ) - - @parameterized.expand(parse_test_case_file('de/data_text_normalization/test_cases_decimal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, expected, test_input): - pred = self.normalizer.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio: - pred_non_deterministic = self.normalizer_with_audio.normalize( - test_input, n_tagged=1000, punct_post_process=False - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/de/test_electronic.py b/tests/nemo_text_processing/de/test_electronic.py deleted file mode 100644 index bbd7c1bd82fc..000000000000 --- a/tests/nemo_text_processing/de/test_electronic.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestElectronic: - inverse_normalizer = ( - InverseNormalizer(lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('de/data_inverse_text_normalization/test_cases_electronic.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer = ( - Normalizer(input_case='cased', lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - - normalizer_with_audio = ( - NormalizerWithAudio(input_case='cased', lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) - if CACHE_DIR and PYNINI_AVAILABLE and RUN_AUDIO_BASED_TESTS - else None - ) - - @parameterized.expand(parse_test_case_file('de/data_text_normalization/test_cases_electronic.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, expected, test_input): - pred = self.normalizer.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio: - pred_non_deterministic = self.normalizer_with_audio.normalize( - test_input, n_tagged=1000, punct_post_process=False - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/de/test_fraction.py b/tests/nemo_text_processing/de/test_fraction.py deleted file mode 100644 index f2d886f723f2..000000000000 --- a/tests/nemo_text_processing/de/test_fraction.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestFraction: - inverse_normalizer = ( - InverseNormalizer(lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('de/data_inverse_text_normalization/test_cases_fraction.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer = ( - Normalizer(input_case='cased', lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - - normalizer_with_audio = ( - NormalizerWithAudio(input_case='cased', lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) - if CACHE_DIR and PYNINI_AVAILABLE and RUN_AUDIO_BASED_TESTS - else None - ) - - @parameterized.expand(parse_test_case_file('de/data_text_normalization/test_cases_fraction.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, expected, test_input): - pred = self.normalizer.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio: - pred_non_deterministic = self.normalizer_with_audio.normalize( - test_input, n_tagged=1000, punct_post_process=False - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/de/test_measure.py b/tests/nemo_text_processing/de/test_measure.py deleted file mode 100644 index a93deaa209b2..000000000000 --- a/tests/nemo_text_processing/de/test_measure.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestMeasure: - inverse_normalizer = ( - InverseNormalizer(lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('de/data_inverse_text_normalization/test_cases_measure.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer = ( - Normalizer(input_case='cased', lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - - normalizer_with_audio = ( - NormalizerWithAudio(input_case='cased', lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) - if CACHE_DIR and RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(parse_test_case_file('de/data_text_normalization/test_cases_measure.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, expected, test_input): - pred = self.normalizer.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio: - pred_non_deterministic = self.normalizer_with_audio.normalize( - test_input, n_tagged=1000, punct_post_process=False - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/de/test_money.py b/tests/nemo_text_processing/de/test_money.py deleted file mode 100644 index 9160ba10bda0..000000000000 --- a/tests/nemo_text_processing/de/test_money.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestMoney: - inverse_normalizer = ( - InverseNormalizer(lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('de/data_inverse_text_normalization/test_cases_money.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer = ( - Normalizer(input_case='cased', lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - - normalizer_with_audio = ( - NormalizerWithAudio(input_case='cased', lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) - if CACHE_DIR and PYNINI_AVAILABLE and RUN_AUDIO_BASED_TESTS - else None - ) - - @parameterized.expand(parse_test_case_file('de/data_text_normalization/test_cases_money.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, expected, test_input): - pred = self.normalizer.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio: - pred_non_deterministic = self.normalizer_with_audio.normalize( - test_input, n_tagged=1000, punct_post_process=False - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/de/test_normalization_with_audio.py b/tests/nemo_text_processing/de/test_normalization_with_audio.py deleted file mode 100644 index 032d0ebf555f..000000000000 --- a/tests/nemo_text_processing/de/test_normalization_with_audio.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from nemo.utils import logging - -from ..utils import CACHE_DIR, get_test_cases_multiple - -try: - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestNormalizeWithAudio: - - normalizer_de = ( - NormalizerWithAudio(input_case='cased', lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(get_test_cases_multiple('de/data_text_normalization/test_cases_normalize_with_audio.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer_de.normalize(test_input, n_tagged=1000, punct_post_process=False) - logging.info(expected) - logging.info("pred") - logging.info(pred) - assert len(set(pred).intersection(set(expected))) == len( - expected - ), f'missing: {set(expected).difference(set(pred))}' diff --git a/tests/nemo_text_processing/de/test_ordinal.py b/tests/nemo_text_processing/de/test_ordinal.py deleted file mode 100644 index 8a45b8853d48..000000000000 --- a/tests/nemo_text_processing/de/test_ordinal.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestOrdinal: - inverse_normalizer = ( - InverseNormalizer(lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('de/data_inverse_text_normalization/test_cases_ordinal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer_with_audio = ( - NormalizerWithAudio(input_case='cased', lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) - if CACHE_DIR and RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(parse_test_case_file('de/data_text_normalization/test_cases_ordinal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, expected, test_input): - - if self.normalizer_with_audio: - pred_non_deterministic = self.normalizer_with_audio.normalize( - test_input, n_tagged=1000, punct_post_process=False - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/de/test_sparrowhawk_inverse_text_normalization.sh b/tests/nemo_text_processing/de/test_sparrowhawk_inverse_text_normalization.sh deleted file mode 100644 index d7aba4c020c4..000000000000 --- a/tests/nemo_text_processing/de/test_sparrowhawk_inverse_text_normalization.sh +++ /dev/null @@ -1,84 +0,0 @@ -#! /bin/sh - -PROJECT_DIR=/workspace/tests - -runtest () { - input=$1 - cd /workspace/sparrowhawk/documentation/grammars - - # read test file - while read testcase; do - IFS='~' read spoken written <<< $testcase - denorm_pred=$(echo $spoken | normalizer_main --config=sparrowhawk_configuration.ascii_proto 2>&1 | tail -n 1 | sed 's/\xC2\xA0/ /g') - - # trim white space - written="$(echo -e "${written}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - denorm_pred="$(echo -e "${denorm_pred}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - - # input expected actual - assertEquals "$spoken" "$written" "$denorm_pred" - done < "$input" -} - -testITNCardinal() { - input=$PROJECT_DIR/de/data_inverse_text_normalization/test_cases_cardinal.txt - runtest $input -} - -testITNDate() { - input=$PROJECT_DIR/de/data_inverse_text_normalization/test_cases_date.txt - runtest $input -} - -testITNDecimal() { - input=$PROJECT_DIR/de/data_inverse_text_normalization/test_cases_decimal.txt - runtest $input -} - -testITNOrdinal() { - input=$PROJECT_DIR/de/data_inverse_text_normalization/test_cases_ordinal.txt - runtest $input -} - -testITNFraction() { - input=$PROJECT_DIR/de/data_inverse_text_normalization/test_cases_fraction.txt - runtest $input -} - -testITNTime() { - input=$PROJECT_DIR/de/data_inverse_text_normalization/test_cases_time.txt - runtest $input -} - -testITNMeasure() { - input=$PROJECT_DIR/de/data_inverse_text_normalization/test_cases_measure.txt - runtest $input -} - -testITNMoney() { - input=$PROJECT_DIR/de/data_inverse_text_normalization/test_cases_money.txt - runtest $input -} - -testITNWhitelist() { - input=$PROJECT_DIR/de/data_inverse_text_normalization/test_cases_whitelist.txt - runtest $input -} - -testITNTelephone() { - input=$PROJECT_DIR/de/data_inverse_text_normalization/test_cases_telephone.txt - runtest $input -} - -testITNElectronic() { - input=$PROJECT_DIR/de/data_inverse_text_normalization/test_cases_electronic.txt - runtest $input -} - -testITNWord() { - input=$PROJECT_DIR/de/data_inverse_text_normalization/test_cases_word.txt - runtest $input -} - -# Load shUnit2 -. $PROJECT_DIR/../shunit2/shunit2 diff --git a/tests/nemo_text_processing/de/test_sparrowhawk_normalization.sh b/tests/nemo_text_processing/de/test_sparrowhawk_normalization.sh deleted file mode 100644 index 3dfdfe9f3cba..000000000000 --- a/tests/nemo_text_processing/de/test_sparrowhawk_normalization.sh +++ /dev/null @@ -1,84 +0,0 @@ -#! /bin/sh - -PROJECT_DIR=/workspace/tests - -runtest () { - input=$1 - cd /workspace/sparrowhawk/documentation/grammars - - # read test file - while read testcase; do - IFS='~' read spoken written <<< $testcase - denorm_pred=$(echo $spoken | normalizer_main --config=sparrowhawk_configuration.ascii_proto 2>&1 | tail -n 1 | sed 's/\xC2\xA0/ /g') - - # trim white space - written="$(echo -e "${written}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - denorm_pred="$(echo -e "${denorm_pred}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - - # input expected actual - assertEquals "$written" "$spoken" "$denorm_pred" - done < "$input" -} - -testTNCardinal() { - input=$PROJECT_DIR/de/data_text_normalization/test_cases_cardinal.txt - runtest $input -} - -testTNDate() { - input=$PROJECT_DIR/de/data_text_normalization/test_cases_date.txt - runtest $input -} - -testTNDecimal() { - input=$PROJECT_DIR/de/data_text_normalization/test_cases_decimal.txt - runtest $input -} - -testTNOrdinal() { - input=$PROJECT_DIR/de/data_text_normalization/test_cases_ordinal.txt - runtest $input -} - -testTNFraction() { - input=$PROJECT_DIR/de/data_text_normalization/test_cases_fraction.txt - runtest $input -} - -testTNTime() { - input=$PROJECT_DIR/de/data_text_normalization/test_cases_time.txt - runtest $input -} - -testTNMeasure() { - input=$PROJECT_DIR/de/data_text_normalization/test_cases_measure.txt - runtest $input -} - -testTNMoney() { - input=$PROJECT_DIR/de/data_text_normalization/test_cases_money.txt - runtest $input -} - -testTNWhitelist() { - input=$PROJECT_DIR/de/data_text_normalization/test_cases_whitelist.txt - runtest $input -} - -testTNTelephone() { - input=$PROJECT_DIR/de/data_text_normalization/test_cases_telephone.txt - runtest $input -} - -testTNElectronic() { - input=$PROJECT_DIR/de/data_text_normalization/test_cases_electronic.txt - runtest $input -} - -testTNWord() { - input=$PROJECT_DIR/de/data_text_normalization/test_cases_word.txt - runtest $input -} - -# Load shUnit2 -. $PROJECT_DIR/../shunit2/shunit2 diff --git a/tests/nemo_text_processing/de/test_telephone.py b/tests/nemo_text_processing/de/test_telephone.py deleted file mode 100644 index d26dbeb8e989..000000000000 --- a/tests/nemo_text_processing/de/test_telephone.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestTelephone: - inverse_normalizer = ( - InverseNormalizer(lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('de/data_inverse_text_normalization/test_cases_telephone.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer = ( - Normalizer(input_case='cased', lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - - normalizer_with_audio = ( - NormalizerWithAudio(input_case='cased', lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) - if CACHE_DIR and PYNINI_AVAILABLE and RUN_AUDIO_BASED_TESTS - else None - ) - - @parameterized.expand(parse_test_case_file('de/data_text_normalization/test_cases_telephone.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, expected, test_input): - pred = self.normalizer.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio: - pred_non_deterministic = self.normalizer_with_audio.normalize( - test_input, n_tagged=1000, punct_post_process=False - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/de/test_time.py b/tests/nemo_text_processing/de/test_time.py deleted file mode 100644 index b3fd8b1604a4..000000000000 --- a/tests/nemo_text_processing/de/test_time.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestTime: - inverse_normalizer = ( - InverseNormalizer(lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('de/data_inverse_text_normalization/test_cases_time.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer = ( - Normalizer(input_case='cased', lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - - normalizer_with_audio = ( - NormalizerWithAudio(input_case='cased', lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) - if CACHE_DIR and PYNINI_AVAILABLE and RUN_AUDIO_BASED_TESTS - else None - ) - - @parameterized.expand(parse_test_case_file('de/data_text_normalization/test_cases_time.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, expected, test_input): - pred = self.normalizer.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio: - pred_non_deterministic = self.normalizer_with_audio.normalize( - test_input, n_tagged=1000, punct_post_process=False - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/de/test_whitelist.py b/tests/nemo_text_processing/de/test_whitelist.py deleted file mode 100644 index b340fb79d44b..000000000000 --- a/tests/nemo_text_processing/de/test_whitelist.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestWhitelist: - inverse_normalizer = ( - InverseNormalizer(lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('de/data_inverse_text_normalization/test_cases_whitelist.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer = ( - Normalizer(input_case='cased', lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - - normalizer_with_audio = ( - NormalizerWithAudio(input_case='cased', lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) - if CACHE_DIR and PYNINI_AVAILABLE and RUN_AUDIO_BASED_TESTS - else None - ) - - @parameterized.expand(parse_test_case_file('de/data_text_normalization/test_cases_whitelist.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, expected, test_input): - pred = self.normalizer.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio: - pred_non_deterministic = self.normalizer_with_audio.normalize( - test_input, n_tagged=1000, punct_post_process=False - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/de/test_word.py b/tests/nemo_text_processing/de/test_word.py deleted file mode 100644 index 2c66d58a287e..000000000000 --- a/tests/nemo_text_processing/de/test_word.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestWord: - inverse_normalizer = ( - InverseNormalizer(lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('de/data_inverse_text_normalization/test_cases_word.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer = ( - Normalizer(input_case='cased', lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - - normalizer_with_audio = ( - NormalizerWithAudio(input_case='cased', lang='de', cache_dir=CACHE_DIR, overwrite_cache=False) - if CACHE_DIR and PYNINI_AVAILABLE and RUN_AUDIO_BASED_TESTS - else None - ) - - @parameterized.expand(parse_test_case_file('de/data_text_normalization/test_cases_word.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, expected, test_input): - pred = self.normalizer.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio: - pred_non_deterministic = self.normalizer_with_audio.normalize( - test_input, n_tagged=1000, punct_post_process=False - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/en/__init__.py b/tests/nemo_text_processing/en/__init__.py deleted file mode 100644 index 9e3250071955..000000000000 --- a/tests/nemo_text_processing/en/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_cardinal.txt b/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_cardinal.txt deleted file mode 100644 index d865e7eafa67..000000000000 --- a/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_cardinal.txt +++ /dev/null @@ -1,28 +0,0 @@ -nine trillion seven hundred eighty nine billion three hundred eighty two million five hundred thirty six thousand one hundred thirty~9789382536130 -two hundred and fifty four~254 -one hundred forty seven thousand four hundred fifty one~147451 -one million one hundred fifty six thousand one hundred seventy three~1156173 -one billion five hundred ninety three million seventy two thousand nine hundred sixty one~1593072961 -ninety seven quadrillion eight hundred eight trillion two hundred sixty four billion seven hundred seventy two million seven hundred ninety two thousand five~97808264772792005 -seventeen sextillion eight hundred fifty five quintillion thirty six quadrillion six hundred fifty seven trillion seven billion five hundred ninety six million one hundred ten thousand nine hundred forty nine~17855036657007596110949 -ten quadrillion ten trillion ten million one hundred thousand ten~10010000010100010 -minus twenty five thousand thirty seven~-25037 -one quadrillion two hundred sixty four trillion three hundred one billion nine hundred thirty eight million one hundred four~1264301938000104 -minus sixty~-60 -forty six thousand six hundred sixty four~46664 -sixty~60 -zero~zero -two million three~2000003 -one thousand thirteen~1013 -one thousand one~1001 -one thousand one hundred~1100 -one thousand twenty six~1026 -one thousand one hundred twenty six~1126 -eighteen million four hundred fifty thousand nine hundred ninety~18450990 -eighteen million nine hundred forty thousand seven hundred twenty two~18940722 -eighteen million six hundred ninety thousand nine hundred sixteen~18690916 -eighteen thousand eight hundred eighty~18880 -eleven hundred~1100 -twenty one hundred~2100 -twenty one hundred and eleven~2111 -eleven hundred twenty one~1121 diff --git a/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_date.txt b/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_date.txt deleted file mode 100644 index 3ec42681447a..000000000000 --- a/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_date.txt +++ /dev/null @@ -1,34 +0,0 @@ -july twenty fifth two thousand twelve~july 25 2012 -nineteen eighties~1980s -two thousand and twenty~2020 -two thousand and nine~2009 -the twenty fifth of july twenty twelve~25 july 2012 -the twenty fifth of july two thousand twelve~25 july 2012 -the twenty second of july twenty twelve~22 july 2012 -the fifteenth of january~15 january -the seventeenth of may twenty ten~17 may 2010 -january first~january 1 -july twenty second two thousand eight~july 22 2008 -june thirty~june 30 -july twenty fifth twenty twelve~july 25 2012 -nineteen seventeen~1917 -twenty twelve~2012 -march sixteen sixty five~march 1665 -sixteen sixty five~1665 -july two thousand twelve~july 2012 -october nineteen oh five~october 1905 -july fifteen o six~july 1506 -the twenty fifth of july twenty twelve~25 july 2012 -july twenty fifth twenty twelve~july 25 2012 -july twenty fifth two thousand twelve~july 25 2012 -july one thousand eight hundred seventy six~july 1876 -february twenty fifth twenty sixteen~february 25 2016 -november twenty fourth twenty fourteen~november 24 2014 -nineteen ninety four~1994 -two thousand three~2003 -one thousand eight~1008 -nineteen seventy six~1976 -june twentieth twenty fourteen~june 20 2014 -nineteen seventy three~1973 -nineteen seventy five~1975 -eleven fifty five~1155 \ No newline at end of file diff --git a/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_decimal.txt b/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_decimal.txt deleted file mode 100644 index 2cd2272837ef..000000000000 --- a/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_decimal.txt +++ /dev/null @@ -1,63 +0,0 @@ -five point two million~5.2 million -one hundred sixty four point five eight thousand~164.58 thousand -four hundred million~400 million -fifty billion~50 billion -four hundred five billion~405 billion -four point eight five billion~4.85 billion -one hundred billion~100 billion -one hundred ten billion~110 billion -one hundred thirty two billion~132 billion -one point eight four billion~1.84 billion -one point eight one billion~1.81 billion -one point five nine billion~1.59 billion -one point four five three billion~1.453 billion -one point seven two billion~1.72 billion -one point two five billion~1.25 billion -thirteen billion~13 billion -thirty billion~30 billion -two thousand eight hundred five point eight seven three billion~2805.873 billion -seventy trillion~70 trillion -thirteen million~13 million -eighteen billion~18 billion -four hundred fifty million~450 million -one hundred thirty million~130 million -ten million~10 million -four hundred million~400 million -five million~5 million -five hundred million~500 million -twelve million~12 million -thirteen million~13 million -four million~4 million -forty five million~45 million -fifteen million~15 million -fifteen trillion~15 trillion -fifteen billion~15 billion -two million~2 million -eight million~8 million -point one two o five~.1205 -minus sixty point two four zero zero~-60.2400 -zero point two six~0.26 -point zero two~.02 -sixty point two~60.2 -eighteen~18 -eighteen point eight five~18.85 -eighteen point five o~18.50 -eighteen point five six~18.56 -eighteen point nine~18.9 -eighteen point o five~18.05 -eighteen point one two~18.12 -eighteen point o one~18.01 -eighteen point o o o~18.000 -eighteen point six~18.6 -eighteen point three o o~18.300 -eighteen point three six~18.36 -eighteen point two five~18.25 -eighteen point two two~18.22 -eight hundred eighteen point three o three~818.303 -eight hundred eight point eight~808.8 -eight hundred eight point zero~808.0 -eight hundred eighty eight point one~888.1 -eight hundred eighty four point three~884.3 -eight hundred eighty two point eight~882.8 -eight hundred eighty two point zero~882.0 -eight hundred forty five point nine four~845.94 \ No newline at end of file diff --git a/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_electronic.txt b/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_electronic.txt deleted file mode 100644 index 2efa54aea236..000000000000 --- a/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_electronic.txt +++ /dev/null @@ -1,25 +0,0 @@ -a dot b c at g mail dot com~a.bc@gmail.com -a at gmail dot com~a@gmail.com -a at m s n dot fr~a@msn.fr -a at a o l dot com~a@aol.com -a at m s n dot com~a@msn.com -a at nvidia dot com~a@nvidia.com -a dot b c at nvidia dot com~a.bc@nvidia.com -c d f at a b c dot e d u~cdf@abc.edu -a b c at g mail dot a b c~abc@gmail.abc -a b c at a b c dot com~abc@abc.com -a s d f one two three at a b c dot com~asdf123@abc.com -a one b two at a b c dot com~a1b2@abc.com -a b three dot s d d dot three at g mail dot com~ab3.sdd.3@gmail.com -dot three at g mail dot com~dot 3@gmail.com -one three at g mail dot com~13@gmail.com -a b three hyphen s d d dash three at g mail dot com~ab3-sdd-3@gmail.com -h t t p colon slash slash w w w dot o u r d a i l y n e w s dot com dot s m~http://www.ourdailynews.com.sm -h t t p colon slash slash w w w dot c o m d a i l y n e w s dot a b dot s m~http://www.comdailynews.ab.sm -h t t p colon slash slash w w w dot c o m d a i l y n e w s dot a b slash s m~http://www.comdailynews.ab/sm -w w w dot c o m d a i l y n e w s dot a b slash s m~www.comdailynews.ab/sm -c o m d a i l y n e w s dot a b slash s m~comdailynews.ab/sm -n vidia dot com~nvidia.com -abc at gmail dot com~abc@gmail.com -athreed at gmail dot com~athreed@gmail.com -kore dot ai~kore.ai diff --git a/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_measure.txt b/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_measure.txt deleted file mode 100644 index 4f6f540e6da4..000000000000 --- a/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_measure.txt +++ /dev/null @@ -1,112 +0,0 @@ -two hundred meters~200 m -fifty six point three per square kilometer~56.3 /km² -two hundred kilometers per hour~200 km/h -twenty eight kilograms force per square centimeter~28 kgf/cm² -forty two thousand two hundred fifty nine per square meter~42259 /m² -minus two thousand twelve kilo liters~-2012 kl -minus sixty six kilograms~-66 kg -two kilo watt hours~2 kwh -one point o o o o two eight cubic deci meters~1.000028 dm³ -seven point five peta bytes~7.5 pb -three hours~3 h -one milli volt~1 mv -two cubic meters~2 m³ -ninety grams~90 g -one hundred twenty four point three lumens~124.3 lm -four hundred forty milliliters~440 ml -thirty one thousand four hundred eighty square feet~31480 sq ft -one thousand six hundred hours~1600 h -thirty one thousand four hundred eighty square feet~31480 sq ft -two square miles~2 sq mi -zero point one nine square miles~0.19 sq mi -one thousand five hundred thirty one c c~1531 cc -three hundred micrometers~300 μm -sixty five thousand square kilometers~65000 km² -two miles per hour~2 mph -two hundred forty five miles per hour~245 mph -one hundred fifty c c~150 cc -sixty point two four zero zero kilograms~60.2400 kg -zero feet~0 ft -zero foot~zero foot -two feet~2 ft -twenty foot~20 foot -point two meters~.2 m -two square meters~2 m² -eighteen feet~18 ft -eighteen mega siemens~18 ms -eighteen ounces~18 oz -eighteen point five kilometers~18.5 km -eighteen point five two square kilometers~18.52 km² -eighteen point nine one square kilometers~18.91 km² -eighteen point one four percent~18.14 % -eighteen point one six percent~18.16 % -eighteen point one square kilometers~18.1 km² -eighteen point six percent~18.6 % -eighteen point two two kilometers~18.22 km -eighteen point zero kilometers~18.0 km -eighteen point zero percent~18.0 % -eighteen square kilometers~18 km² -eighteen thousand eight hundred giga watt hours~18800 gwh -eighteen thousand seven hundred hectares~18700 ha -eight hectares~8 ha -eight hundred eighty five astronomical units~885 au -eight hundred eighty hectares~880 ha -eight hundred eighty kilobytes~880 kb -eight hundred eighty kilometers~880 km -eight hundred eighty nine feet~889 ft -eight hundred eighty six kilometers~886 km -eight hundred eighty two megawatts~882 mw -eight hundred feet~800 ft -eight hundred fifty five square kilometers~855 km² -eight hundred fifty megahertz~850 mhz -eight hundred fifty meters~850 m -eight hundred fifty nanometers~850 nm -eight hundred fifty one meters~851 m -eight hundred fifty seven square kilometers~857 km² -eight hundred fifty three meters~853 m -eight hundred fifty three point six meters~853.6 m -eight hundred five point four six square kilometers~805.46 km² -eight hundred forty two point nine meters~842.9 m -eight hundred forty two square kilometers~842 km² -eight hundred gigabytes~800 gb -eight hundred horsepower~800 hp -eight hundred kilograms~800 kg -eight hundred kilo watt hours~800 kwh -eight hundred kilowatts~800 kw -eight hundred megahertz~800 mhz -eight hundred ninety four c c~894 cc -eight hundred ninety kilowatts~890 kw -eight hundred ninety millimeters~890 mm -eight hundred ninety two square kilometers~892 km² -eight hundred seventy horsepower~870 hp -eight hundred seventy meters~870 m -eight hundred sixty kilograms~860 kg -eight hundred sixty kilometers~860 km -eight hundred sixty miles~860 mi -eight hundred sixty six feet~866 ft -eight hundred ten hectares~810 ha -eight hundred ten kilohertz~810 khz -eight hundred thirty eight point two millimeters~838.2 mm -eight hundred thirty five kilometers~835 km -eight hundred thirty kilohertz~830 khz -eight hundred thirty megawatts~830 mw -eight hundred thirty nine kilometers~839 km -eight hundred thirty six meters~836 m -eight hundred twenty feet~820 ft -eight hundred twenty kilometers~820 km -eight hundred twenty meters~820 m -eight hundred twenty one point zero feet~821.0 ft -eight hundred two point eight nine kilometers~802.89 km -eight hundred volts~800 v -eight kilobits~8 kb -eight kilograms~8 kg -eight million two hundred thousand feet~8200000 ft -eight point eight kilometers~8.8 km -eight point eight meters~8.8 m -eight point eight miles~8.8 mi -eight point five centimeters~8.5 cm -eight point five five percent~8.55 % -eight point five megawatts~8.5 mw -eight point five meters~8.5 m -eight point five two percent~8.52 % -eight point four four percent~8.44 % diff --git a/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_money.txt b/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_money.txt deleted file mode 100644 index 3fbb895f96ec..000000000000 --- a/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_money.txt +++ /dev/null @@ -1,52 +0,0 @@ -two dollars~$2 -one cent~$0.01 -four united states dollars and sixty nine cents~$4.69 -seventy five dollars sixty three~$75.63 -twenty nine dollars fifty cents~$29.50 -eleven dollars and fifty one cents~$11.51 -nine hundred ninety three dollars and ninety two cents~$993.92 -four hundred sixty billion won~₩460 billion -thirty billion yen~¥30 billion -two point five billion dollars~$2.5 billion -forty five billion dollars~$45 billion -fifty million dollars~$50 million -fifty billion dollars~$50 billion -zero point two million dollars~$0.2 million -fifteen point two billion dollars~$15.2 billion -one point six nine billion yuan~1.69 billion yuan -one point four three six billion yuan~1.436 billion yuan -four million yuan~4 million yuan -one dollar~$1 -fifteen thousand dollars~$15000 -one dollars~one dollars -twenty dollar~20 dollar -twenty point five o six dollars~$20.506 -point five o six dollars~$.506 -eighteen dollars~$18 -eighteen million nine hundred twenty five thousand dollars~$18925000 -eighteen thousand eight hundred fifty four dollars~$18854 -eighteen thousand eight hundred one dollars~$18801 -eighteen thousand eight hundred seventy five dollars~$18875 -eighteen thousand eighty one dollars~$18081 -eighteen thousand fifty two dollars~$18052 -eighteen thousand five hundred forty two dollars~$18542 -eighteen thousand five hundred nineteen dollars~$18519 -eighteen thousand five hundred seventy dollars~$18570 -eighteen thousand five hundred seventy eight dollars~$18578 -eighteen thousand five hundred sixteen dollars~$18516 -eighteen thousand four hundred eighty two dollars~$18482 -eighteen thousand four hundred seventy eight dollars~$18478 -eighteen thousand four hundred sixty eight dollars~$18468 -eighteen thousand nine hundred three dollars~$18903 -eighteen thousand nine hundred twenty nine dollars~$18929 -eighteen thousand ninety five dollars~$18095 -eighteen thousand one hundred seventeen dollars~$18117 -eighteen thousand one hundred twenty eight dollars~$18128 -eighteen thousand one hundred twenty five dollars~$18125 -eighteen thousand one hundred twenty four dollars~$18124 -eighteen thousand one hundred twenty nine dollars~$18129 -one thousand fifty five dollars~$1055 -one fifty five dollars~$155 -fifteen hundred dollars~$1500 -ninety nine hundred dollars~$9900 -ninety nine hundred and fifteen dollars and one cent~$9915.01 diff --git a/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_ordinal.txt b/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_ordinal.txt deleted file mode 100644 index f701a68bb798..000000000000 --- a/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_ordinal.txt +++ /dev/null @@ -1,34 +0,0 @@ -one hundredth~100th -twenty five thousand one hundred eleventh~25111th -second~2nd -zeroth~0th -first~1st -second~2nd -third~3rd -fourth~4th -eleventh~11th -twelfth~12th -thirteenth~13th -twenty first~21st -twenty third~23rd -one hundred eleventh~111th -one thousandth~1000th -one hundred twenty first~121st -eleven hundred twenty first~1121st -second~2nd -tenth~10th -sixth~6th -third~3rd -nineteenth~19th -third~3rd -twelfth~12th -forty eighth~48th -seventy first~71st -third~3rd -forty second~42nd -seventeenth~17th -twentieth~20th -twenty first~21st -seventh~7th -second~2nd -fifth~5th \ No newline at end of file diff --git a/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_telephone.txt b/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_telephone.txt deleted file mode 100644 index 92e2d6c3cacf..000000000000 --- a/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_telephone.txt +++ /dev/null @@ -1,18 +0,0 @@ -one two three one two three five six seven eight~123-123-5678 -plus nine one one two three one two three five six seven eight~+91 123-123-5678 -plus forty four one two three one two three five six seven eight~+44 123-123-5678 -four one two three one two three five six seven eight~4 123-123-5678 -zero two three one two three five six seven eight~023-123-5678 -o two three one two three five six seven eight~023-123-5678 -oh two three one two three five six seven eight~023-123-5678 -double oh three one two three five six seven eight~003-123-5678 -four three two double seven three two one four three two one four three double zero five~four 3277 3214 3214 3005 -one two three dot one two three dot o dot four o~123.123.0.40 -one twenty three dot one two three dot o dot four o~123.123.0.40 -two two five dot double five dot o dot four o~225.55.0.40 -two two five dot double five dot o dot forty five~225.55.0.45 -ssn is seven double nine one two three double one three~ssn is 799-12-3113 -seven nine nine~799 -a b nine~ab9 -a b c~a b c -five w k r a three one~5wkra31 diff --git a/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_time.txt b/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_time.txt deleted file mode 100644 index 18f8d789b660..000000000000 --- a/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_time.txt +++ /dev/null @@ -1,29 +0,0 @@ -eight oclock g m t~08:00 gmt -seven a m e s t~07:00 a.m. est -two p m~02:00 p.m. -two thirty~02:30 -three o'clock~03:00 -quarter past one~01:15 -half past three~03:30 -eight fifty one~08:51 -eight fifty two~08:52 -eight forty~08:40 -eight nineteen~08:19 -eight o six~08:06 -eight thirty eight~08:38 -eight thirty two~08:32 -eight twenty nine~08:29 -eleven fifty five p m~11:55 p.m. -eleven fifty three p m~11:53 p.m. -eleven forty a m~11:40 a.m. -eleven forty five a m~11:45 a.m. -eleven forty p m~11:40 p.m. -eleven forty six a m~11:46 a.m. -eleven o six p m~11:06 p.m. -eleven thirteen a m~11:13 a.m. -half past twelve~12:30 -quarter past one~01:15 -quarter to one~12:45 -quarter to twelve~11:45 -set alarm at ten to eleven pm~set alarm at 10:50 p.m. -one min to one am~12:59 a.m. diff --git a/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_whitelist.txt b/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_whitelist.txt deleted file mode 100644 index d8fa65ad33aa..000000000000 --- a/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_whitelist.txt +++ /dev/null @@ -1,7 +0,0 @@ -doctor dao~dr. dao -misses smith~mrs. smith -mister dao~mr. dao -saint george~st. george -i like for example ice cream~i like e.g. ice cream -s and p five hundred~s&p 500 -seven eleven stores~7-eleven stores diff --git a/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_word.txt b/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_word.txt deleted file mode 100644 index c21423e68ae9..000000000000 --- a/tests/nemo_text_processing/en/data_inverse_text_normalization/test_cases_word.txt +++ /dev/null @@ -1,52 +0,0 @@ -~ -, one~, one -, one , two , three , four~, one , two , three , four -e s three~es3 -yahoo!~yahoo! -twenty!~20 ! -x ~x -—~— -aaa~aaa -aabach~aabach -aabenraa~aabenraa -aabye~aabye -aaccessed~aaccessed -aach~aach -aachen's~aachen's -aadri~aadri -aafia~aafia -aagaard~aagaard -aagadu~aagadu -aagard~aagard -aagathadi~aagathadi -aaghart's~aaghart's -aagnes~aagnes -aagomoni~aagomoni -aagon~aagon -aagoo~aagoo -aagot~aagot -aahar~aahar -aahh~aahh -aahperd~aahperd -aaibinterstate~aaibinterstate -aajab~aajab -aakasa~aakasa -aakervik~aakervik -aakirkeby~aakirkeby -aalam~aalam -aalbaek~aalbaek -aaldiu~aaldiu -aalem~aalem -a'ali~a'ali -aalilaassamthey~aalilaassamthey -aalin~aalin -aaliyan~aaliyan -aaliyan's~aaliyan's -aamadu~aamadu -aamara~aamara -aambala~aambala -aamera~aamera -aamer's~aamer's -aamina~aamina -aaminah~aaminah -aamjiwnaang~aamjiwnaang diff --git a/tests/nemo_text_processing/en/data_text_normalization/test_cases_address.txt b/tests/nemo_text_processing/en/data_text_normalization/test_cases_address.txt deleted file mode 100644 index bd9394163d4d..000000000000 --- a/tests/nemo_text_processing/en/data_text_normalization/test_cases_address.txt +++ /dev/null @@ -1,9 +0,0 @@ -2788 San Tomas Expy, Santa Clara, CA 95051~twenty seven eighty eight San Tomas Expressway, Santa Clara, California nine five zero five one -2 San Tomas hwy, Santa, FL, 95051~two San Tomas Highway, Santa, Florida, nine five zero five one -123 Smth St, City, NY~one twenty three Smth Street, City, New York -123 Laguna Ct, Town~one twenty three Laguna Court, Town -1211 E Arques Ave~twelve eleven East Arques Avenue -708 N 1st St, San City~seven zero eight North first Street, San City -12 S 1st st~twelve South first Street -1990 for the Ata ST~nineteen ninety for the Ata ST -Main St.~Main St. diff --git a/tests/nemo_text_processing/en/data_text_normalization/test_cases_cardinal.txt b/tests/nemo_text_processing/en/data_text_normalization/test_cases_cardinal.txt deleted file mode 100644 index ea8af1b6a806..000000000000 --- a/tests/nemo_text_processing/en/data_text_normalization/test_cases_cardinal.txt +++ /dev/null @@ -1,17 +0,0 @@ -1~one -2~two --2~minus two -3~three -123~one hundred and twenty three -13,000~thirteen thousand -13000~one three zero zero zero -9000~nine thousand -9,000~nine thousand -123,000~one hundred twenty three thousand -123000~one two three zero zero zero -123,123,000~one hundred twenty three million one hundred twenty three thousand -123,000,012~one hundred twenty three million twelve -1,000,000~one million -1234567890123124~one two three four five six seven eight nine zero one two three one two four -978-0~nine hundred and seventy eight - zero -004~zero zero four diff --git a/tests/nemo_text_processing/en/data_text_normalization/test_cases_date.txt b/tests/nemo_text_processing/en/data_text_normalization/test_cases_date.txt deleted file mode 100644 index 8b97fce4a5f2..000000000000 --- a/tests/nemo_text_processing/en/data_text_normalization/test_cases_date.txt +++ /dev/null @@ -1,53 +0,0 @@ -july 25 2012~july twenty fifth twenty twelve -jul 25 2012~july twenty fifth twenty twelve -1980s~nineteen eighties -1980 s~nineteen eighties -25 july 2012~the twenty fifth of july twenty twelve -25 jul 2012~the twenty fifth of july twenty twelve -22 july 2012~the twenty second of july twenty twelve -25th july 2012~the twenty fifth of july twenty twelve -25th jul 2012~the twenty fifth of july twenty twelve -22nd july 2012~the twenty second of july twenty twelve -15 january~the fifteenth of january -17 may 2010~the seventeenth of may twenty ten -january 1~january first -june 30~june thirtieth -2012~twenty twelve -july 1506~july fifteen oh six -july 1876~july eighteen seventy six -1994~nineteen ninety four -1976~nineteen seventy six -june 20 2014~june twentieth twenty fourteen -1973~nineteen seventy three -1975~nineteen seventy five -1155~eleven fifty five -2006-08-05~august fifth two thousand six -august 23 2002~august twenty third two thousand two -16 July 1943~the sixteenth of july nineteen forty three -20 january~the twentieth of january -2016-11-03~november third twenty sixteen -2016/07/03~july third twenty sixteen -1980s~nineteen eighties -01.10.2010~january tenth twenty ten -10/06/2005~october sixth two thousand five -1910s~nineteen tens -2000s~two thousands -25 sept.~the twenty fifth of september -1000~one thousand -SEPT. 15~september fifteenth -JAN. 15 2020~january fifteenth twenty twenty -Jan. 15 2020~january fifteenth twenty twenty -Jan 15 2020~january fifteenth twenty twenty -jan. 15~january fifteenth -JAN 15 2020~january fifteenth twenty twenty -SEPTEMBER 15 twenty twenty~september fifteenth twenty twenty -11/17/05~november seventeenth zero five -01/07/98~january seventh ninety eight -One should go to the store on the 26th May in the evening~One should go to the store on the twenty sixth of may in the evening -'80s~eighties -Jan-15-2020~january fifteenth twenty twenty -Jan-15~january fifteenth -15-01-2020~the fifteenth of january twenty twenty -15.01.2020~the fifteenth of january twenty twenty -340 A.D~three forty AD -1998/2/30~february thirtieth nineteen ninety eight \ No newline at end of file diff --git a/tests/nemo_text_processing/en/data_text_normalization/test_cases_decimal.txt b/tests/nemo_text_processing/en/data_text_normalization/test_cases_decimal.txt deleted file mode 100644 index 9fa615018985..000000000000 --- a/tests/nemo_text_processing/en/data_text_normalization/test_cases_decimal.txt +++ /dev/null @@ -1,12 +0,0 @@ -.1665~point one six six five -2.0~two point zero -2.00~two point zero zero -2.050~two point zero five zero -100 million~one hundred million -0.1 billion~zero point one billion -.1 trillion~point one trillion --0.1~minus zero point one -0.004~zero point zero zero four -0.1 M~zero point one M -0.1 B~zero point one B -0.1 K~zero point one K diff --git a/tests/nemo_text_processing/en/data_text_normalization/test_cases_electronic.txt b/tests/nemo_text_processing/en/data_text_normalization/test_cases_electronic.txt deleted file mode 100644 index 61d94180dccf..000000000000 --- a/tests/nemo_text_processing/en/data_text_normalization/test_cases_electronic.txt +++ /dev/null @@ -1,29 +0,0 @@ -a.bc@gmail.com~a dot bc at gmail dot com -a@hotmail.de~a at hotmail dot DE -a@hotmail.fr~a at hotmail dot FR -a@hotmail.it~a at hotmail dot IT -a@aol.it~a at aol dot IT -a@msn.it~a at msn dot IT -cdf@abc.edu~cdf at abc dot EDU -abc@gmail.abc~abc at gmail dot ABC -abc@abc.com~abc at abc dot com -asdf123@abc.com~asdf one two three at abc dot com -a1b2@abc.com~a one b two at abc dot com -ab3.sdd.3@gmail.com~ab three dot sdd dot three at gmail dot com -email is abc1@gmail.com~email is abc one at gmail dot com -abs@nvidia.com~abs at nvidia dot com -email is a-1.b3_c&-d@gma4i-l.com test~email is a dash one dot b three underscore c ampersand dash d at gma four i dash l dot com test -nvidia.com~nvidia dot com -test.com~test dot com -http://www.ourdailynews.com.sm~HTTP semicolon slash slash WWW dot ourdailynews dot com dot SM -https://www.ourdailynews.com.sm~HTTPS semicolon slash slash WWW dot ourdailynews dot com dot SM -www.ourdailynews.com.sm~WWW dot ourdailynews dot com dot SM -www.ourdailynews.com/123-sm~WWW dot ourdailynews dot com slash one two three dash SM -sdf@gmail.com.sm~sdf at gmail dot com dot SM -sdf@gmail.com/123-sm~sdf at gmail dot com slash one two three dash SM -sdf@gmail.abc/123-sm~sdf at gmail dot ABC slash one two three dash SM -ourdailynews.com/123-sm~ourdailynews dot com slash one two three dash SM -ourdailynews.abc~ourdailynews.abc -file:///c/code/NeMo/docs/build/html/processing/intro.html~file semicolon slash slash slash c slash code slash NeMo slash docs slash build slash html slash processing slash intro dot HTML -file:///photos/image.jpg~file semicolon slash slash slash photos slash image dot jpeg -electronic test.com and test2.uk~electronic test dot com and test two dot UK \ No newline at end of file diff --git a/tests/nemo_text_processing/en/data_text_normalization/test_cases_fraction.txt b/tests/nemo_text_processing/en/data_text_normalization/test_cases_fraction.txt deleted file mode 100644 index 3c2316cb3fc2..000000000000 --- a/tests/nemo_text_processing/en/data_text_normalization/test_cases_fraction.txt +++ /dev/null @@ -1,16 +0,0 @@ -1/2007~one two thousand seventh -12639/12640~twelve thousand six hundred thirty nine twelve thousand six hundred fortieths -3 2/4~three and two quarters -1/4~one quarter -1/4th~one quarter -2/4th~two quarters -1/4TH~one quarter -1/3RD~one third -31/32~thirty one thirty seconds -22/3~twenty two thirds -1/3~one third -2 142/1~two and one hundred forty two over one -1/2~one half -2 1/2~two and a half -3 5/2~three and five halves -1795 / 1805~one thousand seven hundred ninety five one thousand eight hundred fifths \ No newline at end of file diff --git a/tests/nemo_text_processing/en/data_text_normalization/test_cases_math.txt b/tests/nemo_text_processing/en/data_text_normalization/test_cases_math.txt deleted file mode 100644 index 4975d6fdab0d..000000000000 --- a/tests/nemo_text_processing/en/data_text_normalization/test_cases_math.txt +++ /dev/null @@ -1,4 +0,0 @@ -1-2=5~one minus two equals five -1- 2 = 5~one minus two equals five -y=x +1~y equals x plus one -x +1 = y~x plus one equals y diff --git a/tests/nemo_text_processing/en/data_text_normalization/test_cases_measure.txt b/tests/nemo_text_processing/en/data_text_normalization/test_cases_measure.txt deleted file mode 100644 index 4d16a18eda89..000000000000 --- a/tests/nemo_text_processing/en/data_text_normalization/test_cases_measure.txt +++ /dev/null @@ -1,19 +0,0 @@ -12kg~twelve kilograms -12KG~twelve kilograms -12/kg~twelve per kilogram -12kg/kg~twelve kilograms per kilogram -1 mbps~one megabit per second -3 mbps~three megabits per second -3 cc/s~three CC per S -100 million kg~one hundred million kilograms -a 123.2-millimeters long~a one hundred and twenty three point two millimeters long -covid-19.5~covid- nineteen point five -7.2-millimeter bullet~seven point two millimeter bullet -4 1/2 lbs~four and a half pounds -4.4x~four point four x -80 s~eighty S -2°C~two degrees Celsius -1°C~one degree Celsius -1234-123kg~one thousand two hundred and thirty four to one hundred and twenty three kilograms -45º&C~forty five degree and C -41,459.00 km³~forty one thousand four hundred and fifty nine point zero zero cubic kilometers diff --git a/tests/nemo_text_processing/en/data_text_normalization/test_cases_money.txt b/tests/nemo_text_processing/en/data_text_normalization/test_cases_money.txt deleted file mode 100644 index d1218e8bd758..000000000000 --- a/tests/nemo_text_processing/en/data_text_normalization/test_cases_money.txt +++ /dev/null @@ -1,65 +0,0 @@ -$2~two dollars -$2.00~two dollars -₩460 billion~four hundred sixty billion won -¥30 billion~thirty billion yen -¥30 b~thirty billion yen -¥30b~thirty billion yen -¥30B~thirty billion yen -$45 billion~forty five billion dollars -$0.2 million~zero point two million dollars -$2.5 million~two point five million dollars -$15.2 billion~fifteen point two billion dollars -1.436 billion yuan~one point four three six billion yuan -4 million yuan~four million yuan -$15000~fifteen thousand dollars -$1~one dollar -$1.00~one dollar -$1.00~one dollar -$2~two dollars -$2.00~two dollars -$2.000~two dollars -$20~twenty dollars -$20.506~twenty point five zero six dollars -$20.50~twenty dollars fifty cents -$20.500~twenty dollars fifty cents -$20.5~twenty dollars fifty cents -$20.01~twenty dollars one cent -$20.010~twenty dollars one cent -$0.010~one cent -$0.02~two cents -$0.20~twenty cents -$.01~one cent -$.506~point five zero six dollars -$18~eighteen dollars -$18925000~eighteen million nine hundred twenty five thousand dollars -$18,925,000~eighteen million nine hundred twenty five thousand dollars -$18854~eighteen thousand eight hundred and fifty four dollars -$18801~eighteen thousand eight hundred and one dollars -$18875~eighteen thousand eight hundred and seventy five dollars -$18081~eighteen thousand and eighty one dollars -$18052~eighteen thousand and fifty two dollars -$18542~eighteen thousand five hundred and forty two dollars -$18519~eighteen thousand five hundred and nineteen dollars -$18570~eighteen thousand five hundred and seventy dollars -$18578~eighteen thousand five hundred and seventy eight dollars -$18516~eighteen thousand five hundred and sixteen dollars -$18482~eighteen thousand four hundred and eighty two dollars -$18478~eighteen thousand four hundred and seventy eight dollars -$18468~eighteen thousand four hundred and sixty eight dollars -$18903~eighteen thousand nine hundred and three dollars -$18929~eighteen thousand nine hundred and twenty nine dollars -$18095~eighteen thousand and ninety five dollars -$18117~eighteen thousand one hundred and seventeen dollars -$18128~eighteen thousand one hundred and twenty eight dollars -$18125~eighteen thousand one hundred and twenty five dollars -$18124~eighteen thousand one hundred and twenty four dollars -$18129~eighteen thousand one hundred and twenty nine dollars -£18000~eighteen thousand pounds -£1.20~one pound twenty pence -£1.111~one point one one one pounds -£2.01~two pounds one penny -$50.00-$100.00~fifty dollars - one hundred dollars -$1,925.21~one thousand nine hundred and twenty five dollars twenty one cents -$1,234.123~one thousand two hundred and thirty four point one two three dollars -US $76.3 trillion~US seventy six point three trillion dollars -US$76.3 trillion~seventy six point three trillion us dollars diff --git a/tests/nemo_text_processing/en/data_text_normalization/test_cases_normalize_with_audio.txt b/tests/nemo_text_processing/en/data_text_normalization/test_cases_normalize_with_audio.txt deleted file mode 100644 index e012cd3c3255..000000000000 --- a/tests/nemo_text_processing/en/data_text_normalization/test_cases_normalize_with_audio.txt +++ /dev/null @@ -1,165 +0,0 @@ -~123 -one two three -one twenty three -one hundred twenty three -one hundred and twenty three -~$123 -one hundred twenty three dollars -one hundred and twenty three dollars -~They bought 23 books for $123.02. -They bought twenty three books for one hundred twenty three dollars and two cents. -They bought twenty three books for one hundred twenty three dollars two cents. -~$1.01 -one dollar and one cent -one us dollar one cent -~1.24 -one point two four -one point twenty four -~~1/24 -january twenty four -the twenty fourth of january -january twenty fourth -~$1.21 -one dollar and twenty one cents -one dollar twenty one cents -~£1.00 -one pound -~t-0t25d12-f -t-oh t twenty five d one two-f -t-oh t two five d one two-f -t-zero t twenty five d twelve-f -~133-A -one hundred thirty three-A -~B2A23C -B two A twenty three C -~25d08A -twenty five d zero eight A -two five d zero eight A -~C24 -C twenty four -~It seemed to her that the jacket Oswald wore was darker than Commission Exhibit No. 162. -It seemed to her that the jacket Oswald wore was darker than Commission Exhibit number one hundred sixty two. -It seemed to her that the jacket Oswald wore was darker than Commission Exhibit number one hundred and sixty two. -It seemed to her that the jacket Oswald wore was darker than Commission Exhibit number one six two. -~"Father, let this cup pass." He prayed was heard. What cup was it that passed away from him? Sure not the death-cup, now filled to the brim! There was no quailing in the awful word; He still was king of kings, of lords the lord: He feared lest, in the suffering waste and grim, His faith might grow too faint and sickly dim." -"Father, let this cup pass." He prayed was heard. What cup was it that passed away from him? Sure not the death-cup, now filled to the brim! There was no quailing in the awful word; He still was king of kings, of lords the lord: He feared lest, in the suffering waste and grim, His faith might grow too faint and sickly dim." -~1970-2010 -nineteen seventy-twenty ten -one thousand nine hundred and seventy-two thousand ten -one thousand nine hundred and seventy-twenty ten -nineteen seventy to twenty ten -one thousand nine hundred seventy to two thousand ten -~W26s -W twenty six s -W two six s -~5-3-Ws -five-three-Ws -~401-ks -four oh one-ks -four hundred one-ks -~The box was 25 x 7 m. -The box was twenty five by seven meters. -~4567 -four thousand five hundred sixty seven -four five six seven -forty five sixty seven -four thousand five hundred and sixty seven -~This example number 15,000 can be a very long one, and can fail to produce valid normalization for such an easy number like 10,125 or dollar value $5349.01, and can fail to terminate, and can fail to terminate, and can fail to terminate, and can fail to terminate, and can fail to terminate, 452. -This example number fifteen thousand can be a very long one, and can fail to produce valid normalization for such an easy number like ten thousand one hundred twenty five or dollar value five thousand three hundred and forty nine dollars and one cent, and can fail to terminate, and can fail to terminate, and can fail to terminate, and can fail to terminate, and can fail to terminate, four fifty two. -~$1.01 -one dollar one cent -one dollar and one cent -~$.02 -two cents -~$17.31 -seventeen dollars and thirty one cents -~$17.312 -seventeen point three one two dollars -~25.] -two five.] -twenty five.] -~Francis I--test -Francis the first--test -Francis first--test -~13000 -one three zero zero zero -one three oh oh oh -thirteen thousand -~11/17/05 -november seventeenth zero five -november seventeenth oh five -the seventeenth of november oh five -~05 -oh five -zero five -~0063 -zero zero six three -oh oh sixty three -~ABC -ABC -A B C -~Test -Test -~2006-08-05 -august fifth two thousand six -the fifth of august two thousand six -~2-5 -two to five -two-five -~627 -six hundred twenty seven -six twenty seven -six two seven -~3/4 -the fourth of march -march fourth -three quarters -three fourths -~3/4/1998 -march four nineteen ninety eight -the fourth of march nineteen ninety eight -three/four/one nine nine eight -march fourth nineteen ninety eight -~1998-3-4 -nineteen ninety eight - march four -nineteen ninety eight - march fourth -nineteen ninety eight - the fourth of march -one nine nine eight-three-four -march fourth nineteen ninety eight -~0.004 -zero point zero zero four -oh point oh oh four -~[0.4] and [5 and 4] -[zero point four] and [five and four] -~Henry III. -Henry three. -Henry the third. -~for Tu (2/22) at 5 pm. -for Tuesday (the twenty second of february) at five PM. -for Tuesday (two divided by twenty two) at five PM. -for Tuesday (february twenty second) at five PM. -for Tuesday (two/two two) at five PM. -for Tuesday (two twenty seconds) at five PM. -for Tuesday (two/twenty two) at five PM. -for Tuesday (february twenty two) at five PM. -~12" -twelve inches -twelve" -~Sam I -Sam first -Sam the first -~2*8 -two times eight -two asterisk eight -~2-8 -two minus eight -two to eight -~2:8 -two to eight -~2/8 -two divided by eight -~[YI1] -[YI1] -[YI one] -~/ and $ -/ and dollar diff --git a/tests/nemo_text_processing/en/data_text_normalization/test_cases_ordinal.txt b/tests/nemo_text_processing/en/data_text_normalization/test_cases_ordinal.txt deleted file mode 100644 index 2e1b5ec7e0ef..000000000000 --- a/tests/nemo_text_processing/en/data_text_normalization/test_cases_ordinal.txt +++ /dev/null @@ -1,27 +0,0 @@ -0th~zeroth -11th~eleventh -23rd~twenty third -1000th~one thousandth -1,000th~one thousandth -1000000th~one millionth -9,000,000th~nine millionth -1st~first -1th~one th -2nd~second -2th~two th -3rd~third -3th~three th -4th~fourth -4rd~four rd -11th~eleventh -11st~eleven st -20th~twentieth -12th~twelfth -13th~thirteenth -12nd~twelve nd -13rd~thirteen rd -21st~twenty first -21th~twenty one th -121st~one hundred twenty first -111th~one hundred eleventh -111st~one hundred eleven st \ No newline at end of file diff --git a/tests/nemo_text_processing/en/data_text_normalization/test_cases_punctuation.txt b/tests/nemo_text_processing/en/data_text_normalization/test_cases_punctuation.txt deleted file mode 100644 index 5f5b249c15fe..000000000000 --- a/tests/nemo_text_processing/en/data_text_normalization/test_cases_punctuation.txt +++ /dev/null @@ -1,64 +0,0 @@ -a `quote` example~a `quote` example -a `25` example~a `twenty five` example -single quote.'~single quote.' -single quote. '4~single quote. ' four -company's results,' said he:~company's results,' said he: -test -~test - --5 test -~minus five test - -abbreviation USA.~abbreviation USA. -abbreviation U.S.A.~abbreviation USA -abbreviation. U.S.A..~abbreviation. USA.. -1-4=5~one minus four equals five -&hi;&hi;~and hi; and hi; -abbreviation USA,~abbreviation USA, -23rd july, 1998~the twenty third of july, nineteen ninety eight -April 29th’s meeting~april twenty ninth’s meeting -?,~?, -?,no~?,no -I've 20' and 14/ they're I'm 16c.~I've twenty ' and fourteen slash they're I'm sixteen c. -He's and I'm and Müller's and d'être~He's and I'm and Müller's and d'être -here (4 June 2014). and~here (the fourth of june twenty fourteen). and -``He's 50 and 2010``.~"He's fifty and twenty ten". -"He's 50 and 2010".~"He's fifty and twenty ten". -``test``.~"test". -This (12 species) and...~This (twelve species) and... -animals: ``cat``, ``dog``, ``lion``~animals: "cat", "dog", "lion" -animals: (1), (2), (3)~animals: (one), (two), (three) -animals: ``cat~animals: "cat -animals: ``4~animals: " four -non matching quotes?" "12text"~non matching quotes?" "twelve text" -12"~twelve inches -"12's and 3'sssssss"~"twelve's and three ' sssssss" -This is ABC 'second ~This is ABC 'second -``text 7`` text ``.~"text seven" text ". -"text 7" text ".~"text seven inches text". -"And now," he said, "He,~"And now," he said, "He, -a: "b", "c", "d", "e", "f" or "g".~a: "b", "c", "d", "e", "f" or "g". -a: "b" "c" "d" "e" "f" or "g".~a: "b" "c" "d" "e" "f" or "g". -a: ``b``, ``c``, ``d``, ``e``, ``f`` or ``g``.~a: "b", "c", "d", "e", "f" or "g". -"And now," he said, "He,~"And now," he said, "He, -80's~eighty's -test .b@gmail.com~test. b at gmail dot com -ourdailynews.abc/123-sm~ourdailynews. abc/one hundred twenty three-sm -123,000012~one hundred and twenty three, zero zero zero zero one two -9000,000th~nine thousand, zero zero zero th -16 July, 1943~the sixteenth of july, nineteen forty three -April 29th’s meeting~april twenty ninth’s meeting -august 23, 2002~august twenty third, two thousand two -11:30EST~eleven: thirty EST -11:30...~eleven thirty... -11:20AM,2:40PM and 10:10PM.~eleven twenty AM, two forty PM and ten ten PM. -dr. Evil~dr. Evil -1961–1965 and (2006–2012)~nineteen sixty one– nineteen sixty five and (two thousand six– twenty twelve) -114...48~one hundred and fourteen... forty eight -12kg.~twelve kilograms. -123?123~ one hundred and twenty three? one hundred and twenty three -2!~two! -1!hello~one! hello -1, ~one, -1!!!!~one!!!! -(1)Hello~(one) Hello -ÀÁÂÃ check §- and ƛ, also ɧ~ÀÁÂÃ check section - and ƛ, also ɧ -Hi it's 5pm,4A.M.?-34. Hi,no,yes,34! 12,again,4 and NO?17 and $.01,here & there--0.004kg~Hi it's five PM, four AM.? minus thirty four. Hi,no,yes, thirty four! twelve, again, four and NO? seventeen and one cent, here and there - minus zero point zero zero four kilograms -1°C.~one degree Celsius. -my email is myemail@gmail.com!~my email is myemail at gmail dot com! diff --git a/tests/nemo_text_processing/en/data_text_normalization/test_cases_punctuation_match_input.txt b/tests/nemo_text_processing/en/data_text_normalization/test_cases_punctuation_match_input.txt deleted file mode 100644 index 51d4aceb503f..000000000000 --- a/tests/nemo_text_processing/en/data_text_normalization/test_cases_punctuation_match_input.txt +++ /dev/null @@ -1,13 +0,0 @@ -&hi;&hi;~and hi;and hi; -23rd july,1998~the twenty third of july,nineteen ninety eight -I've 20' and 14/ they're I'm 16c.~I've twenty' and fourteen slash they're I'm sixteen c. -He's and I'm and Müller's and d'être~He's and I'm and Müller's and d'être -here (4 June 2014).and~here (the fourth of june twenty fourteen).and -This (12 species) and ...~This (twelve species) and ... -"12's and 3'sssssss"~"twelve's and three'sssssss" -a: ``b``,``c``, ``d``, ``e``, ``f`` or ``g``.~a: "b","c", "d", "e", "f" or "g". -1!hello~one!hello -1 ,~one , -(1)Hello~(one)Hello -ÀÁÂÃ check §- and ƛ, also ɧ~ÀÁÂÃ check section- and ƛ, also ɧ -Hi it's 5pm,4A.M.?-34. Hi,no,yes,34! 12,again,4 and NO?17 and $.01,here & there--0.004kg~Hi it's five PM,four AM.?minus thirty four. Hi,no,yes,thirty four! twelve,again,four and NO?seventeen and one cent,here and there - minus zero point zero zero four kilograms \ No newline at end of file diff --git a/tests/nemo_text_processing/en/data_text_normalization/test_cases_range.txt b/tests/nemo_text_processing/en/data_text_normalization/test_cases_range.txt deleted file mode 100644 index 3110928062f3..000000000000 --- a/tests/nemo_text_processing/en/data_text_normalization/test_cases_range.txt +++ /dev/null @@ -1,18 +0,0 @@ -1980-1986~nineteen eighty to nineteen eighty six -from 2-10~from two - ten -2-5lb~two to five pounds -2-5~two - five -2x8 m2~two by eight square meters -2x8~two x eight -2+3~two plus three -2+3 m2~two plus three m two -170 - 45190~one hundred and seventy - four five one nine zero -1980-1970 kg~one thousand nine hundred and eighty to one thousand nine hundred and seventy kilograms -5pm-7 pm~five PM to seven PM -4:00 am - 8:12 pm~four AM to eight twelve PM -978-0~nine hundred and seventy eight - zero -1960s-80s~nineteen sixties to eighties -1960s-1980s~nineteen sixties to nineteen eighties -1960-80~nineteen sixty to eighty -mid-1980s~mid nineteen eighties -mid-80s~mid eighties \ No newline at end of file diff --git a/tests/nemo_text_processing/en/data_text_normalization/test_cases_roman.txt b/tests/nemo_text_processing/en/data_text_normalization/test_cases_roman.txt deleted file mode 100644 index 2e3790361a65..000000000000 --- a/tests/nemo_text_processing/en/data_text_normalization/test_cases_roman.txt +++ /dev/null @@ -1,4 +0,0 @@ -Sam II~Sam second -Chapter IV~Chapter four -PART XL~PART forty -Sam and I~Sam and I diff --git a/tests/nemo_text_processing/en/data_text_normalization/test_cases_serial.txt b/tests/nemo_text_processing/en/data_text_normalization/test_cases_serial.txt deleted file mode 100644 index 4007bf5c0105..000000000000 --- a/tests/nemo_text_processing/en/data_text_normalization/test_cases_serial.txt +++ /dev/null @@ -1,31 +0,0 @@ -t-0t25d12-f~t-zero t twenty five d twelve-f -133-ABC~one hundred thirty three-ABC -B2A23C~B two A twenty three C -25d08A~twenty five d zero eight A -C24~C twenty four -W2s~W two s -1-413-te-b-1-5~one-four hundred thirteen-te-b-one-five -b-c-1-5-b-s-b~b-c-one-five-b-s-b -MIG-25/235212-asdg~MIG-twenty five/two three five two one two-asdg -1/f-4s~one/f-four s -1/f~one per F -4s~four S -7-eleven~seven-eleven -2x~two x -31/31/100~thirty one/thirty one/one hundred -1-8090~one - eight thousand and ninety -f++~f plus plus -dfsd#sfgsfd$~dfsd hash sfgsfd dollar -dfsd$$sfgsfd$~dfsd dollar dollar sfgsfd dollar -$12@12%~dollar twelve at twelve percent sign -@12~at twelve -12@~twelve at -@mypizza~at mypizza -#mytext#~hash mytext hash -test$~test dollar -covid-19~covid-nineteen -55-millimeters long~fifty five-millimeters long -a 4-kilogram bag~a four-kilogram bag -100-car~one hundred-car -123/261788/2021~one hundred twenty three/two six one seven eight eight/two thousand twenty one -2*8~two asterisk eight diff --git a/tests/nemo_text_processing/en/data_text_normalization/test_cases_special_text.txt b/tests/nemo_text_processing/en/data_text_normalization/test_cases_special_text.txt deleted file mode 100644 index c32983d90a45..000000000000 --- a/tests/nemo_text_processing/en/data_text_normalization/test_cases_special_text.txt +++ /dev/null @@ -1,10 +0,0 @@ -[HH AH0 L OW1]~[HH AH0 L OW1] -[nəkɫudɔf ˈfɫəʃt... ənd ˈfɛɫt - ˈɔɫˌweɪz ˈdɪd!]~[nəkɫudɔf ˈfɫəʃt... ənd ˈfɛɫt - ˈɔɫˌweɪz ˈdɪd!] -[poʊstˌmɑɹk]~[poʊstˌmɑɹk] -~ -go home~go home -gohome~gohome -~ -~ -123 123~ one hundred and twenty three one hundred and twenty three - haleluja ~ haleluja \ No newline at end of file diff --git a/tests/nemo_text_processing/en/data_text_normalization/test_cases_telephone.txt b/tests/nemo_text_processing/en/data_text_normalization/test_cases_telephone.txt deleted file mode 100644 index fd51e9f5afbc..000000000000 --- a/tests/nemo_text_processing/en/data_text_normalization/test_cases_telephone.txt +++ /dev/null @@ -1,20 +0,0 @@ -+1 123-123-5678~plus one, one two three, one two three, five six seven eight -123-123-5678~one two three, one two three, five six seven eight -+1-123-123-5678~plus one, one two three, one two three, five six seven eight -+1 (123)-123-5678~plus one, one two three, one two three, five six seven eight -+1(123) 123-5678~plus one, one two three, one two three, five six seven eight -(123)-123-5678~one two three, one two three, five six seven eight -(123) 123-5678~one two three, one two three, five six seven eight -123-123-5678-1111~one two three, one two three, five six seven eight, one one one one -123-123-5678-1~one two three, one two three, five six seven eight, one -1-800-GO-U-HAUL~one, eight hundred, GO U HAUL -1-800-GO-2-HAUL~one, eight hundred, GO two, HAUL -987.987.0.40~nine eight seven dot nine eight seven dot zero dot four zero -call me at +1 123-123-5678~call me at plus one, one two three, one two three, five six seven eight -call me at 123-123-5678~call me at one two three, one two three, five six seven eight -123.123.0.40~one two three dot one two three dot zero dot four zero -IP address is 123.123.0.40~IP address is one two three dot one two three dot zero dot four zero -111-11-1111~one one one, one one, one one one one -my ssn is 111-11-1111~my SSN is one one one, one one, one one one one -555.555.5555~five five five, five five five, five five five five -(555)555-5555~five five five, five five five, five five five five diff --git a/tests/nemo_text_processing/en/data_text_normalization/test_cases_time.txt b/tests/nemo_text_processing/en/data_text_normalization/test_cases_time.txt deleted file mode 100644 index e7f1d53e404c..000000000000 --- a/tests/nemo_text_processing/en/data_text_normalization/test_cases_time.txt +++ /dev/null @@ -1,19 +0,0 @@ -01:00~one o'clock -01:00 am~one AM -01:00 a.m.~one AM -01:00 a.m~one AM -1 a.m.~one AM -23:00~twenty three o'clock -1:00 a.m.~one AM -1:00a.m.~one AM -1.59 p.m.~one fifty nine PM -01:59 p.m.~one fifty nine PM -1:59 p.m.~one fifty nine PM -1:59 p.m. est~one fifty nine PM EST -1:59 p.m.est~one fifty nine PM EST -1:59 p.m. e.s.t~one fifty nine PM EST -10:00:00 p.m. e.s.t~ten hours zero minutes and zero seconds PM EST -1:01:01~one hour one minute and one second -14:10:30~fourteen hours ten minutes and thirty seconds -2pm-5pm~two PM to five PM -5pm~five PM diff --git a/tests/nemo_text_processing/en/data_text_normalization/test_cases_whitelist.txt b/tests/nemo_text_processing/en/data_text_normalization/test_cases_whitelist.txt deleted file mode 100644 index 7fafb9d77152..000000000000 --- a/tests/nemo_text_processing/en/data_text_normalization/test_cases_whitelist.txt +++ /dev/null @@ -1,6 +0,0 @@ -Dr. Evil~doctor Evil -Mrs. Norris~misses Norris -DNA is~DNA is -C. S. Lewis~CS Lewis -tv~TV -and/or~and/or diff --git a/tests/nemo_text_processing/en/data_text_normalization/test_cases_word.txt b/tests/nemo_text_processing/en/data_text_normalization/test_cases_word.txt deleted file mode 100644 index 3fb24ebcc202..000000000000 --- a/tests/nemo_text_processing/en/data_text_normalization/test_cases_word.txt +++ /dev/null @@ -1,38 +0,0 @@ -~ - ~ - 1~one - no~no -May I come in?~May I come in? -Yes you may.~Yes you may. -On the mar.~On the mar. -es3~es three -x ~x -X!~X! -—~— -aabach~aabach -aabenraa~aabenraa -aabye~aabye -aaccessed~aaccessed -aach~aach -aachen's~aachen's -aadri~aadri -aafia~aafia -aagaard~aagaard -aagadu~aagadu -aagard~aagard -aagathadi~aagathadi -aaghart's~aaghart's -aalem~aalem -a'ali~a'ali -aaliyan's~aaliyan's -mother-in-law~mother-in-law -there -0.4kg~there minus zero point four kilograms -there- -0.4kg~there- minus zero point four kilograms -$ and 5% or %~dollar and five percent or percent sign - 1~one -1~one -!1~! one -mar~mar -/$€₩£BB¥#%AA and $€₩£¥#%~slash dollar euro won pound BB yen hash percent sign AA and dollar euro won pound yen hash percent sign -love him while we may,~love him while we may, -mar~mar diff --git a/tests/nemo_text_processing/en/test_address.py b/tests/nemo_text_processing/en/test_address.py deleted file mode 100644 index d2a246faa523..000000000000 --- a/tests/nemo_text_processing/en/test_address.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestAddress: - normalizer_en = ( - Normalizer(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False, post_process=True) - if PYNINI_AVAILABLE - else None - ) - - normalizer_with_audio_en = ( - NormalizerWithAudio(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - # address is tagged by the measure class - @parameterized.expand(parse_test_case_file('en/data_text_normalization/test_cases_address.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer_en.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio_en: - pred_non_deterministic = self.normalizer_with_audio_en.normalize( - test_input, n_tagged=30, punct_post_process=False, - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/en/test_cardinal.py b/tests/nemo_text_processing/en/test_cardinal.py deleted file mode 100644 index 4b5507869546..000000000000 --- a/tests/nemo_text_processing/en/test_cardinal.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestCardinal: - inverse_normalizer_en = ( - (InverseNormalizer(lang='en', cache_dir=CACHE_DIR, overwrite_cache=False)) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('en/data_inverse_text_normalization/test_cases_cardinal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer_en.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer_en = ( - Normalizer(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False, post_process=True) - if PYNINI_AVAILABLE - else None - ) - normalizer_with_audio_en = ( - NormalizerWithAudio(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(parse_test_case_file('en/data_text_normalization/test_cases_cardinal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer_en.normalize(test_input, verbose=False, punct_post_process=False) - assert pred == expected, f"input: {test_input}" - - if self.normalizer_with_audio_en: - pred_non_deterministic = self.normalizer_with_audio_en.normalize( - test_input, n_tagged=30, punct_post_process=False, - ) - assert expected in pred_non_deterministic, f"input: {test_input}" diff --git a/tests/nemo_text_processing/en/test_date.py b/tests/nemo_text_processing/en/test_date.py deleted file mode 100644 index 01bfa50158f1..000000000000 --- a/tests/nemo_text_processing/en/test_date.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestDate: - inverse_normalizer_en = ( - InverseNormalizer(lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('en/data_inverse_text_normalization/test_cases_date.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer_en.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer_en = ( - Normalizer(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False, post_process=True) - if PYNINI_AVAILABLE - else None - ) - normalizer_with_audio_en = ( - NormalizerWithAudio(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(parse_test_case_file('en/data_text_normalization/test_cases_date.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/install_pynini.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm_uncased(self, test_input, expected): - pred = self.normalizer_en.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio_en: - pred_non_deterministic = self.normalizer_with_audio_en.normalize( - test_input, punct_post_process=False, n_tagged=100 - ) - assert expected in pred_non_deterministic, f"INPUT: {test_input}" - - normalizer_uppercased = ( - Normalizer(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - cases_uppercased = {"Aug. 8": "august eighth", "8 Aug.": "the eighth of august", "aug. 8": "august eighth"} - - @parameterized.expand(cases_uppercased.items()) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/install_pynini.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm_cased(self, test_input, expected): - pred = self.normalizer_uppercased.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio_en: - pred_non_deterministic = self.normalizer_with_audio_en.normalize( - test_input, punct_post_process=False, n_tagged=30 - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/en/test_decimal.py b/tests/nemo_text_processing/en/test_decimal.py deleted file mode 100644 index 51a180251ba5..000000000000 --- a/tests/nemo_text_processing/en/test_decimal.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestDecimal: - inverse_normalizer_en = ( - InverseNormalizer(lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('en/data_inverse_text_normalization/test_cases_decimal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer_en.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer_en = ( - Normalizer(input_case="cased", cache_dir=CACHE_DIR, overwrite_cache=False, post_process=True) - if PYNINI_AVAILABLE - else None - ) - normalizer_with_audio_en = ( - NormalizerWithAudio(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(parse_test_case_file('en/data_text_normalization/test_cases_decimal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer_en.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio_en: - pred_non_deterministic = self.normalizer_with_audio_en.normalize( - test_input, n_tagged=30, punct_post_process=False, - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/en/test_electronic.py b/tests/nemo_text_processing/en/test_electronic.py deleted file mode 100644 index 06edfe15370e..000000000000 --- a/tests/nemo_text_processing/en/test_electronic.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestElectronic: - inverse_normalizer_en = ( - InverseNormalizer(lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('en/data_inverse_text_normalization/test_cases_electronic.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer_en.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer_en = ( - Normalizer(input_case="cased", cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - normalizer_with_audio_en = ( - NormalizerWithAudio(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(parse_test_case_file('en/data_text_normalization/test_cases_electronic.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer_en.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio_en: - pred_non_deterministic = self.normalizer_with_audio_en.normalize( - test_input, n_tagged=100, punct_post_process=False, - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/en/test_fraction.py b/tests/nemo_text_processing/en/test_fraction.py deleted file mode 100644 index 647bad02a4ac..000000000000 --- a/tests/nemo_text_processing/en/test_fraction.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestFraction: - normalizer_en = ( - Normalizer(input_case="cased", cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - normalizer_with_audio_en = ( - NormalizerWithAudio(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(parse_test_case_file('en/data_text_normalization/test_cases_fraction.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer_en.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio_en: - pred_non_deterministic = self.normalizer_with_audio_en.normalize( - test_input, n_tagged=30, punct_post_process=False, - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/en/test_math.py b/tests/nemo_text_processing/en/test_math.py deleted file mode 100644 index 25a608bfd840..000000000000 --- a/tests/nemo_text_processing/en/test_math.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestMath: - normalizer_en = ( - Normalizer(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - normalizer_with_audio_en = ( - NormalizerWithAudio(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - # math is tagged by the measure class - @parameterized.expand(parse_test_case_file('en/data_text_normalization/test_cases_math.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer_en.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio_en: - pred_non_deterministic = self.normalizer_with_audio_en.normalize( - test_input, n_tagged=30, punct_post_process=False, - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/en/test_measure.py b/tests/nemo_text_processing/en/test_measure.py deleted file mode 100644 index 6261f2e5da66..000000000000 --- a/tests/nemo_text_processing/en/test_measure.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestMeasure: - inverse_normalizer_en = ( - InverseNormalizer(lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('en/data_inverse_text_normalization/test_cases_measure.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer_en.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer_en = ( - Normalizer(input_case="cased", cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - normalizer_with_audio_en = ( - NormalizerWithAudio(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(parse_test_case_file('en/data_text_normalization/test_cases_measure.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer_en.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio_en: - pred_non_deterministic = self.normalizer_with_audio_en.normalize( - test_input, n_tagged=30, punct_post_process=False, - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/en/test_money.py b/tests/nemo_text_processing/en/test_money.py deleted file mode 100644 index f9b37d7e6dda..000000000000 --- a/tests/nemo_text_processing/en/test_money.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestMoney: - inverse_normalizer_en = ( - InverseNormalizer(lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('en/data_inverse_text_normalization/test_cases_money.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer_en.inverse_normalize(test_input, verbose=False) - assert pred == expected, f"input: {test_input}" - - normalizer_en = ( - Normalizer(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - normalizer_with_audio_en = ( - NormalizerWithAudio(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(parse_test_case_file('en/data_text_normalization/test_cases_money.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer_en.normalize(test_input, verbose=False) - assert pred == expected, f"input: {test_input}" - - if self.normalizer_with_audio_en: - pred_non_deterministic = self.normalizer_with_audio_en.normalize( - test_input, n_tagged=30, punct_post_process=False, - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/en/test_normalization_with_audio.py b/tests/nemo_text_processing/en/test_normalization_with_audio.py deleted file mode 100644 index 37a22b7b1070..000000000000 --- a/tests/nemo_text_processing/en/test_normalization_with_audio.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, get_test_cases_multiple - -try: - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestNormalizeWithAudio: - normalizer_with_audio_en = ( - NormalizerWithAudio(input_case='cased', lang='en', lm=False, cache_dir=CACHE_DIR, overwrite_cache=False) - if CACHE_DIR and PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(get_test_cases_multiple('en/data_text_normalization/test_cases_normalize_with_audio.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - if self.normalizer_with_audio_en: - pred = self.normalizer_with_audio_en.normalize(test_input, n_tagged=30, punct_post_process=True) - assert len(set(pred).intersection(set(expected))) == len( - expected - ), f'missing: {set(expected).difference(set(pred))}' diff --git a/tests/nemo_text_processing/en/test_ordinal.py b/tests/nemo_text_processing/en/test_ordinal.py deleted file mode 100644 index 641ff6f045e3..000000000000 --- a/tests/nemo_text_processing/en/test_ordinal.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestOrdinal: - inverse_normalizer_en = ( - InverseNormalizer(lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('en/data_inverse_text_normalization/test_cases_ordinal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer_en.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer_en = ( - Normalizer(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - normalizer_with_audio_en = ( - NormalizerWithAudio(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(parse_test_case_file('en/data_text_normalization/test_cases_ordinal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer_en.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio_en: - pred_non_deterministic = self.normalizer_with_audio_en.normalize( - test_input, n_tagged=30, punct_post_process=False, - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/en/test_punctuation.py b/tests/nemo_text_processing/en/test_punctuation.py deleted file mode 100644 index 8bda062400df..000000000000 --- a/tests/nemo_text_processing/en/test_punctuation.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.text_normalization.normalize import Normalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestPunctuation: - normalizer_en = ( - Normalizer(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False, post_process=True,) - if PYNINI_AVAILABLE - else None - ) - - # address is tagged by the measure class - @parameterized.expand(parse_test_case_file('en/data_text_normalization/test_cases_punctuation.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer_en.normalize(test_input, verbose=True, punct_post_process=False) - assert pred == expected, f"input: {test_input} != {expected}" - - @parameterized.expand(parse_test_case_file('en/data_text_normalization/test_cases_punctuation_match_input.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm_python_punct_post_process(self, test_input, expected): - pred = self.normalizer_en.normalize(test_input, verbose=True, punct_post_process=True) - assert pred == expected, f"input: {test_input} != {expected}" diff --git a/tests/nemo_text_processing/en/test_range.py b/tests/nemo_text_processing/en/test_range.py deleted file mode 100644 index 97213b37f1ac..000000000000 --- a/tests/nemo_text_processing/en/test_range.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestRange: - normalizer_en = ( - Normalizer(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - normalizer_with_audio_en = ( - NormalizerWithAudio(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - # address is tagged by the measure class - @parameterized.expand(parse_test_case_file('en/data_text_normalization/test_cases_range.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer_en.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio_en: - pred_non_deterministic = self.normalizer_with_audio_en.normalize( - test_input, n_tagged=30, punct_post_process=False, - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/en/test_roman.py b/tests/nemo_text_processing/en/test_roman.py deleted file mode 100644 index ae6ce0842bcd..000000000000 --- a/tests/nemo_text_processing/en/test_roman.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestRoman: - normalizer_en = ( - Normalizer(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - normalizer_with_audio_en = ( - NormalizerWithAudio(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - # address is tagged by the measure class - @parameterized.expand(parse_test_case_file('en/data_text_normalization/test_cases_roman.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer_en.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio_en: - pred_non_deterministic = self.normalizer_with_audio_en.normalize( - test_input, n_tagged=30, punct_post_process=False, - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/en/test_serial.py b/tests/nemo_text_processing/en/test_serial.py deleted file mode 100644 index 4f25cbbaac13..000000000000 --- a/tests/nemo_text_processing/en/test_serial.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestSerial: - normalizer_en = ( - Normalizer(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - normalizer_with_audio_en = ( - NormalizerWithAudio(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(parse_test_case_file('en/data_text_normalization/test_cases_serial.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer_en.normalize(test_input, verbose=False, punct_post_process=False) - assert pred == expected, f"input: {test_input}" - - if self.normalizer_with_audio_en: - pred_non_deterministic = self.normalizer_with_audio_en.normalize( - test_input, n_tagged=30, punct_post_process=False, - ) - assert expected in pred_non_deterministic, f"input: {test_input}" diff --git a/tests/nemo_text_processing/en/test_sparrowhawk_inverse_text_normalization.sh b/tests/nemo_text_processing/en/test_sparrowhawk_inverse_text_normalization.sh deleted file mode 100644 index 2633670c0637..000000000000 --- a/tests/nemo_text_processing/en/test_sparrowhawk_inverse_text_normalization.sh +++ /dev/null @@ -1,79 +0,0 @@ -#! /bin/sh - -PROJECT_DIR=/workspace/tests - -runtest () { - input=$1 - cd /workspace/sparrowhawk/documentation/grammars - - # read test file - while read testcase; do - IFS='~' read spoken written <<< $testcase - denorm_pred=$(echo $spoken | normalizer_main --config=sparrowhawk_configuration.ascii_proto 2>&1 | tail -n 1) - - # trim white space - written="$(echo -e "${written}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - denorm_pred="$(echo -e "${denorm_pred}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - - # input expected actual - assertEquals "$spoken" "$written" "$denorm_pred" - done < "$input" -} - -testITNCardinal() { - input=$PROJECT_DIR/en/data_inverse_text_normalization/test_cases_cardinal.txt - runtest $input -} - -testITNDate() { - input=$PROJECT_DIR/en/data_inverse_text_normalization/test_cases_date.txt - runtest $input -} - -testITNDecimal() { - input=$PROJECT_DIR/en/data_inverse_text_normalization/test_cases_decimal.txt - runtest $input -} - -testITNElectronic() { - input=$PROJECT_DIR/en/data_inverse_text_normalization/test_cases_electronic.txt - runtest $input -} - -testITNOrdinal() { - input=$PROJECT_DIR/en/data_inverse_text_normalization/test_cases_ordinal.txt - runtest $input -} - -testITNTime() { - input=$PROJECT_DIR/en/data_inverse_text_normalization/test_cases_time.txt - runtest $input -} - -testITNMeasure() { - input=$PROJECT_DIR/en/data_inverse_text_normalization/test_cases_measure.txt - runtest $input -} - -testITNMoney() { - input=$PROJECT_DIR/en/data_inverse_text_normalization/test_cases_money.txt - runtest $input -} - -testITNWhitelist() { - input=$PROJECT_DIR/en/data_inverse_text_normalization/test_cases_whitelist.txt - runtest $input -} - -testITNTelephone() { - input=$PROJECT_DIR/en/data_inverse_text_normalization/test_cases_telephone.txt - runtest $input -} - -testITNWord() { - input=$PROJECT_DIR/en/data_inverse_text_normalization/test_cases_word.txt - runtest $input -} - -# Load shUnit2 -. $PROJECT_DIR/../shunit2/shunit2 diff --git a/tests/nemo_text_processing/en/test_sparrowhawk_normalization.sh b/tests/nemo_text_processing/en/test_sparrowhawk_normalization.sh deleted file mode 100644 index 7baa7c198ae9..000000000000 --- a/tests/nemo_text_processing/en/test_sparrowhawk_normalization.sh +++ /dev/null @@ -1,115 +0,0 @@ -#! /bin/sh - -PROJECT_DIR=/workspace/tests - -runtest () { - input=$1 - cd /workspace/sparrowhawk/documentation/grammars - - # read test file - while read testcase; do - IFS='~' read written spoken <<< $testcase - # replace non breaking space with breaking space - denorm_pred=$(echo $written | normalizer_main --config=sparrowhawk_configuration.ascii_proto 2>&1 | tail -n 1 | sed 's/\xC2\xA0/ /g') - - # trim white space - spoken="$(echo -e "${spoken}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - denorm_pred="$(echo -e "${denorm_pred}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - - # input expected actual - assertEquals "$written" "$spoken" "$denorm_pred" - done < "$input" -} - -testTNSpecialText() { - input=$PROJECT_DIR/en/data_text_normalization/test_cases_special_text.txt - runtest $input -} - -testTNCardinal() { - input=$PROJECT_DIR/en/data_text_normalization/test_cases_cardinal.txt - runtest $input -} - -testTNDate() { - input=$PROJECT_DIR/en/data_text_normalization/test_cases_date.txt - runtest $input -} - -testTNDecimal() { - input=$PROJECT_DIR/en/data_text_normalization/test_cases_decimal.txt - runtest $input -} - -testTNRange() { - input=$PROJECT_DIR/en/data_text_normalization/test_cases_range.txt - runtest $input -} - -testTNSerial() { - input=$PROJECT_DIR/en/data_text_normalization/test_cases_serial.txt - runtest $input -} - -#testTNRoman() { -# input=$PROJECT_DIR/en/data_text_normalization/test_cases_roman.txt -# runtest $input -#} - -testTNElectronic() { - input=$PROJECT_DIR/en/data_text_normalization/test_cases_electronic.txt - runtest $input -} - -testTNFraction() { - input=$PROJECT_DIR/en/data_text_normalization/test_cases_fraction.txt - runtest $input -} - -testTNMoney() { - input=$PROJECT_DIR/en/data_text_normalization/test_cases_money.txt - runtest $input -} - -testTNOrdinal() { - input=$PROJECT_DIR/en/data_text_normalization/test_cases_ordinal.txt - runtest $input -} - -testTNTelephone() { - input=$PROJECT_DIR/en/data_text_normalization/test_cases_telephone.txt - runtest $input -} - -testTNTime() { - input=$PROJECT_DIR/en/data_text_normalization/test_cases_time.txt - runtest $input -} - -testTNMeasure() { - input=$PROJECT_DIR/en/data_text_normalization/test_cases_measure.txt - runtest $input -} - -testTNWhitelist() { - input=$PROJECT_DIR/en/data_text_normalization/test_cases_whitelist.txt - runtest $input -} - -testTNWord() { - input=$PROJECT_DIR/en/data_text_normalization/test_cases_word.txt - runtest $input -} - -testTNAddress() { - input=$PROJECT_DIR/en/data_text_normalization/test_cases_address.txt - runtest $input -} - -testTNMath() { - input=$PROJECT_DIR/en/data_text_normalization/test_cases_math.txt - runtest $input -} - -# Load shUnit2 -. $PROJECT_DIR/../shunit2/shunit2 diff --git a/tests/nemo_text_processing/en/test_special_text.py b/tests/nemo_text_processing/en/test_special_text.py deleted file mode 100644 index 4a73bbcbead0..000000000000 --- a/tests/nemo_text_processing/en/test_special_text.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestSpecialText: - - normalizer_en = ( - Normalizer(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - - normalizer_with_audio_en = ( - NormalizerWithAudio(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(parse_test_case_file('en/data_text_normalization/test_cases_special_text.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer_en.normalize(test_input, verbose=False) - assert pred == expected - - # if self.normalizer_with_audio_en: - # pred_non_deterministic = self.normalizer_with_audio_en.normalize( - # test_input, n_tagged=30, punct_post_process=False, - # ) - # assert expected in pred_non_deterministic, f"input: {test_input}" diff --git a/tests/nemo_text_processing/en/test_telephone.py b/tests/nemo_text_processing/en/test_telephone.py deleted file mode 100644 index c0db058fc9fb..000000000000 --- a/tests/nemo_text_processing/en/test_telephone.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestTelephone: - inverse_normalizer_en = ( - InverseNormalizer(lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('en/data_inverse_text_normalization/test_cases_telephone.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer_en.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer_en = ( - Normalizer(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - normalizer_with_audio_en = ( - NormalizerWithAudio(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(parse_test_case_file('en/data_text_normalization/test_cases_telephone.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer_en.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio_en: - pred_non_deterministic = self.normalizer_with_audio_en.normalize( - test_input, n_tagged=10, punct_post_process=False - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/en/test_text_split.py b/tests/nemo_text_processing/en/test_text_split.py deleted file mode 100644 index 4241914ea5fa..000000000000 --- a/tests/nemo_text_processing/en/test_text_split.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest - -from ..utils import CACHE_DIR - -try: - from nemo_text_processing.text_normalization.normalize import Normalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestTextSentenceSplit: - normalizer_en = ( - Normalizer(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False, post_process=True) - if PYNINI_AVAILABLE - else None - ) - - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - def test_text_sentence_split(self): - text = "This happened in 1918 when Mrs. and Mr. Smith paid $111.12 in U.S.A. at 9 a.m. on Dec. 1. 2020. And Jan. 17th. This is an example. He paid $123 for this desk. 123rd, St. Patrick." - gt_sentences = [ - 'This happened in 1918 when Mrs. and Mr. Smith paid $111.12 in U.S.A. at 9 a.m. on Dec. 1. 2020.', - 'And Jan. 17th.', - 'This is an example.', - 'He paid $123 for this desk.', - '123rd, St. Patrick.', - ] - sentences = self.normalizer_en.split_text_into_sentences(text) - assert gt_sentences == sentences diff --git a/tests/nemo_text_processing/en/test_time.py b/tests/nemo_text_processing/en/test_time.py deleted file mode 100644 index aff02d57a786..000000000000 --- a/tests/nemo_text_processing/en/test_time.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestTime: - inverse_normalizer_en = ( - InverseNormalizer(lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('en/data_inverse_text_normalization/test_cases_time.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer_en.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer_en = ( - Normalizer(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - normalizer_with_audio_en = ( - NormalizerWithAudio(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(parse_test_case_file('en/data_text_normalization/test_cases_time.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer_en.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio_en: - pred_non_deterministic = self.normalizer_with_audio_en.normalize( - test_input, n_tagged=10, punct_post_process=False - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/en/test_whitelist.py b/tests/nemo_text_processing/en/test_whitelist.py deleted file mode 100644 index 7364b90b5387..000000000000 --- a/tests/nemo_text_processing/en/test_whitelist.py +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestWhitelist: - inverse_normalizer_en = ( - (InverseNormalizer(lang='en', cache_dir=CACHE_DIR, overwrite_cache=False)) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('en/data_inverse_text_normalization/test_cases_whitelist.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer_en.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer_en = ( - Normalizer(input_case='cased', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - normalizer_with_audio_en = ( - NormalizerWithAudio(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(parse_test_case_file('en/data_text_normalization/test_cases_whitelist.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer_en.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio_en: - pred_non_deterministic = self.normalizer_with_audio_en.normalize( - test_input, n_tagged=10, punct_post_process=False - ) - assert expected in pred_non_deterministic - - normalizer_uppercased = Normalizer(input_case='cased', lang='en') if PYNINI_AVAILABLE else None - cases_uppercased = {"Dr. Evil": "doctor Evil", "dr. Evil": "dr. Evil", "no. 4": "no. four"} - - @parameterized.expand(cases_uppercased.items()) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm_cased(self, test_input, expected): - pred = self.normalizer_uppercased.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio_en: - pred_non_deterministic = self.normalizer_with_audio_en.normalize( - test_input, n_tagged=10, punct_post_process=False - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/en/test_word.py b/tests/nemo_text_processing/en/test_word.py deleted file mode 100644 index b5e798d0c4bf..000000000000 --- a/tests/nemo_text_processing/en/test_word.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestWord: - inverse_normalizer_en = ( - InverseNormalizer(lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('en/data_inverse_text_normalization/test_cases_word.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer_en.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer_en = ( - Normalizer(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False, post_process=True) - if PYNINI_AVAILABLE - else None - ) - normalizer_with_audio_en = ( - NormalizerWithAudio(input_case='cased', lang='en', cache_dir=CACHE_DIR, overwrite_cache=False) - if RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(parse_test_case_file('en/data_text_normalization/test_cases_word.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer_en.normalize(test_input, verbose=False) - assert pred == expected, f"input: {test_input} != {expected}" - - if self.normalizer_with_audio_en: - pred_non_deterministic = self.normalizer_with_audio_en.normalize( - test_input, n_tagged=3, punct_post_process=False - ) - assert expected in pred_non_deterministic, f"input: {test_input}" diff --git a/tests/nemo_text_processing/es/__init__.py b/tests/nemo_text_processing/es/__init__.py deleted file mode 100644 index 7d200df57afe..000000000000 --- a/tests/nemo_text_processing/es/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_cardinal.txt b/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_cardinal.txt deleted file mode 100644 index 7cd24635c5eb..000000000000 --- a/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_cardinal.txt +++ /dev/null @@ -1,50 +0,0 @@ -(non-ideal case) entre dieciséis mil y dieciocho mil~(non-ideal case) entre 16018 1000 -doscientos cincuenta y uno~251 -novecientos noventa y nueve millones novecientos noventa y nueve mil novecientos noventa y nueve~999999999 -cero~cero -uno~uno -una~una -dos~dos -nueve~nueve -diez~10 -, uno~, uno -, diez~, 10 -menos veintitrés~-23 -cien~100 -ciento uno~101 -ciento un~101 -ciento una~101 -mil y uno~1001 -mil uno~1001 -nueve billones setecientos ochenta y nueve mil trescientos ochenta y dos millones quinientos treinta y seis mil ciento treinta~9789382536130 -doscientos cincuenta y cuatro~254 -ciento cuarenta y siete mil cuatrocientos cincuenta y uno~147451 -un millón ciento cincuenta y seis mil ciento setenta y tres~1156173 -mil quinientos noventa y tres millones setenta y dos mil novecientos sesenta y uno~1593072961 -noventa y siete mil ochocientos ocho billones doscientos sesenta y cuatro mil setecientos setenta y dos millones setecientos noventa y dos mil cinco~97808264772792005 -diecisiete mil ochocientos cincuenta y cinco trillones treinta y seis mil seiscientos cincuenta y siete billones siete mil quinientos noventa y seis millones ciento diez mil novecientos cuarenta y nueve~17855036657007596110949 -diez mil diez billones diez millones cien mil diez~10010000010100010 -menos veinticinco mil treinta y siete~-25037 -mil doscientos sesenta y cuatro billones trescientos un mil novecientos treinta y ocho millones ciento cuatro~1264301938000104 -menos sesenta~-60 -cuarenta y seis mil seiscientos sesenta y cuatro~46664 -sesenta~60 -dos millones tres~2000003 -mil trece~1013 -mil cien~1100 -mil veintiséis~1026 -mil ciento veintiséis~1126 -dieciocho millones cuatrocientos cincuenta mil novecientos noventa~18450990 -dieciocho millones novecientos cuarenta mil setecientos veintidós~18940722 -dieciocho millones seiscientos noventa mil novecientos dieciséis~18690916 -dieciocho mil ochocientos ochenta~18880 -un millardo uno~1000000001 -mil millones uno~1000000001 -mil millones ciento uno~1000000101 -mil millones mil ciento uno~1000001101 -mil millones diez mil ciento uno~1000010101 -mil un millón diez mil ciento uno~1001010101 -dos millardos cincuenta y dos~2000000052 -muchas millones~muchas millones -mil billones uno~1000000000000001 -mil trillones uno~1000000000000000000001 \ No newline at end of file diff --git a/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_date.txt b/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_date.txt deleted file mode 100644 index f3de84bf416b..000000000000 --- a/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_date.txt +++ /dev/null @@ -1,6 +0,0 @@ -primero de enero~1 de enero -uno de enero~1 de enero -el uno de diciembre~el 1 de diciembre -el primero de diciembre~el 1 de diciembre -domingo veintiséis de octubre~domingo 26 de octubre -treinta y uno de diciembre de mil novecientos noventa y dos~31 de diciembre de 1992 \ No newline at end of file diff --git a/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_decimal.txt b/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_decimal.txt deleted file mode 100644 index 3aa1005275a2..000000000000 --- a/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_decimal.txt +++ /dev/null @@ -1,29 +0,0 @@ -uno coma dos seis~1,26 -menos uno coma dos seis~-1,26 -uno coma veintiséis~1,26 -cero coma dos seis~0,26 -cero coma veintiséis~0,26 -tres coma ciento cuarenta y uno~3,141 -tres coma cero ciento cuarenta y uno~3,0141 -tres coma ciento cuarenta y uno cincuenta y nueve~3,14159 -tres coma catorce ciento cincuenta y nueve~3,14159 -tres coma catorce quince noventa y dos sesenta cinco treinta y cinco~3,1415926535 -tres coma catorce quince cero noventa y dos sesenta cinco treinta y cinco~3,14150926535 -tres coma catorce quince cero novecientos veintiséis cero quinientos treinta y cinco~3,141509260535 -cuatrocientos millones~400 millones -uno punto treinta y tres~1.33 -uno punto treinta y tres millones~1.33 millones -cero coma seis millones~0,6 millones -mil ochocientos veinticuatro millón~1824 millón -mil ochocientos veinticuatro millones~1824 millones -punto dos seis~.26 -un millón~1 millón -dos millones~2 millones -un millardo~1 millardo -dos millardos~2 millardos -un billón~1 billón -dos billones~2 billones -un trillón~1 trillón -dos trillones~2 trillones -un cuatrillón~1 cuatrillón -dos cuatrillones~2 cuatrillones diff --git a/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_electronic.txt b/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_electronic.txt deleted file mode 100644 index fd9387f63d78..000000000000 --- a/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_electronic.txt +++ /dev/null @@ -1,13 +0,0 @@ -a punto b c arroba g mail punto com~a.bc@gmail.com -c d f arroba a b c punto e d u~cdf@abc.edu -a b c arroba g mail punto a b c~abc@gmail.abc -a b c arroba a b c punto com~abc@abc.com -a s d f uno dos tres arroba a b c punto com~asdf123@abc.com -a uno b dos arroba a b c punto com~a1b2@abc.com -a b tres punto s d d punto tres arroba g mail punto com~ab3.sdd.3@gmail.com -hache te te pe ese dos puntos barra barra doble ve doble ve doble ve punto n vidia punto com~https://www.nvidia.com -doble ve doble ve doble ve punto n vidia punto com~www.nvidia.com -doble ve doble ve doble ve punto nvidia punto com~www.nvidia.com -w w w punto nvidia punto com~www.nvidia.com -doble ve doble ve doble ve punto a b c punto es barra e f g~www.abc.es/efg -doble ve doble ve doble ve punto a b c punto es~www.abc.es \ No newline at end of file diff --git a/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_measure.txt b/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_measure.txt deleted file mode 100644 index 582a0d69c695..000000000000 --- a/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_measure.txt +++ /dev/null @@ -1,13 +0,0 @@ -doscientos metros~200 m -tres horas~3 h -una hora~1 h -doscientos cuarenta y cinco millas por hora~245 mph -dos kilos~2 kg -sesenta coma dos cuatro cero cero kilogramos~60,2400 kg -menos sesenta coma veinticuatro cero cero kilogramos~-60,2400 kg -ocho coma cinco dos por ciento~8,52 % -menos ocho coma cinco dos por ciento~-8,52 % -uno porciento~1 % -tres centímetros~3 cm -cuatro segundos~4 s -cinco litros~5 l \ No newline at end of file diff --git a/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_money.txt b/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_money.txt deleted file mode 100644 index c9c8892257cd..000000000000 --- a/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_money.txt +++ /dev/null @@ -1,15 +0,0 @@ -doce dólares y cinco centavos~$12,05 -doce dólares y cinco céntimos~$12,05 -setenta y cinco dólares sesenta y tres~$75,63 -setenta y cinco dólares y sesenta y tres centavos~$75,63 -setenta y cinco dólares con sesenta y tres centavos~$75,63 -setenta y cinco dólares con sesenta y tres~$75,63 -veintinueve dólares cincuenta centavos~$29,50 -un dólar~$1 -veinticinco centavos~$0,25 -veinticinco céntimos~$0,25 -doce euros y cinco centavos~€12,05 -doce dólares estadounidenses y cinco centavos~US$12,05 -doce dólares americanos y cinco centavos~US$12,05 -doce pesos y cinco centavos~$12,05 -doce yenes y cinco centavos~¥12,05 \ No newline at end of file diff --git a/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_ordinal.txt b/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_ordinal.txt deleted file mode 100644 index 18830c934767..000000000000 --- a/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_ordinal.txt +++ /dev/null @@ -1,29 +0,0 @@ -primero~primero -tercera~tercera -primer~primer -tercer~tercer -noveno~noveno -novena~novena -décimo~10.º -décima~10.ª -undécimo~11.º -undécima~11.ª -decimoprimero~11.º -décimo primero~11.º -decimoprimer~11.ᵉʳ -décimo primer~11.ᵉʳ -decimoprimera~11.ª -décima primera~11.ª -(technically ungrammatical) décimo primera~(technically ungrammatical) 11.ª -decimotercero~13.º -vigésimo primero~21.º -vigésima primera~21.ª -(technically ungrammatical) vigésimo primera~(technically ungrammatical) 21.ª -vigésimo primer~21.ᵉʳ -vigésimosegundo~22.º -vigésimo segundo~22.º -vigésimosegunda~22.ª -vigésima segunda~22.ª -vigésimo tercero~23.º -centésimo undécimo~111.º -centésimo trigésimo cuarto~134.º diff --git a/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_telephone.txt b/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_telephone.txt deleted file mode 100644 index 8a536301e1f8..000000000000 --- a/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_telephone.txt +++ /dev/null @@ -1,6 +0,0 @@ -uno dos tres uno dos tres cinco seis siete ocho~123-123-5678 -uno veintitrés uno veintitrés cincuenta y seis setenta y ocho~123-123-5678 -uno dos tres cuatro cinco seis siete ocho nueve~123-456-789 -uno veintitrés cuatro cincuenta y seis siete ochenta y nueve~123-456-789 -uno dos tres cuatro cinco seis siete ocho~1234-5678 -doce treinta y cuatro cincuenta y seis setenta y ocho~1234-5678 \ No newline at end of file diff --git a/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_time.txt b/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_time.txt deleted file mode 100644 index ed554fc266ac..000000000000 --- a/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_time.txt +++ /dev/null @@ -1,19 +0,0 @@ -las dieciséis cincuenta~las 16:50 -la una~la una -las dos~las dos -las tres personas~las tres personas -las dos a eme~las 2:00 a.m. -la una pe eme~la 1:00 p.m. -la una y diez~la 1:10 -la una y diez a eme~la 1:10 a.m. -la una y diez pe eme~la 1:10 p.m. -la una diez~la 1:10 -la una con diez~la 1:10 -la una y cuarto~la 1:15 -la una y media~la 1:30 -las dos menos veinte~la 1:40 -las dos menos cuarto~la 1:45 -cuarto para las dos~la 1:45 -un cuarto para las dos~la 1:45 -las veintitrés y media~las 23:30 -las veintitrés y cincuenta y nueve~las 23:59 \ No newline at end of file diff --git a/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_whitelist.txt b/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_whitelist.txt deleted file mode 100644 index 849a5a428a3d..000000000000 --- a/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_whitelist.txt +++ /dev/null @@ -1,4 +0,0 @@ -usted~ud. -ustedes~uds. -habla usted español~habla ud. español -hablan ustedes español~hablan uds. español \ No newline at end of file diff --git a/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_word.txt b/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_word.txt deleted file mode 100644 index 80b5275d40e5..000000000000 --- a/tests/nemo_text_processing/es/data_inverse_text_normalization/test_cases_word.txt +++ /dev/null @@ -1,49 +0,0 @@ -~ -yahoo!~yahoo! -veinte!~20 ! -x ~x -—~— -aaa~aaa -aabach~aabach -aabenraa~aabenraa -aabye~aabye -aaccessed~aaccessed -aach~aach -aachen's~aachen's -aadri~aadri -aafia~aafia -aagaard~aagaard -aagadu~aagadu -aagard~aagard -aagathadi~aagathadi -aaghart's~aaghart's -aagnes~aagnes -aagomoni~aagomoni -aagon~aagon -aagoo~aagoo -aagot~aagot -aahar~aahar -aahh~aahh -aahperd~aahperd -aaibinterstate~aaibinterstate -aajab~aajab -aakasa~aakasa -aakervik~aakervik -aakirkeby~aakirkeby -aalam~aalam -aalbaek~aalbaek -aaldiu~aaldiu -aalem~aalem -a'ali~a'ali -aalilaassamthey~aalilaassamthey -aalin~aalin -aaliyan~aaliyan -aaliyan's~aaliyan's -aamadu~aamadu -aamara~aamara -aambala~aambala -aamera~aamera -aamer's~aamer's -aamina~aamina -aaminah~aaminah -aamjiwnaang~aamjiwnaang diff --git a/tests/nemo_text_processing/es/data_text_normalization/test_cases_cardinal.txt b/tests/nemo_text_processing/es/data_text_normalization/test_cases_cardinal.txt deleted file mode 100644 index e12b3f115504..000000000000 --- a/tests/nemo_text_processing/es/data_text_normalization/test_cases_cardinal.txt +++ /dev/null @@ -1,69 +0,0 @@ -2~dos -3~tres -4~cuatro -5~cinco -6~seis -7~siete -8~ocho -9~nueve -10~diez -11~once -12~doce -13~trece -14~catorce -15~quince -16~dieciséis -17~diecisiete -18~dieciocho -19~diecinueve -20~veinte -22~veintidós -23~veintitrés -24~veinticuatro -25~veinticinco -26~veintiséis -27~veintisiete -28~veintiocho -29~veintinueve -30~treinta -40~cuarenta -50~cincuenta -60~sesenta -70~setenta -80~ochenta -90~noventa -100~cien -102~ciento dos -120~ciento veinte -130~ciento treinta -1000~mil -1 000~mil -1.000~mil -1010~mil diez -1020~mil veinte -1100~mil cien -1110~mil ciento diez -1111~mil ciento once -2000~dos mil -2002~dos mil dos -2010~dos mil diez -2020~dos mil veinte -2100~dos mil cien -2110~dos mil ciento diez -2111~dos mil ciento once -10000~diez mil -10 000~diez mil -10.000~diez mil -100000~cien mil -100 000~cien mil -100.000~cien mil -1 000 000~un millón -1.000.000~un millón -1 034 068~un millón treinta y cuatro mil sesenta y ocho -2.000.000~dos millones -1.000.000.000~mil millones -2.000.000.000~dos mil millones -3 000 000 000 000~tres billones -3.000.000.000.000~tres billones -100 000 000 000 000 000 000 000~cien mil trillones -100 000 000 000 000 000 000 002~cien mil trillones dos diff --git a/tests/nemo_text_processing/es/data_text_normalization/test_cases_date.txt b/tests/nemo_text_processing/es/data_text_normalization/test_cases_date.txt deleted file mode 100644 index bccdca7ec552..000000000000 --- a/tests/nemo_text_processing/es/data_text_normalization/test_cases_date.txt +++ /dev/null @@ -1,13 +0,0 @@ -1 enero~primero de enero -5 febrero~cinco de febrero -20 de marzo~veinte de marzo -abril 30~treinta de abril -31 marzo~treinta y uno de marzo -10 mayo 1990~diez de mayo de mil novecientos noventa -junio 11 2000~once de junio de dos mil -30 julio del 2020~treinta de julio del dos mil veinte -30-2-1990~treinta de febrero de mil novecientos noventa -30/2/1990~treinta de febrero de mil novecientos noventa -30.2.1990~treinta de febrero de mil novecientos noventa -1990-2-30~treinta de febrero de mil novecientos noventa -1990-02-30~treinta de febrero de mil novecientos noventa \ No newline at end of file diff --git a/tests/nemo_text_processing/es/data_text_normalization/test_cases_decimal.txt b/tests/nemo_text_processing/es/data_text_normalization/test_cases_decimal.txt deleted file mode 100644 index 39df4d89d935..000000000000 --- a/tests/nemo_text_processing/es/data_text_normalization/test_cases_decimal.txt +++ /dev/null @@ -1,19 +0,0 @@ -33,32~treinta y tres coma treinta y dos -32,22 millones~treinta y dos coma veintidós millones -320 320,22 millones~trescientos veinte mil trescientos veinte coma veintidós millones -5.002,132~cinco mil dos coma ciento treinta y dos -3,2 trillones~tres coma dos trillones -3 millones~tres millones -3 000 millones~tres mil millones -3000 millones~tres mil millones -3.000 millones~tres mil millones -3.001 millones~tres mil un millones -1 millón~un millón -1 000 millones~mil millones -1000 millones~mil millones -1.000 millones~mil millones -2,33302 millones~dos coma tres tres tres cero dos millones -1,5332 millón~uno coma cinco tres tres dos millón -1,53322 millón~uno coma cinco tres tres dos dos millón -1,53321 millón~uno coma cinco tres tres dos un millón -101,010101 millones~ciento uno coma cero uno cero uno cero un millones \ No newline at end of file diff --git a/tests/nemo_text_processing/es/data_text_normalization/test_cases_electronic.txt b/tests/nemo_text_processing/es/data_text_normalization/test_cases_electronic.txt deleted file mode 100644 index 0ac0b0bbd4fb..000000000000 --- a/tests/nemo_text_processing/es/data_text_normalization/test_cases_electronic.txt +++ /dev/null @@ -1,12 +0,0 @@ -a.bc@gmail.com~a punto b c arroba gmail punto com -cdf@abc.edu~c d f arroba a b c punto e d u -abc@gmail.abc~a b c arroba gmail punto a b c -abc@abc.com~a b c arroba a b c punto com -asdf123@abc.com~a s d f uno dos tres arroba a b c punto com -a1b2@abc.com~a uno b dos arroba a b c punto com -ab3.sdd.3@gmail.com~a b tres punto s d d punto tres arroba gmail punto com -https://www.nvidia.com~h t t p s dos puntos barra barra w w w punto nvidia punto com -www.nvidia.com~w w w punto nvidia punto com -www.abc.es/efg~w w w punto a b c punto es barra e f g -www.abc.es~w w w punto a b c punto es -http://www.ourdailynews.com.sm~h t t p dos puntos barra barra w w w punto o u r d a i l y n e w s punto com punto s m \ No newline at end of file diff --git a/tests/nemo_text_processing/es/data_text_normalization/test_cases_fraction.txt b/tests/nemo_text_processing/es/data_text_normalization/test_cases_fraction.txt deleted file mode 100644 index 1260c898d61a..000000000000 --- a/tests/nemo_text_processing/es/data_text_normalization/test_cases_fraction.txt +++ /dev/null @@ -1,72 +0,0 @@ -1/3~un tercio -2/3~dos tercios -1/4~un cuarto -2/4~dos cuartos -1/5~un quinto -2/5~dos quintos -1/6~un sexto -2/6~dos sextos -1/7~un séptimo -2/7~dos séptimos -1/8~un octavo -2/8~dos octavos -1/9~un noveno -2/9~dos novenos -1/10~un décimo -2/10~dos décimos -1/11~un onceavo -1/12~un doceavo -1/13~un treceavo -1/14~un catorceavo -1/15~un quinceavo -1/16~un dieciseisavo -1/17~un diecisieteavo -1/18~un dieciochoavo -1/19~un diecinueveavo -1/20~un veinteavo -1/21~un veintiunavo -1/22~un veintidosavo -1/30~un treintavo -1/31~un treintaiunavo -1/40~un cuarentavo -1/41~un cuarentaiunavo -1/50~un cincuentavo -1/60~un sesentavo -1/70~un setentavo -1/80~un ochentavo -1/90~un noventavo -1/100~un centésimo -2/100~dos centésimos -3 2/100~tres y dos centésimos -1/101~uno sobre ciento uno -1/110~uno sobre ciento diez -1/111~uno sobre ciento once -1/112~uno sobre ciento doce -1/123~uno sobre ciento veintitrés -1/134~uno sobre ciento treinta y cuatro -1/200~un ducentésimo -1/201~uno sobre doscientos uno -1/234~uno sobre doscientos treinta y cuatro -1/300~un tricentésimo -1/345~uno sobre trescientos cuarenta y cinco -1/400~un cuadringentésimo -1/456~uno sobre cuatrocientos cincuenta y seis -1/500~un quingentésimo -1/600~un sexcentésimo -1/700~un septingentésimo -1/800~un octingentésimo -1/900~un noningentésimo -1/1000~un milésimo -2/1000~dos milésimos -2 2/1000~dos y dos milésimos -1/1001~uno sobre mil uno -1/1100~uno sobre mil cien -1/1200~uno sobre mil doscientos -1/1234~uno sobre mil doscientos treinta y cuatro -1/2000~un dosmilésimo -1/5000~un cincomilésimo -1/10000~un diezmilésimo -1/100.000~un cienmilésimo -1/1.000.000~un millonésimo -1/100.000.000~un cienmillonésimo -1/1.200.000.000~un mildoscientosmillonésimo \ No newline at end of file diff --git a/tests/nemo_text_processing/es/data_text_normalization/test_cases_measure.txt b/tests/nemo_text_processing/es/data_text_normalization/test_cases_measure.txt deleted file mode 100644 index a456ee3eda31..000000000000 --- a/tests/nemo_text_processing/es/data_text_normalization/test_cases_measure.txt +++ /dev/null @@ -1,24 +0,0 @@ -1,2-a~uno coma dos a -a-5~a cinco -200 m~doscientos metros -3 3/5 m~tres y tres quintos de metro -3 h~tres horas -1 h~una hora -245 mph~doscientas cuarenta y cinco millas por hora -200.045 mph~doscientas mil cuarenta y cinco millas por hora -1,01 mph~una coma cero una millas por hora -1 1/2 mph~una y media milla por hora -1 1/2 m~uno y medio metro -1 3/5 mph~una y tres quintos de milla por hora -3/5 mph~tres quintos de milla por hora -2 kg~dos kilogramos -60,2400 kg~sesenta coma dos cuatro cero cero kilogramos --60,2400 kg~menos sesenta coma dos cuatro cero cero kilogramos -8,52 %~ocho coma cincuenta y dos por ciento --8,52 %~menos ocho coma cincuenta y dos por ciento -1 %~uno por ciento -3 cm~tres centímetros -4 s~cuatro segundos -5 l~cinco litros -4,51/s~cuatro coma cincuenta y uno por segundo -0,0101 s~cero coma cero uno cero un segundos \ No newline at end of file diff --git a/tests/nemo_text_processing/es/data_text_normalization/test_cases_money.txt b/tests/nemo_text_processing/es/data_text_normalization/test_cases_money.txt deleted file mode 100644 index 0a8706ecf502..000000000000 --- a/tests/nemo_text_processing/es/data_text_normalization/test_cases_money.txt +++ /dev/null @@ -1,25 +0,0 @@ -$1~un dólar -1 $~un dólar -$1,50~un dólar cincuenta centavos -1,50 $~un dólar cincuenta centavos -£200.000.001~doscientos millones una libras -200.000.001 £~doscientos millones una libras -2 billones de euros~dos billones de euros -€2 billones~dos billones de euros -€ 2 billones~dos billones de euros -€ 2,3 billones~dos coma tres billones de euros -2,3 billones de euros~dos coma tres billones de euros -€5,50~cinco euros cincuenta céntimos -5,50 €~cinco euros cincuenta céntimos -5,01 €~cinco euros un céntimo -5,01 £~cinco libras un penique -21 czk~veintiuna coronas checas -czk21~veintiuna coronas checas -czk21,1 millones~veintiuno coma un millones de coronas checas -czk 5,50 billones~cinco coma cincuenta billones de coronas checas -rs 5,50 billones~cinco coma cincuenta billones de rupias -czk5,50 billones~cinco coma cincuenta billones de coronas checas -0,55 $~cincuenta y cinco centavos -1,01 $~un dólar un centavo -¥12,05~doce yenes cinco centavos -£200.000.001~doscientos millones una libras \ No newline at end of file diff --git a/tests/nemo_text_processing/es/data_text_normalization/test_cases_normalize_with_audio.txt b/tests/nemo_text_processing/es/data_text_normalization/test_cases_normalize_with_audio.txt deleted file mode 100644 index f118673ad173..000000000000 --- a/tests/nemo_text_processing/es/data_text_normalization/test_cases_normalize_with_audio.txt +++ /dev/null @@ -1,162 +0,0 @@ -~1,0101 -uno coma cero uno cero un -uno coma cero uno cero uno -una coma cero una cero una -~21 -veintiún -veintiuno -veintiuna -~201 -doscientos un -doscientos uno -doscientas una -doscientas un -~1 -un -uno -una -~121 -ciento veintiún -ciento veintiuno -ciento veintiuna -121 -~200 -doscientos -doscientas -200 -~201 -doscientos un -doscientos uno -doscientas una -201 -~221 -doscientos veintiún -doscientas veintiún -doscientos veintiuno -doscientas veintiuna -~1 -un -uno -una -1 -~550.000.001 -quinientos cincuenta millones un -quinientos cincuenta millones una -quinientos cincuenta millones uno -550.000.001 -~500.501 -quinientos mil quinientos un -quinientos mil quinientos uno -quinientas mil quinientas una -quinientas mil quinientas un -500.501 -~500.001.º -quinientosmilésimo primero -quingentésimo milésimo primero -quinientosmilésimos primeros -quingentésimos milésimos primeros -500.001.º -~500.001.ª -quinientasmilésima primera -quingentésima milésima primera -quinientasmilésimas primeras -quingentésimas milésimas primeras -500.001.ª -~11.ª -décima primera -decimoprimera -décimas primeras -decimoprimeras -undécima -undécimas -11.ª -~11.º -décimo primero -decimoprimero -décimos primeros -decimoprimeros -undécimo -undécimos -11.º -~12.º -décimo segundo -decimosegundo -décimos segundos -decimosegundos -duodécimo -duodécimos -12.º -~200,0101 -doscientos coma cero uno cero un -doscientos coma cero uno cero uno -doscientas coma cero una cero una -doscientas coma cero una cero un -200,0101 -~1.000.200,21 -un millón doscientos coma veintiún -un millón doscientas coma veintiún -un millón doscientos coma veintiuno -un millón doscientas coma veintiuna -un millón doscientas y veintiún -un millón doscientos coma dos un -un millón doscientos coma dos uno -un millón doscientas coma dos una -un millón doscientas coma dos un -1.000.200,21 -~1/12 -un doceavo -una doceava parte -un duodécimo -una duodécima parte -uno sobre doce -1/12 -~5/200 -cinco ducentésimos -cinco ducentésimas partes -cinco sobre doscientos -5/200 -~1 5/3 -uno y cinco tercios -una y cinco terceras partes -uno y cinco sobre tres -una y cinco sobre tres -~1/5/2020 -primero de mayo de dos mil veinte -uno de mayo de dos mil veinte -cinco de enero de dos mil veinte -~$5,50 -cinco dólares con cincuenta -cinco dólares y cincuenta -cinco dólares cincuenta -cinco dólares con cincuenta centavos -cinco dólares y cincuenta centavos -cinco dólares cincuenta centavos -~2.30 h -dos y treinta -dos y media -tres menos treinta -tres menos media -treinta para las tres -~cmxcix -noningentésimo nonagésimo noveno -noningentésima nonagésima novena -~1/2 -medio -media parte -media -~1 1/2 -uno y medio -una y media parte -una y media -~3/2 -tres medios -tres medias -tres medias partes -~12.30 a.m. -doce y treinta de la medianoche -doce y treinta de la noche -doce y media de la medianoche -doce y media de la noche -una menos treinta de la mañana -una menos media de la mañana -treinta para la una de la mañana \ No newline at end of file diff --git a/tests/nemo_text_processing/es/data_text_normalization/test_cases_ordinal.txt b/tests/nemo_text_processing/es/data_text_normalization/test_cases_ordinal.txt deleted file mode 100644 index 77c084cf8e37..000000000000 --- a/tests/nemo_text_processing/es/data_text_normalization/test_cases_ordinal.txt +++ /dev/null @@ -1,107 +0,0 @@ -1.ᵉʳ~primer -1.º~primero -1.ª~primera -2.º~segundo -2.ª~segunda -3.ᵉʳ~tercer -3.º~tercero -3.ª~tercera -4.º~cuarto -4.ª~cuarta -5.º~quinto -5.ª~quinta -6.º~sexto -6.ª~sexta -7.º~séptimo -7.ª~séptima -8.º~octavo -8.ª~octava -9.º~noveno -9.ª~novena -10.º~décimo -10.ª~décima -11.ᵉʳ~decimoprimer -11.º~undécimo -11.ª~undécima -12.º~duodécimo -12.ª~duodécima -13.ᵉʳ~decimotercer -13.º~decimotercero -13.ª~decimotercera -14.º~decimocuarto -14.ª~decimocuarta -15.º~decimoquinto -15.ª~decimoquinta -16.º~decimosexto -16.ª~decimosexta -17.º~decimoséptimo -17.ª~decimoséptima -18.º~decimoctavo -18.ª~decimoctava -19.º~decimonoveno -19.ª~decimonovena -20.º~vigésimo -20.ª~vigésima -21.ᵉʳ~vigesimoprimer -21.º~vigesimoprimero -21.ª~vigesimoprimera -30.º~trigésimo -30.ª~trigésima -31.ᵉʳ~trigésimo primer -31.º~trigésimo primero -31.ª~trigésima primera -40.º~cuadragésimo -40.ª~cuadragésima -41.ᵉʳ~cuadragésimo primer -41.º~cuadragésimo primero -41.ª~cuadragésima primera -50.º~quincuagésimo -50.ª~quincuagésima -51.ᵉʳ~quincuagésimo primer -51.º~quincuagésimo primero -51.ª~quincuagésima primera -60.º~sexagésimo -60.ª~sexagésima -70.º~septuagésimo -70.ª~septuagésima -80.º~octogésimo -80.ª~octogésima -90.º~nonagésimo -90.ª~nonagésima -100.º~centésimo -100.ª~centésima -101.ᵉʳ~centésimo primer -101.º~centésimo primero -101.ª~centésima primera -134.º~centésimo trigésimo cuarto -134.ª~centésima trigésima cuarta -200.º~ducentésimo -200.ª~ducentésima -300.º~tricentésimo -300.ª~tricentésima -400.º~cuadringentésimo -400.ª~cuadringentésima -500.º~quingentésimo -500.ª~quingentésima -600.º~sexcentésimo -600.ª~sexcentésima -700.º~septingentésimo -700.ª~septingentésima -800.º~octingentésimo -800.ª~octingentésima -900.º~noningentésimo -900.ª~noningentésima -1000.º~milésimo -1000.ª~milésima -1001.ᵉʳ~milésimo primer -1 000.º~milésimo -1 000.ª~milésima -1 001.ᵉʳ~milésimo primer -1.000.º~milésimo -1.000.ª~milésima -1.001.ᵉʳ~milésimo primer -1248.º~milésimo ducentésimo cuadragésimo octavo -1248.ª~milésima ducentésima cuadragésima octava -2000.º~dosmilésimo -100 000.º~cienmilésimo -999.º~noningentésimo nonagésimo noveno \ No newline at end of file diff --git a/tests/nemo_text_processing/es/data_text_normalization/test_cases_telephone.txt b/tests/nemo_text_processing/es/data_text_normalization/test_cases_telephone.txt deleted file mode 100644 index a6aa6928b6ec..000000000000 --- a/tests/nemo_text_processing/es/data_text_normalization/test_cases_telephone.txt +++ /dev/null @@ -1,3 +0,0 @@ -123-123-5678~uno dos tres uno dos tres cinco seis siete ocho -123-456-789~uno dos tres cuatro cinco seis siete ocho nueve -1234-5678~uno dos tres cuatro cinco seis siete ocho \ No newline at end of file diff --git a/tests/nemo_text_processing/es/data_text_normalization/test_cases_time.txt b/tests/nemo_text_processing/es/data_text_normalization/test_cases_time.txt deleted file mode 100644 index 62d5c3485dc3..000000000000 --- a/tests/nemo_text_processing/es/data_text_normalization/test_cases_time.txt +++ /dev/null @@ -1,26 +0,0 @@ -1.00~una -1:00~una -01:00~una -01 h~una -3 h~tres horas -1 h~una hora -1.05 h~una y cinco -01.05 h~una y cinco -1.00 h~una -1.00 a.m.~una de la mañana -1.00 a.m~una de la mañana -1.00 p.m.~una de la tarde -1.00 p.m est~una de la tarde e s t -1.00 est~una e s t -5:02 est~cinco y dos e s t -5:02 p.m pst~cinco y dos de la noche p s t -5:02 p.m.~cinco y dos de la noche -12.15~doce y cuarto -12.15 a.m.~doce y cuarto de la noche -12.15 p.m.~doce y cuarto del mediodía -13.30~trece y media -14.05~catorce y cinco -24:50~veinticuatro y cincuenta -3:02:32 pst~tres horas dos minutos y treinta y dos segundos p s t -00:52~cero y cincuenta y dos -0:52~cero y cincuenta y dos diff --git a/tests/nemo_text_processing/es/data_text_normalization/test_cases_whitelist.txt b/tests/nemo_text_processing/es/data_text_normalization/test_cases_whitelist.txt deleted file mode 100644 index 1699392fc454..000000000000 --- a/tests/nemo_text_processing/es/data_text_normalization/test_cases_whitelist.txt +++ /dev/null @@ -1,3 +0,0 @@ -el dr.~el doctor -sr. rodriguez~señor rodriguez -182 esq. toledo~ciento ochenta y dos esquina toledo \ No newline at end of file diff --git a/tests/nemo_text_processing/es/data_text_normalization/test_cases_word.txt b/tests/nemo_text_processing/es/data_text_normalization/test_cases_word.txt deleted file mode 100644 index 0d3e97450c17..000000000000 --- a/tests/nemo_text_processing/es/data_text_normalization/test_cases_word.txt +++ /dev/null @@ -1,48 +0,0 @@ -~ -yahoo!~yahoo! -veinte!~veinte! -—~— -aaa~aaa -aabach~aabach -aabenraa~aabenraa -aabye~aabye -aaccessed~aaccessed -aach~aach -aachen's~aachen's -aadri~aadri -aafia~aafia -aagaard~aagaard -aagadu~aagadu -aagard~aagard -aagathadi~aagathadi -aaghart's~aaghart's -aagnes~aagnes -aagomoni~aagomoni -aagon~aagon -aagoo~aagoo -aagot~aagot -aahar~aahar -aahh~aahh -aahperd~aahperd -aaibinterstate~aaibinterstate -aajab~aajab -aakasa~aakasa -aakervik~aakervik -aakirkeby~aakirkeby -aalam~aalam -aalbaek~aalbaek -aaldiu~aaldiu -aalem~aalem -a'ali~a'ali -aalilaassamthey~aalilaassamthey -aalin~aalin -aaliyan~aaliyan -aaliyan's~aaliyan's -aamadu~aamadu -aamara~aamara -aambala~aambala -aamera~aamera -aamer's~aamer's -aamina~aamina -aaminah~aaminah -aamjiwnaang~aamjiwnaang diff --git a/tests/nemo_text_processing/es/test_cardinal.py b/tests/nemo_text_processing/es/test_cardinal.py deleted file mode 100644 index ddb302661d78..000000000000 --- a/tests/nemo_text_processing/es/test_cardinal.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestCardinal: - - inverse_normalizer = ( - InverseNormalizer(lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('es/data_inverse_text_normalization/test_cases_cardinal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer = ( - Normalizer(input_case='cased', lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - - normalizer_with_audio = ( - NormalizerWithAudio(input_case='cased', lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - if CACHE_DIR and RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(parse_test_case_file('es/data_text_normalization/test_cases_cardinal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio: - pred_non_deterministic = self.normalizer_with_audio.normalize( - test_input, n_tagged=500, punct_post_process=False - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/es/test_date.py b/tests/nemo_text_processing/es/test_date.py deleted file mode 100644 index f14b3338ee7a..000000000000 --- a/tests/nemo_text_processing/es/test_date.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestDate: - inverse_normalizer = ( - InverseNormalizer(lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('es/data_inverse_text_normalization/test_cases_date.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer = ( - Normalizer(input_case='cased', lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - - normalizer_with_audio = ( - NormalizerWithAudio(input_case='cased', lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - if CACHE_DIR and RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(parse_test_case_file('es/data_text_normalization/test_cases_date.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio: - pred_non_deterministic = self.normalizer_with_audio.normalize( - test_input, n_tagged=500, punct_post_process=False - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/es/test_decimal.py b/tests/nemo_text_processing/es/test_decimal.py deleted file mode 100644 index 8ffab62e0bdd..000000000000 --- a/tests/nemo_text_processing/es/test_decimal.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestDecimal: - inverse_normalizer = ( - InverseNormalizer(lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('es/data_inverse_text_normalization/test_cases_decimal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer = ( - Normalizer(input_case='cased', lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - - normalizer_with_audio = ( - NormalizerWithAudio(input_case='cased', lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) - if CACHE_DIR and RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(parse_test_case_file('es/data_text_normalization/test_cases_decimal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio: - pred_non_deterministic = self.normalizer_with_audio.normalize( - test_input, n_tagged=500, punct_post_process=False - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/es/test_electronic.py b/tests/nemo_text_processing/es/test_electronic.py deleted file mode 100644 index a8eb62af3399..000000000000 --- a/tests/nemo_text_processing/es/test_electronic.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestElectronic: - inverse_normalizer_es = ( - InverseNormalizer(lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('es/data_inverse_text_normalization/test_cases_electronic.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm_es(self, test_input, expected): - pred = self.inverse_normalizer_es.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer = ( - Normalizer(input_case='cased', lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - - normalizer_with_audio = ( - NormalizerWithAudio(input_case='cased', lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) - if CACHE_DIR and PYNINI_AVAILABLE and RUN_AUDIO_BASED_TESTS - else None - ) - - @parameterized.expand(parse_test_case_file('es/data_text_normalization/test_cases_electronic.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio: - pred_non_deterministic = self.normalizer_with_audio.normalize( - test_input, n_tagged=500, punct_post_process=False - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/es/test_fraction.py b/tests/nemo_text_processing/es/test_fraction.py deleted file mode 100644 index 746a9857a7e3..000000000000 --- a/tests/nemo_text_processing/es/test_fraction.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestFraction: - normalizer = ( - Normalizer(input_case='cased', lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - - normalizer_with_audio = ( - NormalizerWithAudio(input_case='cased', lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) - if CACHE_DIR and RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(parse_test_case_file('es/data_text_normalization/test_cases_fraction.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio: - pred_non_deterministic = self.normalizer_with_audio.normalize( - test_input, n_tagged=500, punct_post_process=False - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/es/test_measure.py b/tests/nemo_text_processing/es/test_measure.py deleted file mode 100644 index 8b7e673a3634..000000000000 --- a/tests/nemo_text_processing/es/test_measure.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestMeasure: - inverse_normalizer_es = ( - InverseNormalizer(lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('es/data_inverse_text_normalization/test_cases_measure.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm_es(self, test_input, expected): - pred = self.inverse_normalizer_es.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer = ( - Normalizer(input_case='cased', lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - - normalizer_with_audio = ( - NormalizerWithAudio(input_case='cased', lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) - if CACHE_DIR and RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(parse_test_case_file('es/data_text_normalization/test_cases_measure.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio: - pred_non_deterministic = self.normalizer_with_audio.normalize( - test_input, n_tagged=500, punct_post_process=False - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/es/test_money.py b/tests/nemo_text_processing/es/test_money.py deleted file mode 100644 index 090aafe6ef07..000000000000 --- a/tests/nemo_text_processing/es/test_money.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestMoney: - inverse_normalizer = ( - InverseNormalizer(lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('es/data_inverse_text_normalization/test_cases_money.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer = ( - Normalizer(input_case='cased', lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - - normalizer_with_audio = ( - NormalizerWithAudio(input_case='cased', lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) - if CACHE_DIR and PYNINI_AVAILABLE and RUN_AUDIO_BASED_TESTS - else None - ) - - @parameterized.expand(parse_test_case_file('es/data_text_normalization/test_cases_money.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio: - pred_non_deterministic = self.normalizer_with_audio.normalize( - test_input, n_tagged=500, punct_post_process=False - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/es/test_normalization_with_audio.py b/tests/nemo_text_processing/es/test_normalization_with_audio.py deleted file mode 100644 index 67c495e9de2b..000000000000 --- a/tests/nemo_text_processing/es/test_normalization_with_audio.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, get_test_cases_multiple - -try: - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestNormalizeWithAudio: - - normalizer_es = ( - NormalizerWithAudio(input_case='cased', lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(get_test_cases_multiple('es/data_text_normalization/test_cases_normalize_with_audio.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer_es.normalize(test_input, n_tagged=50, punct_post_process=False) - print(expected) - print("pred") - print(pred) - assert len(set(pred).intersection(set(expected))) == len( - expected - ), f'missing: {set(expected).difference(set(pred))}' diff --git a/tests/nemo_text_processing/es/test_ordinal.py b/tests/nemo_text_processing/es/test_ordinal.py deleted file mode 100644 index ef49e4167669..000000000000 --- a/tests/nemo_text_processing/es/test_ordinal.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestOrdinal: - inverse_normalizer = ( - InverseNormalizer(lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('es/data_inverse_text_normalization/test_cases_ordinal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer = ( - Normalizer(input_case='cased', lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - normalizer_with_audio = ( - NormalizerWithAudio(input_case='cased', lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) - if CACHE_DIR and PYNINI_AVAILABLE and RUN_AUDIO_BASED_TESTS - else None - ) - - @parameterized.expand(parse_test_case_file('es/data_text_normalization/test_cases_ordinal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio: - pred_non_deterministic = self.normalizer_with_audio.normalize( - test_input, n_tagged=500, punct_post_process=False, - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/es/test_sparrowhawk_inverse_text_normalization.sh b/tests/nemo_text_processing/es/test_sparrowhawk_inverse_text_normalization.sh deleted file mode 100644 index f6a71c2cb005..000000000000 --- a/tests/nemo_text_processing/es/test_sparrowhawk_inverse_text_normalization.sh +++ /dev/null @@ -1,64 +0,0 @@ -#! /bin/sh - -PROJECT_DIR=/workspace/tests - -runtest () { - input=$1 - cd /workspace/sparrowhawk/documentation/grammars - - # read test file - while read testcase; do - IFS='~' read spoken written <<< $testcase - denorm_pred=$(echo $spoken | normalizer_main --config=sparrowhawk_configuration.ascii_proto 2>&1 | tail -n 1) - - # trim white space - written="$(echo -e "${written}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - denorm_pred="$(echo -e "${denorm_pred}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - - # input expected actual - assertEquals "$spoken" "$written" "$denorm_pred" - done < "$input" -} - -testITNCardinal() { - input=$PROJECT_DIR/es/data_inverse_text_normalization/test_cases_cardinal.txt - runtest $input -} - -testITNDate() { - input=$PROJECT_DIR/es/data_inverse_text_normalization/test_cases_date.txt - runtest $input -} - -testITNDecimal() { - input=$PROJECT_DIR/es/data_inverse_text_normalization/test_cases_decimal.txt - runtest $input -} - -testITNOrdinal() { - input=$PROJECT_DIR/es/data_inverse_text_normalization/test_cases_ordinal.txt - runtest $input -} - -testITNTime() { - input=$PROJECT_DIR/es/data_inverse_text_normalization/test_cases_time.txt - runtest $input -} - -testITNMeasure() { - input=$PROJECT_DIR/es/data_inverse_text_normalization/test_cases_measure.txt - runtest $input -} - -testITNMoney() { - input=$PROJECT_DIR/es/data_inverse_text_normalization/test_cases_money.txt - runtest $input -} - -testITNWhitelist() { - input=$PROJECT_DIR/es/data_inverse_text_normalization/test_cases_whitelist.txt - runtest $input -} - -# Load shUnit2 -. $PROJECT_DIR/../shunit2/shunit2 diff --git a/tests/nemo_text_processing/es/test_sparrowhawk_normalization.sh b/tests/nemo_text_processing/es/test_sparrowhawk_normalization.sh deleted file mode 100644 index a74399e26712..000000000000 --- a/tests/nemo_text_processing/es/test_sparrowhawk_normalization.sh +++ /dev/null @@ -1,84 +0,0 @@ -#! /bin/sh - -PROJECT_DIR=/workspace/tests - -runtest () { - input=$1 - cd /workspace/sparrowhawk/documentation/grammars - - # read test file - while read testcase; do - IFS='~' read written spoken <<< $testcase - denorm_pred=$(echo $written | normalizer_main --config=sparrowhawk_configuration.ascii_proto 2>&1 | tail -n 1) - - # trim white space - spoken="$(echo -e "${spoken}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - denorm_pred="$(echo -e "${denorm_pred}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - - # input expected actual - assertEquals "$written" "$spoken" "$denorm_pred" - done < "$input" -} - -testTNCardinal() { - input=$PROJECT_DIR/es/data_text_normalization/test_cases_cardinal.txt - runtest $input -} - -testTNDate() { - input=$PROJECT_DIR/es/data_text_normalization/test_cases_date.txt - runtest $input -} - -testTNDecimal() { - input=$PROJECT_DIR/es/data_text_normalization/test_cases_decimal.txt - runtest $input -} - -testTNElectronic() { - input=$PROJECT_DIR/es/data_text_normalization/test_cases_electronic.txt - runtest $input -} - -testTNFraction() { - input=$PROJECT_DIR/es/data_text_normalization/test_cases_fraction.txt - runtest $input -} - -testTNMoney() { - input=$PROJECT_DIR/es/data_text_normalization/test_cases_money.txt - runtest $input -} - -testTNOrdinal() { - input=$PROJECT_DIR/es/data_text_normalization/test_cases_ordinal.txt - runtest $input -} - -testTNTelephone() { - input=$PROJECT_DIR/es/data_text_normalization/test_cases_ordinal.txt - runtest $input -} - -testTNTime() { - input=$PROJECT_DIR/es/data_text_normalization/test_cases_time.txt - runtest $input -} - -testTNMeasure() { - input=$PROJECT_DIR/es/data_text_normalization/test_cases_measure.txt - runtest $input -} - -testTNWhitelist() { - input=$PROJECT_DIR/es/data_text_normalization/test_cases_whitelist.txt - runtest $input -} - -testTNWord() { - input=$PROJECT_DIR/es/data_text_normalization/test_cases_word.txt - runtest $input -} - -# Load shUnit2 -. $PROJECT_DIR/../shunit2/shunit2 diff --git a/tests/nemo_text_processing/es/test_telephone.py b/tests/nemo_text_processing/es/test_telephone.py deleted file mode 100644 index edf020cf34e7..000000000000 --- a/tests/nemo_text_processing/es/test_telephone.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestTelephone: - inverse_normalizer_es = ( - InverseNormalizer(lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('es/data_inverse_text_normalization/test_cases_telephone.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm_es(self, test_input, expected): - pred = self.inverse_normalizer_es.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer = ( - Normalizer(input_case='cased', lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - - normalizer_with_audio = ( - NormalizerWithAudio(input_case='cased', lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) - if CACHE_DIR and RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(parse_test_case_file('es/data_text_normalization/test_cases_telephone.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio: - pred_non_deterministic = self.normalizer_with_audio.normalize( - test_input, n_tagged=30, punct_post_process=False - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/es/test_time.py b/tests/nemo_text_processing/es/test_time.py deleted file mode 100644 index 0f19bad9b4ae..000000000000 --- a/tests/nemo_text_processing/es/test_time.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestTime: - inverse_normalizer_es = ( - InverseNormalizer(lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('es/data_inverse_text_normalization/test_cases_time.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm_es(self, test_input, expected): - pred = self.inverse_normalizer_es.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer = ( - Normalizer(input_case='cased', lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - - normalizer_with_audio = ( - NormalizerWithAudio(input_case='cased', lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) - if CACHE_DIR and RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(parse_test_case_file('es/data_text_normalization/test_cases_time.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio: - pred_non_deterministic = self.normalizer_with_audio.normalize( - test_input, n_tagged=500, punct_post_process=False - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/es/test_whitelist.py b/tests/nemo_text_processing/es/test_whitelist.py deleted file mode 100644 index d8d36a5fd183..000000000000 --- a/tests/nemo_text_processing/es/test_whitelist.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestWhitelist: - inverse_normalizer_es = ( - InverseNormalizer(lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('es/data_inverse_text_normalization/test_cases_whitelist.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm_es(self, test_input, expected): - pred = self.inverse_normalizer_es.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer = ( - Normalizer(input_case='cased', lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - normalizer_with_audio = ( - NormalizerWithAudio(input_case='cased', lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) - if CACHE_DIR and PYNINI_AVAILABLE and RUN_AUDIO_BASED_TESTS - else None - ) - - @parameterized.expand(parse_test_case_file('es/data_text_normalization/test_cases_whitelist.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer.normalize(test_input, verbose=False) - assert pred == expected - - if self.normalizer_with_audio: - pred_non_deterministic = self.normalizer_with_audio.normalize( - test_input, n_tagged=10, punct_post_process=False - ) - assert expected in pred_non_deterministic diff --git a/tests/nemo_text_processing/es/test_word.py b/tests/nemo_text_processing/es/test_word.py deleted file mode 100644 index 54b6307a30fe..000000000000 --- a/tests/nemo_text_processing/es/test_word.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, RUN_AUDIO_BASED_TESTS, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - from nemo_text_processing.text_normalization.normalize import Normalizer - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestWord: - inverse_normalizer_es = ( - InverseNormalizer(lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('es/data_inverse_text_normalization/test_cases_word.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm_es(self, test_input, expected): - pred = self.inverse_normalizer_es.inverse_normalize(test_input, verbose=False) - assert pred == expected - - normalizer_es = ( - Normalizer(input_case='cased', lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) - if PYNINI_AVAILABLE - else None - ) - normalizer_with_audio_es = ( - NormalizerWithAudio(input_case='cased', lang='es', cache_dir=CACHE_DIR, overwrite_cache=False) - if CACHE_DIR and RUN_AUDIO_BASED_TESTS and PYNINI_AVAILABLE - else None - ) - - @parameterized.expand(parse_test_case_file('es/data_text_normalization/test_cases_word.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm(self, test_input, expected): - pred = self.normalizer_es.normalize(test_input, verbose=False) - assert pred == expected, f"input: {test_input}" - - if self.normalizer_with_audio_es: - pred_non_deterministic = self.normalizer_with_audio_es.normalize( - test_input, n_tagged=150, punct_post_process=False - ) - assert expected in pred_non_deterministic, f"input: {test_input}" diff --git a/tests/nemo_text_processing/fr/__init__.py b/tests/nemo_text_processing/fr/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/tests/nemo_text_processing/fr/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_cardinal.txt b/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_cardinal.txt deleted file mode 100644 index 9cebe04a5ffc..000000000000 --- a/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_cardinal.txt +++ /dev/null @@ -1,106 +0,0 @@ -cent~100 -dix-huit~18 -vingt et un~21 -vingt-et-un~21 -trente et un~31 -trente-et-un~31 -quarante-trois~43 -quarante trois~40 trois -cinquante et un~51 -cinquante-et-un~51 -soixante et un~61 -soixante-et-un~61 -soixante-dix~70 -soixante-douze~72 -quatre-vingts~80 -quatre-vingt-dix-huit~98 -cent~100 -cent deux~102 -cent-deux~102 -cent vingt~120 -cent-vingt~120 -deux-cents~200 -deux cent neuf~209 -deux-cent-neuf~209 -cent onze~111 -cent-onze~111 -mille~1000 -cent vingt~120 -cent-vingt~120 -mille vingt~1020 -mille-vingt~1020 -neuf billion sept cent quatre-vingt-neuf milliard trois cent quatre-vingt-deux million cinq cent trente-six mille cent trente~9789382536130 -neuf-billion-sept-cent-quatre-vingt-neuf-milliard-trois-cent-quatre-vingt-deux-million-cinq-cent-trente-six-mille-cent-trente~9789382536130 -deux cent cinquante-quatre~254 -deux-cent-cinquante-quatre~254 -cent quarante-sept mille quatre cent cinquante et une~147451 -cent-quarante-sept-mille-quatre-cent-cinquante-et-une~147451 -un million cent cinquante-six mille cent soixante-treize~1156173 -un-million-cent-cinquante-six-mille-cent-soixante-treize~1156173 -un milliard cinq cent quatre-vingt-treize million soixante-douze mille neuf cent soixante et un~1593072961 -un-milliard-cinq-cent-quatre-vingt-treize-million-soixante-douze-mille-neuf-cent-soixante-et-un~1593072961 -un milliard cinq cent quatre-vingt-treize million septante-deux mille neuf cent soixante et un~1593072961 -un-milliard-cinq-cent-quatre-vingt-treize-million-septante-deux-mille-neuf-cent-soixante-et-un~1593072961 -quatre-vingt-dix-sept billiard huit cent huit billion deux cent soixante-quatre milliard sept cent soixante-douze million sept cent quatre-vingt-douze mille cinq~97808264772792005 -quatre-vingt-dix-sept-billiard-huit-cent-huit-billion-deux-cent-soixante-quatre-milliard-sept-cent-soixante-douze-million-sept-cent-quatre-vingt-douze-mille-cinq~97808264772792005 -dix billiard dix billion dix million cent mille dix~10010000010100010 -dix-billiard-dix-billion-dix-million-cent-mille-dix~10010000010100010 -moins vingt-cinq mille trente-sept~-25037 -moins vingt-cinq-mille-trente-sept~-25037 -moins dix-neuf cent trente-sept~-1937 -moins dix-neuf-cent-trente-sept~-1937 -un billiard deux cent soixante-quatre billion trois cent un milliard neuf cent trente-huit million cent quatre~1264301938000104 -un-billiard-deux-cent-soixante-quatre-billion-trois-cent-un-milliard-neuf-cent-trente-huit-million-cent-quatre~1264301938000104 -moins soixante~-60 -quarante-six mille six cent soixante-quatre~46664 -quarante-six-mille-six-cent-soixante-quatre~46664 -soixante~60 -zéro~zéro -un~un -une~une -deux~deux -neuf~neuf -dix~10 -onze~11 -douze~12 -treize~13 -quatorze~14 -quinze~15 -seize~16 -dix-sept~17 -dix-huit~18 -vingt~20 -trente~30 -quarante~40 -cinquante~50 -soixante~60 -soixante-dix~70 -septante~70 -quatre-vingts~80 -huitante~80 -quatre-vingt-dix~90 -deux million dix~2000010 -deux-million-dix~2000010 -mille treize~1013 -mille-treize~1013 -mille un~1001 -mille-un~1001 -mille cent~1100 -mille-cent~1100 -onze cents~1100 -onze-cents~1100 -dix-huit mille treize~18013 -dix-huit-mille-treize~18013 -mille vingt-six~1026 -mille-vingt-six~1026 -mille cent vingt-six~1126 -mille-cent-vingt-six~1126 -onze cent vingt-six~1126 -onze-cent-vingt-six~1126 -dix-huit million quatre cent cinquante mille neuf cent quatre-vingt-dix~18450990 -dix-huit-million-quatre-cent-cinquante-mille-neuf-cent-quatre-vingt-dix~18450990 -dix-huit-million-quatre-cent-cinquante-mille-neuf-cent-nonante~18450990 -dix-huit mille huit cent quatre-vingts~18880 -dix-huit-mille-huit-cent-quatre-vingts~18880 -dix-huit mille huit cent huitante~18880 -dix-huit-mille-huit-cent-huitante~18880 \ No newline at end of file diff --git a/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_date.txt b/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_date.txt deleted file mode 100644 index b31c11c19ab9..000000000000 --- a/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_date.txt +++ /dev/null @@ -1,6 +0,0 @@ -vingt-quatre juillet deux-mille-treize~24 juillet 2013 -vingt-quatre juillet~24 juillet -quatorze janvier~14 janvier -premier janvier~1ᵉʳ janvier -trente juin~30 juin -dix-huit mai dix-neuf cent trente~18 mai 1930 \ No newline at end of file diff --git a/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_decimal.txt b/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_decimal.txt deleted file mode 100644 index 6e14ac07e58b..000000000000 --- a/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_decimal.txt +++ /dev/null @@ -1,15 +0,0 @@ -zéro virgule deux million~0,2 million -dix-huit milliards~18 milliards -quatre cent soixante millions~460 millions -quatre-cent-soixante millions~460 millions -quatre-cent-soixante-millions~460 millions -cent vingt millions~120 millions -cent-vingt-millions~120 millions -cent vingt millions~120 millions -dix billions~10 billions -dix-billions~10 billions -moins soixante virgule deux quatre zéro zéro~-60,240 0 -huit cent dix-huit virgule trois zéro trois~818,303 -huit-cent-dix-huit virgule trois zéro trois~818,303 -huit-cent-dix-huit virgule trente trois~818,303 -mille-huit-cent-dix-huit virgule trois zéro trois trois quatre~1 818,303 34 \ No newline at end of file diff --git a/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_electronic.txt b/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_electronic.txt deleted file mode 100644 index f70075b1896f..000000000000 --- a/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_electronic.txt +++ /dev/null @@ -1,10 +0,0 @@ -a point b c arobase g mail point com~a.bc@gmail.com -a point b c at g mail point com~a.bc@gmail.com -c d f at a b c point e d u~cdf@abc.edu -a b c at g mail point a b c~abc@gmail.abc -a b c arobase g mail point a b c~abc@gmail.abc -a b c at a b c point com~abc@abc.com -a s d f un deux trois at a b c point com~asdf123@abc.com -a un b deux arobase a b c point com~a1b2@abc.com -a b trois point s d d point trois at g mail point com~ab3.sdd.3@gmail.com -a b trois point s d d point trois arobase g mail point com~ab3.sdd.3@gmail.com \ No newline at end of file diff --git a/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_fraction.txt b/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_fraction.txt deleted file mode 100644 index fbaed97b3ce2..000000000000 --- a/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_fraction.txt +++ /dev/null @@ -1,30 +0,0 @@ -demi~1/2 -un tiers~1/3 -un quart~1/4 -un cinquième~1/5 -un sixième~1/6 -un septième~1/7 -un huitième~1/8 -deux neuvième~2/9 -un et demi~1 1/2 -un dixième~1/10 -un onzième~1/11 -un douzième~1/12 -un treizième~1/13 -un quatrième~1/4 -un quatorzième~1/14 -un quinzième~1/15 -un seizième~1/16 -un dix-septième~1/17 -un dix-huitième~1/18 -un dix-neuvième~1/19 -un vingtième~1/20 -un trentième~1/30 -un quarantième~1/40 -un cinquantième~1/50 -un soixantième~1/60 -un soixante-dixième~1/70 -un quatre-vingtième~1/80 -un quatre-vingt-dixième~1/90 -un centième~1/100 -quatre et deux quatrièmes~4 2/4 \ No newline at end of file diff --git a/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_measure.txt b/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_measure.txt deleted file mode 100644 index af99890be5be..000000000000 --- a/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_measure.txt +++ /dev/null @@ -1,15 +0,0 @@ -deux cents mètres~200 m -cinquante-six virgule trois par kilomètre carré~56,3 /km² -deux-cents kilomètres par heure~200 km/h -deux-cents kilomètres heure~200 km/h -quarante-deux-mille-deux-cent-cinquante-neuf par mètre carré~42 259 /m² -moins soixante-six kilogrammes~-66 kg -un virgule zéro zéro zéro zéro vingt-huit centimètre cube~1,000 028 cm³ -cinquante minutes~50 min -deux mètres cubes~2 m³ -quatre-vingt-dix grammes~90 g -quatre-cent-quarante millilitres~440 ml -trois cents micromètres~300 µm -soixante-cinq kilomètres carrés~65 km² -deux kilomètres par heure~2 km/h -soixante virgule vingt-quatre zéro zéro kilogrammes~60,240 0 kg \ No newline at end of file diff --git a/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_money.txt b/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_money.txt deleted file mode 100644 index 9d67e8f05d76..000000000000 --- a/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_money.txt +++ /dev/null @@ -1,22 +0,0 @@ -deux dollars~2 $ -un centime~0,01 € -vingt centimes~0,20 € -vingt-deux centimes~0,22 € -deux dollars vingt~2,20 $ -deux euros et vingt centimes~2,20 € -vingt euros~20 € -un franc suisse~1 CHF -vingt euro cinq~20,05 € -un euro~1 € -deux euro~2 € -cinq euro et soixante~5,60 € -cinquante centimes~0,50 € -quatre-vingt mille won~80 000 ₩ -quatre-vingt-mille won~80 000 ₩ -quatre-vingt-millions de wons~80 millions de wons -trois livre~3 £ -trois pence~0,03 £ -zéro euro~0 € -zéro euro quatre-vingt~0,80 € -deux-millions de dollars~2 millions de dollars -quatre virgule quatre-vingt milliards d'euros~4,80 milliards d'euros \ No newline at end of file diff --git a/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_ordinal.txt b/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_ordinal.txt deleted file mode 100644 index 5d5c8efc7987..000000000000 --- a/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_ordinal.txt +++ /dev/null @@ -1,23 +0,0 @@ -centième~100ᵉ -centièmes~100ᵉˢ -vingt-cinq-mille-cent-onzième~25111ᵉ -première~1ʳᵉ -premières~1ʳᵉˢ -premier~1ᵉʳ -premiers~1ᵉʳˢ -second~2ᵈ -seconds~2ᵈˢ -seconde~2ᵈᵉ -secondes~2ᵈᵉˢ -deuxième~2ᵉ -troisième~3ᵉ -quatrième~4ᵉ -onzièmes~11ᵉˢ -treizième~13ᵉ -vingt-et-unième~21ᵉ -vingt-troisièmes~23ᵉˢ -cent-onzième~111ᵉ -cent onzième~111ᵉ -millième~1000ᵉ -dix-neuvième siècle~XIXᵉ siècle -vingtième siècle~XXᵉ siècle \ No newline at end of file diff --git a/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_telephone.txt b/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_telephone.txt deleted file mode 100644 index 764bedda74ee..000000000000 --- a/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_telephone.txt +++ /dev/null @@ -1,4 +0,0 @@ -zéro deux douze trente-deux trente trente~02 12 32 30 30 -zéro deux une deux trois deux trois zéro trois zéro~02 12 32 30 30 -deux douze trente-deux trente trente~02 12 32 30 30 -deux une deux trois deux trois zéro trois zéro~02 12 32 30 30 \ No newline at end of file diff --git a/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_time.txt b/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_time.txt deleted file mode 100644 index a8381312406e..000000000000 --- a/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_time.txt +++ /dev/null @@ -1,18 +0,0 @@ -huit heures~8 h -huit heures du matin~8 h -huit heures du soir~20 h -minuit~0 h -deux heures de l'après-midi~14 h -quatorze heures~14 h -midi~12 h -dix-huit heures~18 h -huit heures sept~8 h 07 -minuit dix-sept~0 h 17 -douze heures~12 h -onze heures et demie~11 h 30 -midi moins le quart~11 h 45 -onze heures et trois quarts~11 h 45 -midi moins trois~11 h 57 -onze heures cinquante-sept~11 h 57 -onze heures trente-huit~11 h 38 -midi moins vingt-deux~11 h 38 \ No newline at end of file diff --git a/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_whitelist.txt b/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_whitelist.txt deleted file mode 100644 index 8535bfd461cb..000000000000 --- a/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_whitelist.txt +++ /dev/null @@ -1,8 +0,0 @@ -docteur~Dʳ -docteures~Dʳᵉˢ -monsieur~M. -messieurs~MM. -madame~Mᵐᵉ -mesdames~Mᵐᵉˢ -mademoiselle~Mˡˡᵉ -mademoiselles~Mˡˡᵉˢ \ No newline at end of file diff --git a/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_word.txt b/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_word.txt deleted file mode 100644 index 66f3445a034d..000000000000 --- a/tests/nemo_text_processing/fr/data_inverse_text_normalization/test_cases_word.txt +++ /dev/null @@ -1,49 +0,0 @@ -~ -yahoo!~yahoo! -vingt!~20 ! -x ~x -—~— -aaa~aaa -aabach~aabach -aabenraa~aabenraa -aabye~aabye -aaccessed~aaccessed -aach~aach -aachen's~aachen's -aadri~aadri -aafia~aafia -aagaard~aagaard -aagadu~aagadu -aagard~aagard -aagathadi~aagathadi -aaghart's~aaghart's -aagnes~aagnes -aagomoni~aagomoni -aagon~aagon -aagoo~aagoo -aagot~aagot -aahar~aahar -aahh~aahh -aahperd~aahperd -aaibinterstate~aaibinterstate -aajab~aajab -aakasa~aakasa -aakervik~aakervik -aakirkeby~aakirkeby -aalam~aalam -aalbaek~aalbaek -aaldiu~aaldiu -aalem~aalem -a'ali~a'ali -aalilaassamthey~aalilaassamthey -aalin~aalin -aaliyan~aaliyan -aaliyan's~aaliyan's -aamadu~aamadu -aamara~aamara -aambala~aambala -aamera~aamera -aamer's~aamer's -aamina~aamina -aaminah~aaminah -aamjiwnaang~aamjiwnaang diff --git a/tests/nemo_text_processing/fr/test_cardinal.py b/tests/nemo_text_processing/fr/test_cardinal.py deleted file mode 100644 index 3da85485d5c0..000000000000 --- a/tests/nemo_text_processing/fr/test_cardinal.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestCardinal: - - inverse_normalizer = ( - InverseNormalizer(lang='fr', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('fr/data_inverse_text_normalization/test_cases_cardinal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/fr/test_date.py b/tests/nemo_text_processing/fr/test_date.py deleted file mode 100644 index 2cb1120c0656..000000000000 --- a/tests/nemo_text_processing/fr/test_date.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestDate: - inverse_normalizer = ( - InverseNormalizer(lang='fr', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('fr/data_inverse_text_normalization/test_cases_date.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/fr/test_decimal.py b/tests/nemo_text_processing/fr/test_decimal.py deleted file mode 100644 index 0adbbc078306..000000000000 --- a/tests/nemo_text_processing/fr/test_decimal.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestDecimal: - inverse_normalizer = ( - InverseNormalizer(lang='fr', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('fr/data_inverse_text_normalization/test_cases_decimal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/fr/test_electronic.py b/tests/nemo_text_processing/fr/test_electronic.py deleted file mode 100644 index 8ccb76e1829f..000000000000 --- a/tests/nemo_text_processing/fr/test_electronic.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestElectronic: - inverse_normalizer = ( - InverseNormalizer(lang='fr', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('fr/data_inverse_text_normalization/test_cases_electronic.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/fr/test_fraction.py b/tests/nemo_text_processing/fr/test_fraction.py deleted file mode 100644 index cf4d132945c0..000000000000 --- a/tests/nemo_text_processing/fr/test_fraction.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestFraction: - inverse_normalizer = ( - InverseNormalizer(lang='fr', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('fr/data_inverse_text_normalization/test_cases_fraction.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/fr/test_measure.py b/tests/nemo_text_processing/fr/test_measure.py deleted file mode 100644 index 68ec945aee6d..000000000000 --- a/tests/nemo_text_processing/fr/test_measure.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestMeasure: - inverse_normalizer = ( - InverseNormalizer(lang='fr', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('fr/data_inverse_text_normalization/test_cases_measure.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/fr/test_money.py b/tests/nemo_text_processing/fr/test_money.py deleted file mode 100644 index 0b4ee9385a35..000000000000 --- a/tests/nemo_text_processing/fr/test_money.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestMoney: - inverse_normalizer = ( - InverseNormalizer(lang='fr', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('fr/data_inverse_text_normalization/test_cases_money.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/fr/test_ordinal.py b/tests/nemo_text_processing/fr/test_ordinal.py deleted file mode 100644 index ffe49df17bde..000000000000 --- a/tests/nemo_text_processing/fr/test_ordinal.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestOrdinal: - inverse_normalizer = ( - InverseNormalizer(lang='fr', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('fr/data_inverse_text_normalization/test_cases_ordinal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/fr/test_sparrowhawk_inverse_text_normalization.sh b/tests/nemo_text_processing/fr/test_sparrowhawk_inverse_text_normalization.sh deleted file mode 100644 index 4ca12af7f278..000000000000 --- a/tests/nemo_text_processing/fr/test_sparrowhawk_inverse_text_normalization.sh +++ /dev/null @@ -1,84 +0,0 @@ -#! /bin/sh - -PROJECT_DIR=/workspace/tests - -runtest () { - input=$1 - cd /workspace/sparrowhawk/documentation/grammars - - # read test file - while read testcase; do - IFS='~' read spoken written <<< $testcase - denorm_pred=$(echo $spoken | normalizer_main --config=sparrowhawk_configuration.ascii_proto 2>&1 | tail -n 1) - - # trim white space - written="$(echo -e "${written}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - denorm_pred="$(echo -e "${denorm_pred}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - - # input expected actual - assertEquals "$spoken" "$written" "$denorm_pred" - done < "$input" -} - -testITNCardinal() { - input=$PROJECT_DIR/fr/data_inverse_text_normalization/test_cases_cardinal.txt - runtest $input -} - -testITNDate() { - input=$PROJECT_DIR/fr/data_inverse_text_normalization/test_cases_date.txt - runtest $input -} - -testITNDecimal() { - input=$PROJECT_DIR/fr/data_inverse_text_normalization/test_cases_decimal.txt - runtest $input -} - -testITNOrdinal() { - input=$PROJECT_DIR/fr/data_inverse_text_normalization/test_cases_ordinal.txt - runtest $input -} - -testITNFraction() { - input=$PROJECT_DIR/fr/data_inverse_text_normalization/test_cases_fraction.txt - runtest $input -} - -testITNTime() { - input=$PROJECT_DIR/fr/data_inverse_text_normalization/test_cases_time.txt - runtest $input -} - -testITNMeasure() { - input=$PROJECT_DIR/fr/data_inverse_text_normalization/test_cases_measure.txt - runtest $input -} - -testITNMoney() { - input=$PROJECT_DIR/fr/data_inverse_text_normalization/test_cases_money.txt - runtest $input -} - -testITNWhitelist() { - input=$PROJECT_DIR/fr/data_inverse_text_normalization/test_cases_whitelist.txt - runtest $input -} - -testITNTelephone() { - input=$PROJECT_DIR/fr/data_inverse_text_normalization/test_cases_telephone.txt - runtest $input -} - -testITNElectronic() { - input=$PROJECT_DIR/fr/data_inverse_text_normalization/test_cases_electronic.txt - runtest $input -} - -testITNWord() { - input=$PROJECT_DIR/fr/data_inverse_text_normalization/test_cases_word.txt - runtest $input -} - -# Load shUnit2 -. $PROJECT_DIR/../shunit2/shunit2 diff --git a/tests/nemo_text_processing/fr/test_telephone.py b/tests/nemo_text_processing/fr/test_telephone.py deleted file mode 100644 index 10bc6051d0c5..000000000000 --- a/tests/nemo_text_processing/fr/test_telephone.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestTelephone: - inverse_normalizer = ( - InverseNormalizer(lang='fr', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('fr/data_inverse_text_normalization/test_cases_telephone.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/fr/test_time.py b/tests/nemo_text_processing/fr/test_time.py deleted file mode 100644 index 2b2cc356a3f2..000000000000 --- a/tests/nemo_text_processing/fr/test_time.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestTime: - inverse_normalizer = ( - InverseNormalizer(lang='fr', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('fr/data_inverse_text_normalization/test_cases_time.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/fr/test_whitelist.py b/tests/nemo_text_processing/fr/test_whitelist.py deleted file mode 100644 index ec8e8d57b94d..000000000000 --- a/tests/nemo_text_processing/fr/test_whitelist.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestWhitelist: - inverse_normalizer = ( - InverseNormalizer(lang='fr', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('fr/data_inverse_text_normalization/test_cases_whitelist.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/fr/test_word.py b/tests/nemo_text_processing/fr/test_word.py deleted file mode 100644 index 19ada867b030..000000000000 --- a/tests/nemo_text_processing/fr/test_word.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestWord: - inverse_normalizer = ( - InverseNormalizer(lang='fr', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('fr/data_inverse_text_normalization/test_cases_word.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/pt/__init__.py b/tests/nemo_text_processing/pt/__init__.py deleted file mode 100644 index 2db92b257416..000000000000 --- a/tests/nemo_text_processing/pt/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_cardinal.txt b/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_cardinal.txt deleted file mode 100755 index 7219efacd9fb..000000000000 --- a/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_cardinal.txt +++ /dev/null @@ -1,70 +0,0 @@ -(non-ideal case) entre dezesseis mil e dezoito e mil~(non-ideal case) entre 16018 e 1000 -duzentos e cinquenta e um~251 -novecentos e noventa e nove milhões novecentos e noventa e nove mil novecentos e noventa e nove~999999999 -zero~zero -um~um -uma~uma -dois~dois -nove~nove -dez~10 -onze~11 -, um~, um -, uma~, uma -, dez~, 10 -, onze~, 11 -vinte três~23 -vinte e três~23 -trinta e três~33 -menos vinte e três~-23 -cem~100 -cento e um~101 -cento e uma~101 -cento e dez~110 -cento e onze~111 -cento e vinte três~123 -cento e vinte e três~123 -cento e cinquenta~150 -duzentos~200 -duzentos e um~201 -mil e um~1001 -mil e uma~1001 -dois mil e vinte e dois~2022 -nove trilhões setecentos e oitenta e nove bilhões trezentos e oitenta e dois milhões quinhentos e trinta e seis mil cento e trinta~9789382536130 -duzentos e cinquenta e quatro~254 -cento e quarenta e sete mil quatrocentos e cinquenta e um~147451 -cento e quarenta e sete mil quatrocentos e cinquenta e uma~147451 -um milhão cento e cinquenta e seis mil cento e setenta e três~1156173 -um bilhão quinhentos e noventa e três milhões e setenta e dois mil novecentos e sessenta e um~1593072961 -noventa e sete quatrilhões oitocentos e oito trilhões duzentos e sessenta e quatro bilhões setecentos e setenta e dois milhões setecentos e noventa e dois mil e cinco~97808264772792005 -dezessete sextilhões oitocentos e cinquenta e cinco quintilhões e trinta e seis quatrilhões seiscentos e cinquenta e sete trilhões e sete bilhões quinhentos e noventa e seis milhões cento e dez mil novecentos e quarenta e nove~17855036657007596110949 -dez quatrilhões e dez trilhões e dez milhões e cem mil e dez~10010000010100010 -menos vinte e cinco mil e trinta e sete~-25037 -um quatrilhão duzentos e sessenta e quatro trilhões trezentos e um bilhões novecentos e trinta e oito milhões cento e quatro~1264301938000104 -menos sessenta~-60 -quarenta e seis mil seiscentos e sessenta e quatro~46664 -sessenta~60 -dois milhões e três~2000003 -mil e treze~1013 -mil e cem~1100 -mil e vinte e seis~1026 -mil cento e vinte e seis~1126 -dezoito milhões quatrocentos e cinquenta mil novecentos e noventa~18450990 -dezoito milhões novecentos e quarenta mil setecentos e vinte e dois~18940722 -dezoito milhões seiscentos e noventa mil novecentos e dezesseis~18690916 -dezoito mil oitocentos e oitenta~18880 -um bilhão e um~1000000001 -um bilhão e uma~1000000001 -um bilhão cento e um~1000000101 -um bilhão cento e uma~1000000101 -um bilhão e mil cento e um~1000001101 -um bilhão e mil cento e uma~1000001101 -um bilhão e dez mil cento e um~1000010101 -um bilhão e dez mil cento e uma~1000010101 -um bilhão e um milhão e dez mil cento e um~1001010101 -um bilhão e um milhão e dez mil cento e uma~1001010101 -dois bilhões e cinquenta e dois~2000000052 -muitos milhões~muitos milhões -um quatrilhão e um~1000000000000001 -um quatrilhão e uma~1000000000000001 -um sextilhão e um~1000000000000000000001 -um sextilhão e uma~1000000000000000000001 \ No newline at end of file diff --git a/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_date.txt b/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_date.txt deleted file mode 100644 index 6aa2c03127e6..000000000000 --- a/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_date.txt +++ /dev/null @@ -1,15 +0,0 @@ -primeiro de janeiro~01 de janeiro -um de janeiro~01 de janeiro -cinco de março~05 de março -em primeiro de dezembro~em 01 de dezembro -domingo vinte e seis de outubro~domingo 26 de outubro -trinta e um de dezembro de mil novecentos e oitenta e oito~31 de dezembro de 1988 -vinte e sete de agosto de dois mil e quinze~27 de agosto de 2015 -primeiro de setembro de dois mil e vinte e dois~01 de setembro de 2022 -um do um~01/01 -dois do dois~02/02 -trinta e um do doze~31/12 -um do um de mil novecentos e oitenta e sete~01/01/1987 -dez do oito de dois mil~10/08/2000 -doze do três de dois mil e dois~12/03/2002 -trinta e um do doze de dois mil e trinta e sete~31/12/2037 \ No newline at end of file diff --git a/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_decimal.txt b/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_decimal.txt deleted file mode 100755 index bdafdc642442..000000000000 --- a/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_decimal.txt +++ /dev/null @@ -1,26 +0,0 @@ -um vírgula dois seis~1,26 -menos um vírgula dois seis~-1,26 -um vírgula vinte e seis~1,26 -zero vírgula vinte e seis~0,26 -zero vírgula vinte seis~0,26 -três vírgula cento e quarenta e um~3,141 -três vírgula zero cento e quarenta e um~3,0141 -três vírgula cento e quarenta e um cinquenta e nove~3,14159 -três vírgula quatorze cento e cinquenta e nove~3,14159 -três vírgula quatorze quinze noventa e dois sessenta e cinco trinta e cinco~3,1415926535 -três vírgula quatorze quinze zero noventa e dois sessenta e cinco trinta e cinco~3,14150926535 -três vírgula quatorze quinze zero novecentos e vinte e seis zero quinhentos e trinta e cinco~3,141509260535 -quatrocentos milhões~400 milhões -um ponto trinta e três~1.33 -um ponto trinta e três milhões~1.33 milhões -zero vírgula seis milhões~0,6 milhões -mil oitocentos e vinte e quatro milhões~1824 milhões -ponto dois seis~.26 -um milhão~1 milhão -dois milhões~2 milhões -um bilhão~1 bilhão -dois bilhões~2 bilhões -um trilhão~1 trilhão -dois trilhões~2 trilhões -um quatrilhão~1 quatrilhão -dois quatrilhões~2 quatrilhões diff --git a/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_electronic.txt b/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_electronic.txt deleted file mode 100644 index 70a5319756c0..000000000000 --- a/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_electronic.txt +++ /dev/null @@ -1,13 +0,0 @@ -a ponto b c arroba g mail ponto com~a.bc@gmail.com -c d f arroba a b c ponto e d u~cdf@abc.edu -a b c arroba g mail ponto a b c~abc@gmail.abc -a b c arroba a b c ponto com~abc@abc.com -a s d f um dois três arroba a b c ponto com~asdf123@abc.com -a um b dois arroba a b c ponto com~a1b2@abc.com -a b três ponto s d d ponto três arroba g mail ponto com~ab3.sdd.3@gmail.com -agá tê tê pê ésse dois pontos barra barra dáblio dáblio dáblio ponto n vidia ponto com~https://www.nvidia.com -dáblio dáblio dáblio ponto n vidia ponto com~www.nvidia.com -dáblio dáblio dáblio ponto nvidia ponto com~www.nvidia.com -w w w ponto nvidia ponto com~www.nvidia.com -dáblio dáblio dáblio ponto a b c ponto es barra e f g~www.abc.es/efg -dáblio dáblio dáblio ponto a b c ponto br~www.abc.br \ No newline at end of file diff --git a/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_measure.txt b/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_measure.txt deleted file mode 100755 index 7e3c8ca8b90c..000000000000 --- a/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_measure.txt +++ /dev/null @@ -1,12 +0,0 @@ -duzentos metros~200 m -duzentos e quarenta e cinco milhas por hora~245 mph -duzentos e quarenta e cinco quilômetros por hora~245 kph -duzentos e quarenta e cinco metros por segundo~245 m/s -dois quilos~2 kg -sessenta vírgula dois quatro zero zero quilogramas~60,2400 kg -menos sessenta vírgula dois quatro zero zero quilogramas~-60,2400 kg -oito vírgula cinco dois por cento~8,52 % -menos oito vírgula cinco dois por cento~-8,52 % -um por cento~1 % -três centímetros~3 cm -cinco litros~5 l \ No newline at end of file diff --git a/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_money.txt b/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_money.txt deleted file mode 100755 index 88d7e0e7db07..000000000000 --- a/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_money.txt +++ /dev/null @@ -1,25 +0,0 @@ -doze dólares e cinco centavos~$ 12,05 -doze dólares~$ 12 -setenta e cinco dólares e sessenta e três~$ 75,63 -setenta e cinco dólares e sessenta e três centavos~$ 75,63 -setenta e cinco dólares com sessenta e três centavos~$ 75,63 -setenta e cinco dólares com sessenta e três~$ 75,63 -vinte e nove dólares e cinquenta centavos~$ 29,50 -um dólar~$ 1 -um real~R$ 1 -cem reais~R$ 100 -duzentos reais~R$ 200 -cento e noventa e nove reais e noventa e nove centavos~R$ 199,99 -um real e um centavo~R$ 1,01 -vinte centavos~R$ 0,20 -vinte e cinco centavos~R$ 0,25 -doze euros e cinco centavos~€ 12,05 -doze dólares americanos e cinco centavos~US$ 12,05 -duas libras esterlinas~£ 2 -doze dólares e cinco centavos~$ 12,05 -pagamos cento e quinze reais por uma bala~pagamos R$ 115 por uma bala -quinze mil reais~R$ 15000 -dois bilhões de reais~R$ 2 bilhões -dois milhões de reais~R$ 2 milhões -três vírgula sete milhões de reais~R$ 3,7 milhões -quatro ponto oito milhões de dólares~$ 4.8 milhões diff --git a/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_ordinal.txt b/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_ordinal.txt deleted file mode 100755 index 0ae43e978c8b..000000000000 --- a/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_ordinal.txt +++ /dev/null @@ -1,19 +0,0 @@ -primeiro~primeiro -terceira~terceira -nono~nono -nona~nona -décimo~10º -décima~10ª -décimo primeiro~11º -décima primeira~11ª -(technically ungrammatical) décima primeira~(technically ungrammatical) 11ª -(technically ungrammatical) décima primeira casa~(technically ungrammatical) 11ª casa -décimo terceiro~13º -vigésimo primeiro~21º -vigésima primeira~21ª -(technically ungrammatical) vigésimo primeira~(technically ungrammatical) 21ª -vigésimo segundo~22º -vigésima segunda~22ª -vigésimo terceiro~23º -centésimo décimo primeiro~111º -centésimo trigésimo quarto~134º diff --git a/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_telephone.txt b/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_telephone.txt deleted file mode 100755 index 04e2875b5290..000000000000 --- a/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_telephone.txt +++ /dev/null @@ -1,27 +0,0 @@ -um dois três quatro cinco seis sete oito~1234-5678 -um dois três quatro cinco seis sete oito telefone~1234-5678 telefone -um dois três quatro cinco meia sete oito~1234-5678 -um dois três quatro cinco meia sete oito telefone~1234-5678 telefone -um dois três quatro cinco seis sete oito nove~12345-6789 -um dois três quatro cinco seis sete oito nove telefone~12345-6789 telefone -quatro cinco um dois três quatro cinco seis sete oito~(45) 1234-5678 -quatro cinco um dois três quatro cinco seis sete oito telefone~(45) 1234-5678 telefone -quatro cinco um dois três quatro cinco seis sete oito nove~(45) 12345-6789 -quatro cinco um dois três quatro cinco seis sete oito nove telefone~(45) 12345-6789 telefone -vinte e sete vinte e oito trinta e sete trinta e oito~2728-3738 -vinte e sete vinte e oito trinta e sete trinta e oito telefone~2728-3738 telefone -um vinte e sete vinte e oito trinta e sete trinta e oito~12728-3738 -um vinte e sete vinte e oito trinta e sete trinta e oito telefone~12728-3738 telefone -nove oito sete seis cinquenta e quatro zero zero~9876-5400 -nove oito sete seis cinquenta e quatro zero um~9876-5401 -noventa e oito setenta e seis zero zero trinta e cinco~9876-0035 -noventa e oito setenta e seis zero zero trinta~9876-0030 -nove noventa e oito setenta e seis zero zero trinta e um~99876-0031 -nove noventa e oito setenta e seis zero zero trinta~99876-0030 -dois três nove noventa e oito setenta e seis zero zero trinta e um~(23) 99876-0031 -dois três nove noventa e oito setenta e seis zero zero trinta~(23) 99876-0030 -vinte e três nove noventa e oito setenta e seis zero zero trinta e um~(23) 99876-0031 -vinte e três nove noventa e oito setenta e seis zero zero trinta~(23) 99876-0030 -vinte e três nove noventa e oito setenta e seis zero meia trinta e um~(23) 99876-0631 -vinte e três nove noventa e oito setenta e seis zero meia trinta~(23) 99876-0630 -vinte três nove noventa e oito setenta e seis zero meia trinta~(23) 99876-0630 \ No newline at end of file diff --git a/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_time.txt b/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_time.txt deleted file mode 100755 index 7aef50d2d489..000000000000 --- a/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_time.txt +++ /dev/null @@ -1,19 +0,0 @@ -às quinze pro meio dia~às 11:45 -às quinze pra meia noite~às 23:45 -às quinze para a meia noite~às 23:45 -às quinze pras duas da tarde~às 1:45 da tarde -às dez pras duas da madrugada~às 1:50 da madrugada -às quinze pras duas da tarde~às 1:45 da tarde -chegaram às quinze pras duas da tarde~chegaram às 1:45 da tarde -ao meio dia~ao meio dia -ao meio dia e meia hora~ao 12:30 -ao meio dia e meia~ao 12:30 -ao meio dia e meio~ao 12:30 -meia noite~meia noite -à meia noite~à meia noite -à meia noite e quinze~à 0:15 -meia noite e meia~0:30 -à uma e trinta~à 1:30 -às onze e trinta~às 11:30 -às três horas e trinta minutos~às 3:30 -às quinze horas e quarenta minutos~às 15:40 diff --git a/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_whitelist.txt b/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_whitelist.txt deleted file mode 100755 index 798df8b9bdfd..000000000000 --- a/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_whitelist.txt +++ /dev/null @@ -1,6 +0,0 @@ -primeira segunda feira~primeira segunda-feira -primeira segunda-feira~primeira segunda-feira -terça feira~terça-feira -quarta feira~quarta-feira -quinta feira~quinta-feira -sexta feira~sexta-feira \ No newline at end of file diff --git a/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_word.txt b/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_word.txt deleted file mode 100755 index fdd32f2ea4f2..000000000000 --- a/tests/nemo_text_processing/pt/data_inverse_text_normalization/test_cases_word.txt +++ /dev/null @@ -1,49 +0,0 @@ -~ -yahoo!~yahoo! -vinte!~20 ! -x ~x -—~— -aaa~aaa -aabach~aabach -aabenraa~aabenraa -aabye~aabye -aaccessed~aaccessed -aach~aach -aachen's~aachen's -aadri~aadri -aafia~aafia -aagaard~aagaard -aagadu~aagadu -aagard~aagard -aagathadi~aagathadi -aaghart's~aaghart's -aagnes~aagnes -aagomoni~aagomoni -aagon~aagon -aagoo~aagoo -aagot~aagot -aahar~aahar -aahh~aahh -aahperd~aahperd -aaibinterstate~aaibinterstate -aajab~aajab -aakasa~aakasa -aakervik~aakervik -aakirkeby~aakirkeby -aalam~aalam -aalbaek~aalbaek -aaldiu~aaldiu -aalem~aalem -a'ali~a'ali -aalilaassamthey~aalilaassamthey -aalin~aalin -aaliyan~aaliyan -aaliyan's~aaliyan's -aamadu~aamadu -aamara~aamara -aambala~aambala -aamera~aamera -aamer's~aamer's -aamina~aamina -aaminah~aaminah -aamjiwnaang~aamjiwnaang diff --git a/tests/nemo_text_processing/pt/test_cardinal.py b/tests/nemo_text_processing/pt/test_cardinal.py deleted file mode 100644 index 9a16a6b3477d..000000000000 --- a/tests/nemo_text_processing/pt/test_cardinal.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestCardinal: - - inverse_normalizer = ( - InverseNormalizer(lang='pt', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('pt/data_inverse_text_normalization/test_cases_cardinal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/pt/test_date.py b/tests/nemo_text_processing/pt/test_date.py deleted file mode 100644 index e7783819585c..000000000000 --- a/tests/nemo_text_processing/pt/test_date.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestDate: - inverse_normalizer = ( - InverseNormalizer(lang='pt', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('pt/data_inverse_text_normalization/test_cases_date.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/pt/test_decimal.py b/tests/nemo_text_processing/pt/test_decimal.py deleted file mode 100644 index 48daab819a38..000000000000 --- a/tests/nemo_text_processing/pt/test_decimal.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestDecimal: - inverse_normalizer = ( - InverseNormalizer(lang='pt', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('pt/data_inverse_text_normalization/test_cases_decimal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/pt/test_electronic.py b/tests/nemo_text_processing/pt/test_electronic.py deleted file mode 100644 index cce372cffded..000000000000 --- a/tests/nemo_text_processing/pt/test_electronic.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestElectronic: - inverse_normalizer = ( - InverseNormalizer(lang='pt', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('pt/data_inverse_text_normalization/test_cases_electronic.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/pt/test_measure.py b/tests/nemo_text_processing/pt/test_measure.py deleted file mode 100644 index b56fca76de6a..000000000000 --- a/tests/nemo_text_processing/pt/test_measure.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestMeasure: - inverse_normalizer = ( - InverseNormalizer(lang='pt', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('pt/data_inverse_text_normalization/test_cases_measure.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/pt/test_money.py b/tests/nemo_text_processing/pt/test_money.py deleted file mode 100644 index 0d372f0f55e6..000000000000 --- a/tests/nemo_text_processing/pt/test_money.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestMoney: - inverse_normalizer = ( - InverseNormalizer(lang='pt', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('pt/data_inverse_text_normalization/test_cases_money.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/pt/test_ordinal.py b/tests/nemo_text_processing/pt/test_ordinal.py deleted file mode 100644 index 2ef6a89c87e3..000000000000 --- a/tests/nemo_text_processing/pt/test_ordinal.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestOrdinal: - inverse_normalizer = ( - InverseNormalizer(lang='pt', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('pt/data_inverse_text_normalization/test_cases_ordinal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/pt/test_sparrowhawk_inverse_text_normalization.sh b/tests/nemo_text_processing/pt/test_sparrowhawk_inverse_text_normalization.sh deleted file mode 100755 index 74d8ddafdfc6..000000000000 --- a/tests/nemo_text_processing/pt/test_sparrowhawk_inverse_text_normalization.sh +++ /dev/null @@ -1,84 +0,0 @@ -#! /bin/sh - -PROJECT_DIR=/workspace/tests - -runtest () { - input=$1 - cd /workspace/sparrowhawk/documentation/grammars - - # read test file - while read testcase; do - IFS='~' read spoken written <<< $testcase - denorm_pred=$(echo $spoken | normalizer_main --config=sparrowhawk_configuration.ascii_proto 2>&1 | tail -n 1) - - # trim white space - written="$(echo -e "${written}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - denorm_pred="$(echo -e "${denorm_pred}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - - # input expected actual - assertEquals "$spoken" "$written" "$denorm_pred" - done < "$input" -} - -testITNCardinal() { - input=$PROJECT_DIR/pt/data_inverse_text_normalization/test_cases_cardinal.txt - runtest $input -} - -testITNDate() { - input=$PROJECT_DIR/pt/data_inverse_text_normalization/test_cases_date.txt - runtest $input -} - -testITNDecimal() { - input=$PROJECT_DIR/pt/data_inverse_text_normalization/test_cases_decimal.txt - runtest $input -} - -testITNOrdinal() { - input=$PROJECT_DIR/pt/data_inverse_text_normalization/test_cases_ordinal.txt - runtest $input -} - -#testITNFraction() { -# input=$PROJECT_DIR/pt/data_inverse_text_normalization/test_cases_fraction.txt -# runtest $input -#} - -testITNTime() { - input=$PROJECT_DIR/pt/data_inverse_text_normalization/test_cases_time.txt - runtest $input -} - -testITNMeasure() { - input=$PROJECT_DIR/pt/data_inverse_text_normalization/test_cases_measure.txt - runtest $input -} - -testITNMoney() { - input=$PROJECT_DIR/pt/data_inverse_text_normalization/test_cases_money.txt - runtest $input -} - -testITNWhitelist() { - input=$PROJECT_DIR/pt/data_inverse_text_normalization/test_cases_whitelist.txt - runtest $input -} - -testITNTelephone() { - input=$PROJECT_DIR/pt/data_inverse_text_normalization/test_cases_telephone.txt - runtest $input -} - -testITNElectronic() { - input=$PROJECT_DIR/pt/data_inverse_text_normalization/test_cases_electronic.txt - runtest $input -} - -testITNWord() { - input=$PROJECT_DIR/pt/data_inverse_text_normalization/test_cases_word.txt - runtest $input -} - -# Load shUnit2 -. $PROJECT_DIR/../shunit2/shunit2 diff --git a/tests/nemo_text_processing/pt/test_telephone.py b/tests/nemo_text_processing/pt/test_telephone.py deleted file mode 100644 index a2fecad525fe..000000000000 --- a/tests/nemo_text_processing/pt/test_telephone.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestTelephone: - inverse_normalizer = ( - InverseNormalizer(lang='pt', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('pt/data_inverse_text_normalization/test_cases_telephone.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/pt/test_time.py b/tests/nemo_text_processing/pt/test_time.py deleted file mode 100644 index 87f5bb2867a0..000000000000 --- a/tests/nemo_text_processing/pt/test_time.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestTime: - inverse_normalizer = ( - InverseNormalizer(lang='pt', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('pt/data_inverse_text_normalization/test_cases_time.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/pt/test_whitelist.py b/tests/nemo_text_processing/pt/test_whitelist.py deleted file mode 100644 index 978556c83a39..000000000000 --- a/tests/nemo_text_processing/pt/test_whitelist.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestWhitelist: - inverse_normalizer = ( - InverseNormalizer(lang='pt', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('pt/data_inverse_text_normalization/test_cases_whitelist.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/pt/test_word.py b/tests/nemo_text_processing/pt/test_word.py deleted file mode 100644 index b3ed35b32b52..000000000000 --- a/tests/nemo_text_processing/pt/test_word.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestWord: - inverse_normalizer = ( - InverseNormalizer(lang='pt', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('pt/data_inverse_text_normalization/test_cases_word.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/ru/__init__.py b/tests/nemo_text_processing/ru/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/tests/nemo_text_processing/ru/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_cardinal.txt b/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_cardinal.txt deleted file mode 100644 index 015ece7d1dea..000000000000 --- a/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_cardinal.txt +++ /dev/null @@ -1,30 +0,0 @@ -двести пятьдесят четыре~254 -сто сорок семь тысяч четыреста пятьдесят один~147451 -один миллион сто пятьдесят шесть тысяч сто семьдесят три~1156173 -один миллиард пятьсот девяносто три миллиона семьдесят две тысячи девятьсот шестьдесят один~1593072961 -минус двадцать пять тысяч тридцать семь~-25037 -минус шестьдесят~-60 -сорок шесть тысяч шестьсот шестьдесят четыре~46664 -шестьдесят~60 -ноль~ноль -я одна~я одна -девять~девять -два миллиона три~2000003 -одна тысяча тринадцать~1013 -одна тысяча один~1001 -одна тысяча сто~1100 -одна тысяча двадцать шесть~1026 -одна тысяча сто двадцать шесть~1126 -тысяча тринадцать~1013 -тысяча один~1001 -тысяча сто~1100 -тысяча двадцать шесть~1026 -тысяча сто двадцать шесть~1126 -одна тысяча~1000 -тысяча~1000 -восемнадцать миллионов четыреста пятьдесят тысяч девятьсот девяносто~18450990 -восемнадцать миллионов девятьсот сорок тысяч семьсот двадцать два~18940722 -восемнадцать миллионов шестьсот девяносто тысяч девятьсот шестнадцать~18690916 -восемнадцать тысяч восемьсот восемьдесят~18880 -два миллиона~2000000 -одна тысяча~1000 diff --git a/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_date.txt b/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_date.txt deleted file mode 100644 index 5bca2e0b6da1..000000000000 --- a/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_date.txt +++ /dev/null @@ -1,15 +0,0 @@ -двенадцатое декабря две тысячи шестого~12.12.2006 -первого января восемьдесят шестого~01.01.86 -второе марта тысяча девятьсот шестого~02.03.1906 -шестое марта тысяча шестого года~06.03.1006г. -двадцать третьего октября две тысячи второго~23.10.2002 -восемнадцатое мая~18.05 -ноль второго мая две тысячи девятого~02.05.2009 -ноль второго пятого две тысячи девятого года~02.05.2009г. -ноль второго ноль первого~02.01 -тридцать первого января ноль шестого~31.01.06 -в двухтысячных годах~в 2000гг. -в две тысячи девятом~в 2009 -первый день марта~первый день марта -двадцатого июня~20.06 -первое февраля~01.02 \ No newline at end of file diff --git a/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_decimal.txt b/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_decimal.txt deleted file mode 100644 index 29035aec84a5..000000000000 --- a/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_decimal.txt +++ /dev/null @@ -1,16 +0,0 @@ -ноль целых и одна десятая~0,1 -ноль целых и семьсот двадцать девять тысячных~0,729 -одной целой и трех десятых~1,3 -одна целая и две десятых~1,2 -три целых и две сотых~3,02 -ноль целых и три тысячных~0,003 -одна целая и две десятитысячных~1,0002 -ноль целых и семьсот двадцать четыре тысячных~0,724 -триста целых и две сотых~300,02 -ноль целых и девять десятых~0,9 -две целых три десятых~2,3 -две целых три сотых~2,03 -две целых три тысячных~2,003 -две целых три десятитысячных~2,0003 -минус две целых пять десятых~-2,5 -одна целая двадцать три сотых миллиарда~1,23 млрд \ No newline at end of file diff --git a/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_electronic.txt b/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_electronic.txt deleted file mode 100644 index 45c0c28e8cd1..000000000000 --- a/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_electronic.txt +++ /dev/null @@ -1,4 +0,0 @@ -эй точка би си собака джи мейл точка ком~a.bc@gmail.com -эй собака хот мейл точка ди и~a@hotmail.de -эй один два три тире ви собака эй би си точка ком~a123-v@abc.com -адрес эй дефис один точка би три нижнее подчеркивание си амперсанд тире ди собака джи эм эй четыре ай тире эл точка ком тест~адрес a-1.b3_c&-d@gma4i-l.com тест diff --git a/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_measure.txt b/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_measure.txt deleted file mode 100644 index fb11e21b7330..000000000000 --- a/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_measure.txt +++ /dev/null @@ -1,16 +0,0 @@ -двенадцать килограм~12 кг -одного килограма~1 кг -три целых две десятых литра~3,2 л -три целых две десятых миллиона литров~3,2 млн л -три километра в час~3 км/ч -двадцати килограммовый мешок~20 килограммовый мешок -четыре целых пять десятых килограммовый мешок~4,5 килограммовый мешок -тест одна целая две сотых~тест 1,02 -тест восемнадцать~тест 18 -пятнадцать рублей за квадратный метр~15 pуб./м² -пятнадцать за килограм~15/кг -пять градусов~5 ° -пять градусов фаренгейта~5 °F -пять градусов цельсия~5 °C -пять градусов по цельсию~5 °C -пять градусов по фаренгейту~5 °F diff --git a/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_money.txt b/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_money.txt deleted file mode 100644 index 913b437f9497..000000000000 --- a/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_money.txt +++ /dev/null @@ -1,9 +0,0 @@ -один доллар~1 USD -один рубль~1 руб. -тридцать одного рубля~31 руб. -восемнадцать тысяч долларов сэ ш а~18000 долл. США -два рубля~2 руб. -две целых и пять десятых тысячи рублей~2,5 тыс руб. -две целых и три сотых рублей~2,03 руб. -шесть рублей тридцать пять копеек~6 руб. 35 коп. -одна целая двадцать три сотых миллиарда рублей~1,23 млрд руб. diff --git a/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_ordinal.txt b/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_ordinal.txt deleted file mode 100644 index e8e9b8892f80..000000000000 --- a/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_ordinal.txt +++ /dev/null @@ -1,15 +0,0 @@ -одна тысяча девятьсот девяносто шестого~1996 -тысяча девятьсот девяносто девятый~1999 -тысяча восемьсот шестьдесят третьего~1863 -тысяча девятьсот семьдесят четвертом~1974 -двадцать четвертого~24 -двадцать восьмой~28 -девятнадцатый~19 -десятый~10 -тысяча восемьсот пятьдесят третьего~1853 -две тысячи двенадцатый~2012 -тысяча пятьсот тридцать шестом~1536 -два миллиона пятитысячный~2005000 -двадцать первые~21 -двухтысячные~2000 -я в первый раз~я в первый раз \ No newline at end of file diff --git a/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_ordinal_hard.txt b/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_ordinal_hard.txt deleted file mode 100644 index 6d62e345fdd5..000000000000 --- a/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_ordinal_hard.txt +++ /dev/null @@ -1,57 +0,0 @@ -тысяча девятьсот пятидесятые~1950-е -восьмого~8-го -двадцатого~20-го -тысяча девятьсот девяностые~1990-е -третьих~3-х -тысяча девятьсот шестидесятых~1960-х -шестой~6-й -вторая~2-ая -пятьдесят шестого~56-го -тысяча девятьсот двадцатых~1920-х -второй~2-й -третье~3-е -двадцать вторая~22-я -тысяча восемьсот шестидесятых~1860-х -тысяча восемьсот семидесятых~1870-х -двести девяносто первый~291-й -пятый~5-й -шестой~6-й -первого~1-го -пятые~5-е -вторые~2-е -пятьдесят первая~51-я -одиннадцатый~11-й -первый~1-й -сорок шестая~46-я -второй~2-й -восемьдесят четвертом~84-м -тысяча девятьсот шестидесятые~1960-е -первые~1-е -пятьдесят шестой~56-й -тринадцатый~13-й -девятого~9-го -второго~2-го -первый~1-й -четвертом~4-м -втором~2-м -двадцатых~20-х -тысяча восемьсот сороковые~1840-е -пятая~5-я -тысяча девятьсот тридцатом~1930-м -тридцать восьмая~38-я -тридцатых~30-х -первого~1-го -второго~2-го -второго~2-го -семидесятая~70-я -тысяча девятьсот двадцатых~1920-х -шестнадцатый~16-й -двенадцатом~12 -четырнадцатом~14 -двух тысячных~2000-х -вторые~2-е -тысяча восемьсот девяностых~1890-х -вторые~2-е -двадцать седьмой~27-й -пятом~5-м -шестью семь это сорок два~6-ю 7 это 42 diff --git a/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_telephone.txt b/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_telephone.txt deleted file mode 100644 index 676e8e260df5..000000000000 --- a/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_telephone.txt +++ /dev/null @@ -1 +0,0 @@ -восемь девятьсот тринадцать девятьсот восемьдесят три пятьдесят шесть ноль один~8-913-983-56-01 diff --git a/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_time.txt b/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_time.txt deleted file mode 100644 index 2a9fd32f7d9e..000000000000 --- a/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_time.txt +++ /dev/null @@ -1,9 +0,0 @@ -тринадцать часов пять минут~13:05 -тринадцать часов и пять минут~13:05 -два часа и три минуты~02:03 -один час ноль минут~01:00 -два часа~02:00 -без пятнадцати минут шесть~05:45 -без одной минуты десять~09:59 -двенадцать часов~12:00 -семь минут девятого~08:07 diff --git a/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_whitelist.txt b/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_whitelist.txt deleted file mode 100644 index 9b2fc93c7cad..000000000000 --- a/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_whitelist.txt +++ /dev/null @@ -1,4 +0,0 @@ -двух комнатная квартира~двух комн. кв. -жилой комплекс Принцип~ЖК Принцип -Высокий градус нашей дискуссии.~Высокий градус нашей дискуссии. -мяч был в нескольких метрах от нас~мяч был в нескольких метрах от нас diff --git a/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_word.txt b/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_word.txt deleted file mode 100644 index 3defc0486bbc..000000000000 --- a/tests/nemo_text_processing/ru/data_inverse_text_normalization/test_cases_word.txt +++ /dev/null @@ -1,50 +0,0 @@ -~ -yahoo!~yahoo! -двадцать!~20 ! -два би тридцать пять~два би 35 -x ~x -—~— -aaa~aaa -aabach~aabach -aabenraa~aabenraa -aabye~aabye -aaccessed~aaccessed -aach~aach -aachen's~aachen's -aadri~aadri -aafia~aafia -aagaard~aagaard -aagadu~aagadu -aagard~aagard -aagathadi~aagathadi -aaghart's~aaghart's -aagnes~aagnes -aagomoni~aagomoni -aagon~aagon -aagoo~aagoo -aagot~aagot -aahar~aahar -aahh~aahh -aahperd~aahperd -aaibinterstate~aaibinterstate -aajab~aajab -aakasa~aakasa -aakervik~aakervik -aakirkeby~aakirkeby -aalam~aalam -aalbaek~aalbaek -aaldiu~aaldiu -aalem~aalem -a'ali~a'ali -aalilaassamthey~aalilaassamthey -aalin~aalin -aaliyan~aaliyan -aaliyan's~aaliyan's -aamadu~aamadu -aamara~aamara -aambala~aambala -aamera~aamera -aamer's~aamer's -aamina~aamina -aaminah~aaminah -aamjiwnaang~aamjiwnaang diff --git a/tests/nemo_text_processing/ru/data_text_normalization/__init__.py b/tests/nemo_text_processing/ru/data_text_normalization/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/tests/nemo_text_processing/ru/data_text_normalization/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/tests/nemo_text_processing/ru/data_text_normalization/test_cases_cardinal.txt b/tests/nemo_text_processing/ru/data_text_normalization/test_cases_cardinal.txt deleted file mode 100644 index 1850e07d3478..000000000000 --- a/tests/nemo_text_processing/ru/data_text_normalization/test_cases_cardinal.txt +++ /dev/null @@ -1,28 +0,0 @@ -три~03 -минус пятьдесят~-50 -минус пятьдесят~-050 -двести пятьдесят четыре~254 -две тысячи триста~2 300 -минус две тысячи триста~-2 300 -две тысячи триста~002300 -две тысячи триста~2.300 -шестьдесят~60 -ноль~0 -два миллиона три~2000003 -одна тысяча тринадцать~1013 -одна тысяча сто~1100 -тысяча сто~1100 -одна тысяча двадцать шесть~1026 -тысяча двадцать шесть~1026 -одна тысяча сто двадцать шесть~1126 -тысяча тринадцать~1013 -тысяча один~1001 -одна тысяча один~1001 -одна тысяча~1000 -тысяча~1000 -одна тысяча~1000 -две тысячи пять~2 005 -два б три четыре~2-б-34 -два би три~2-b-3 -тест два~тест2 -тридцать код~30код diff --git a/tests/nemo_text_processing/ru/data_text_normalization/test_cases_cardinal_normalize_with_audio.txt b/tests/nemo_text_processing/ru/data_text_normalization/test_cases_cardinal_normalize_with_audio.txt deleted file mode 100644 index e2149b3fd101..000000000000 --- a/tests/nemo_text_processing/ru/data_text_normalization/test_cases_cardinal_normalize_with_audio.txt +++ /dev/null @@ -1,51 +0,0 @@ -~03 -три -~-50 -минус пятьдесят -~-050 -минус пятьдесят -~254 -двести пятьдесят четыре -~147691 -сто сорок семь тысяч шестьсот девяносто один -~1156173 -один миллион сто пятьдесят шесть тысяч сто семьдесят три -~1593072961 -один миллиард пятьсот девяносто три миллиона семьдесят две тысячи девятьсот шестьдесят один -~46664 -сорое шесть тысяч шестьсот шестьдесят четыре -сорое шесть тысяч шестьсот шестьдесят четырый -~60 -шестьдесят -шестидесятый -~0 -ноль -~2000003 -два миллиона три -~1013 -одна тысяча тринадцать -~1001 -одна тысяча один -~1100 -одна тысяча сто -тысяча сто -~1026 -одна тысяча двадцать шесть -тысяча двадцать шесть -~1126 -одна тысяча сто двадцать шесть -~1013 -тысяча тринадцать -~1001 -тысяча один -~1000 -одна тысяча -тысяча -~18450990 -восемнадцать миллионов четыреста пятьдесят тысяч девятьсот девяносто -~18940722 -восемнадцать миллионов девятьсот сорок тысяч семьсот двадцать два -~18690916 -восемнадцать миллионов шестьсот девяносто тысяч девятьсот шестнадцать -~18880 -восемнадцать тысяч восемьсот восемьдесят \ No newline at end of file diff --git a/tests/nemo_text_processing/ru/data_text_normalization/test_cases_date.txt b/tests/nemo_text_processing/ru/data_text_normalization/test_cases_date.txt deleted file mode 100644 index 40869e8bc4ab..000000000000 --- a/tests/nemo_text_processing/ru/data_text_normalization/test_cases_date.txt +++ /dev/null @@ -1,6 +0,0 @@ -шестое марта две тысячи шестого~06.03.2006 -первого января восемьдесят шестого~01-01-86 -второе марта тысяча девятьсот шестого~2/3/1906 -шестое марта тысяча шестого года~6/3/1006г. -третьего февраля две тысячи второго~03-фев-2002 -тридцать первого января ноль шестого~31.01.06 diff --git a/tests/nemo_text_processing/ru/data_text_normalization/test_cases_decimal.txt b/tests/nemo_text_processing/ru/data_text_normalization/test_cases_decimal.txt deleted file mode 100644 index 771475b0716e..000000000000 --- a/tests/nemo_text_processing/ru/data_text_normalization/test_cases_decimal.txt +++ /dev/null @@ -1,17 +0,0 @@ -ноль целых одна десятая~0,1 -ноль целых семьсот двадцать девять тысячных~0,729 -одними целых три десятых~1,3 -одна целая две десятых~1,2 -три целых две сотых~3,02 -ноль целых три тысячных~0,003 -одна целая две десятитысячных~1,0002 -ноль целых семьсот двадцать четыре тысячных~0,724 -триста целых две сотых~300,02 -ноль целых девять десятых~0,9 -две целых три десятых~2,3 -две целых три сотых~2,03 -две целых три тысячных~2,003 -две целых три десятитысячных~2,0003 -минус две целых пять десятых~-2,5 -одна целая двадцать три сотых миллиарда~1,23млрд -одна целая двадцать три сотых миллиарда~1,23 млрд. diff --git a/tests/nemo_text_processing/ru/data_text_normalization/test_cases_electronic.txt b/tests/nemo_text_processing/ru/data_text_normalization/test_cases_electronic.txt deleted file mode 100644 index 45c0c28e8cd1..000000000000 --- a/tests/nemo_text_processing/ru/data_text_normalization/test_cases_electronic.txt +++ /dev/null @@ -1,4 +0,0 @@ -эй точка би си собака джи мейл точка ком~a.bc@gmail.com -эй собака хот мейл точка ди и~a@hotmail.de -эй один два три тире ви собака эй би си точка ком~a123-v@abc.com -адрес эй дефис один точка би три нижнее подчеркивание си амперсанд тире ди собака джи эм эй четыре ай тире эл точка ком тест~адрес a-1.b3_c&-d@gma4i-l.com тест diff --git a/tests/nemo_text_processing/ru/data_text_normalization/test_cases_measure.txt b/tests/nemo_text_processing/ru/data_text_normalization/test_cases_measure.txt deleted file mode 100644 index 7f646995924e..000000000000 --- a/tests/nemo_text_processing/ru/data_text_normalization/test_cases_measure.txt +++ /dev/null @@ -1,13 +0,0 @@ -двенадцать килограм~12 кг -двенадцать килограм~12кг -три целых две десятых литра~3,2 л -три целых две десятых миллиона литров~3,2 млн л -три километра в час~3 км/ч -четырех килограммовый мешок~4-х килограммовый мешок -девять мешок~9мешок -четыре целых два десятых килограммовый мешок~4,2-килограммовый мешок -тест одна целая две сотых~тест-1,02 -тест восемь~тест-8 -четыре килограма в килограме~4 кг/кг -один в килограме~1/кг -один квадратный метр~один м² diff --git a/tests/nemo_text_processing/ru/data_text_normalization/test_cases_money.txt b/tests/nemo_text_processing/ru/data_text_normalization/test_cases_money.txt deleted file mode 100644 index 65fe14698829..000000000000 --- a/tests/nemo_text_processing/ru/data_text_normalization/test_cases_money.txt +++ /dev/null @@ -1,9 +0,0 @@ -один доллар~1 USD -один рубль~1 руб. -тридцать одного рубля~31 руб. -восемнадцать тысяч долларов сэ ш а~18000 долл. США -два рубля~2 руб. -две целых пять десятых тысячи рублей~2,5 тыс руб. -две целых три сотых рублей~2,03 руб. -шесть рублей тридцать пять копеек~6 руб. 35 коп. -одна целая двадцать три сотых миллиарда рублей~1,23 млрд руб. diff --git a/tests/nemo_text_processing/ru/data_text_normalization/test_cases_ordinal.txt b/tests/nemo_text_processing/ru/data_text_normalization/test_cases_ordinal.txt deleted file mode 100644 index 1c4b9f2c409b..000000000000 --- a/tests/nemo_text_processing/ru/data_text_normalization/test_cases_ordinal.txt +++ /dev/null @@ -1,75 +0,0 @@ -тысяча девятьсот пятидесятые~1950-е -восьмого~8-го -двадцатого~20-го -тысяча девятьсот девяносто шестого~1996 -тысяча девятьсот девяносто девятый~1999 -тысяча девятьсот девяностые~1990-е -тысяча восемьсот шестьдесят третьего~1863 -третьих~3-х -тысяча девятьсот семьдесят четвертом~1974 -тысяча девятьсот шестидесятых~1960-х -шестой~6-й -вторая~2-ая -пятьдесят шестого~56-го -двадцать четвертого~24 -двадцать восьмой~28 -тысяча восемьсот пятьдесят третьего~1853 -две тысячи двенадцатый~2012 -тысяча восемьсот девяностом~1890 -тысяча девятьсот двадцатых~1920-х -тысяча девятьсот пятидесятом~1950 -второй~2-й -третье~3-е -двадцать вторая~22-я -тысяча восемьсот шестидесятых~1860-х -тысяча восемьсот семидесятых~1870-х -двести девяносто первый~291-й -пятый~5-й -шестой~6-й -первого~1-го -пятые~5-е -вторые~2-е -пятьдесят первая~51-я -одиннадцатый~11-й -первый~1-й -сорок шестая~46-я -второй~2-й -восемьдесят четвертом~84-м -второй~2-й -тысяча девятьсот шестидесятые~1960-е -тысяча семьсот восемьдесят первом~1781 -тысяча девятьсот сорок пятом~1945 -первые~1-е -тысяча девятьсот девятом~1909 -пятьдесят шестой~56-й -тринадцатый~13-й -тысяча девятьсот тридцать первого~1931 -тысяча девятьсот тридцать седьмой~1937 -девятого~9-го -второго~2-го -первый~1-й -четвертом~4-м -втором~2-м -тысяча пятьсот тридцать шестом~1536 -двадцатых~20-х -тысяча восемьсот сороковые~1840-е -пятая~5-я -тысяча девятьсот тридцатом~1930-м -две тысячи третьем~2003 -тридцать восьмая~38-я -тридцатых~30-х -семьсот сороковом~740 -первого~1-го -второго~2-го -второго~2-го -семидесятая~70-я -тысяча девятьсот двадцатых~1920-х -шестнадцатый~16-й -двенадцатом~12 -четырнадцатом~14 -двухтысячных~2000-х -вторые~2-е -тысяча восемьсот девяностых~1890-х -вторые~2-е -двадцать седьмой~27-й -пятом~5-м diff --git a/tests/nemo_text_processing/ru/data_text_normalization/test_cases_telephone.txt b/tests/nemo_text_processing/ru/data_text_normalization/test_cases_telephone.txt deleted file mode 100644 index 676e8e260df5..000000000000 --- a/tests/nemo_text_processing/ru/data_text_normalization/test_cases_telephone.txt +++ /dev/null @@ -1 +0,0 @@ -восемь девятьсот тринадцать девятьсот восемьдесят три пятьдесят шесть ноль один~8-913-983-56-01 diff --git a/tests/nemo_text_processing/ru/data_text_normalization/test_cases_time.txt b/tests/nemo_text_processing/ru/data_text_normalization/test_cases_time.txt deleted file mode 100644 index 2a9fd32f7d9e..000000000000 --- a/tests/nemo_text_processing/ru/data_text_normalization/test_cases_time.txt +++ /dev/null @@ -1,9 +0,0 @@ -тринадцать часов пять минут~13:05 -тринадцать часов и пять минут~13:05 -два часа и три минуты~02:03 -один час ноль минут~01:00 -два часа~02:00 -без пятнадцати минут шесть~05:45 -без одной минуты десять~09:59 -двенадцать часов~12:00 -семь минут девятого~08:07 diff --git a/tests/nemo_text_processing/ru/data_text_normalization/test_cases_whitelist.txt b/tests/nemo_text_processing/ru/data_text_normalization/test_cases_whitelist.txt deleted file mode 100644 index 18d573037863..000000000000 --- a/tests/nemo_text_processing/ru/data_text_normalization/test_cases_whitelist.txt +++ /dev/null @@ -1,4 +0,0 @@ -квадратный метр~м² -градус~° -процент~% -эй би си~abc diff --git a/tests/nemo_text_processing/ru/data_text_normalization/test_cases_word.txt b/tests/nemo_text_processing/ru/data_text_normalization/test_cases_word.txt deleted file mode 100644 index ec47add35a07..000000000000 --- a/tests/nemo_text_processing/ru/data_text_normalization/test_cases_word.txt +++ /dev/null @@ -1,3 +0,0 @@ -~ -тест тест одна целая пять десятых в два часа~тест тест 1,5 в 2 часа -текст, и знаки препинания!~текст, и знаки препинания! diff --git a/tests/nemo_text_processing/ru/test_ru_inverse_normalization.py b/tests/nemo_text_processing/ru/test_ru_inverse_normalization.py deleted file mode 100644 index 779e236230f9..000000000000 --- a/tests/nemo_text_processing/ru/test_ru_inverse_normalization.py +++ /dev/null @@ -1,147 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestRuInverseNormalize: - - normalizer = InverseNormalizer(lang='ru', cache_dir=CACHE_DIR) if PYNINI_AVAILABLE else None - - @parameterized.expand(parse_test_case_file('ru/data_inverse_text_normalization/test_cases_cardinal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm_cardinal(self, test_input, expected): - pred = self.normalizer.inverse_normalize(test_input, verbose=False) - assert expected == pred - - @parameterized.expand(parse_test_case_file('ru/data_inverse_text_normalization/test_cases_ordinal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm_ordinal(self, test_input, expected): - pred = self.normalizer.inverse_normalize(test_input, verbose=False) - assert expected == pred - - # @parameterized.expand(parse_test_case_file('ru_data_inverse_text_normalization/test_cases_ordinal_hard.txt')) - # @pytest.mark.run_only_on('CPU') - # @pytest.mark.unit - # def test_denorm_ordinal_hard(self, test_input, expected): - # pred = self.normalizer.inverse_normalize(test_input, verbose=False) - # assert expected == pred - - @parameterized.expand(parse_test_case_file('ru/data_inverse_text_normalization/test_cases_decimal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm_decimal(self, test_input, expected): - pred = self.normalizer.inverse_normalize(test_input, verbose=False) - assert expected == pred - - @parameterized.expand(parse_test_case_file('ru/data_inverse_text_normalization/test_cases_electronic.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm_electronic(self, test_input, expected): - pred = self.normalizer.inverse_normalize(test_input, verbose=False) - assert expected == pred - - @parameterized.expand(parse_test_case_file('ru/data_inverse_text_normalization/test_cases_date.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm_date(self, test_input, expected): - pred = self.normalizer.inverse_normalize(test_input, verbose=False) - assert expected == pred - - @parameterized.expand(parse_test_case_file('ru/data_inverse_text_normalization/test_cases_measure.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm_measure(self, test_input, expected): - pred = self.normalizer.inverse_normalize(test_input, verbose=False) - assert expected == pred - - @parameterized.expand(parse_test_case_file('ru/data_inverse_text_normalization/test_cases_money.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm_money(self, test_input, expected): - pred = self.normalizer.inverse_normalize(test_input, verbose=False) - assert expected == pred - - @parameterized.expand(parse_test_case_file('ru/data_inverse_text_normalization/test_cases_time.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm_time(self, test_input, expected): - pred = self.normalizer.inverse_normalize(test_input, verbose=False) - assert expected == pred - - @parameterized.expand(parse_test_case_file('ru/data_inverse_text_normalization/test_cases_whitelist.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm_whitelist(self, test_input, expected): - pred = self.normalizer.inverse_normalize(test_input, verbose=False) - assert expected == pred - - @parameterized.expand(parse_test_case_file('ru/data_inverse_text_normalization/test_cases_word.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm_word(self, test_input, expected): - pred = self.normalizer.inverse_normalize(test_input, verbose=False) - assert expected == pred diff --git a/tests/nemo_text_processing/ru/test_ru_normalization.py b/tests/nemo_text_processing/ru/test_ru_normalization.py deleted file mode 100644 index 9ec3e78894c2..000000000000 --- a/tests/nemo_text_processing/ru/test_ru_normalization.py +++ /dev/null @@ -1,152 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestRuNormalizeWithAudio: - - normalizer = NormalizerWithAudio(input_case='cased', lang='ru', cache_dir=CACHE_DIR) if PYNINI_AVAILABLE else None - N_TAGGED = 100 - - @parameterized.expand(parse_test_case_file('ru/data_text_normalization/test_cases_cardinal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm_cardinal(self, expected, test_input): - preds = self.normalizer.normalize(test_input, n_tagged=50) - assert expected in preds - - @parameterized.expand(parse_test_case_file('ru/data_text_normalization/test_cases_ordinal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm_ordinal(self, expected, test_input): - preds = self.normalizer.normalize(test_input, n_tagged=self.N_TAGGED) - assert expected in preds - - @parameterized.expand(parse_test_case_file('ru/data_text_normalization/test_cases_decimal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm_decimal(self, expected, test_input): - preds = self.normalizer.normalize(test_input, n_tagged=self.N_TAGGED) - assert expected in preds - - @parameterized.expand(parse_test_case_file('ru/data_text_normalization/test_cases_measure.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm_measure(self, expected, test_input): - preds = self.normalizer.normalize(test_input, n_tagged=self.N_TAGGED) - assert expected in preds - - @parameterized.expand(parse_test_case_file('ru/data_text_normalization/test_cases_date.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm_date(self, expected, test_input): - preds = self.normalizer.normalize(test_input, n_tagged=self.N_TAGGED) - assert expected in preds, expected not in preds - - @parameterized.expand(parse_test_case_file('ru/data_text_normalization/test_cases_telephone.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm_telephone(self, expected, test_input): - preds = self.normalizer.normalize(test_input, n_tagged=self.N_TAGGED) - assert expected in preds, expected not in preds - - @parameterized.expand(parse_test_case_file('ru/data_text_normalization/test_cases_money.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm_money(self, expected, test_input): - preds = self.normalizer.normalize(test_input, n_tagged=self.N_TAGGED) - assert expected in preds, expected not in preds - - @parameterized.expand(parse_test_case_file('ru/data_text_normalization/test_cases_time.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm_time(self, expected, test_input): - preds = self.normalizer.normalize(test_input, n_tagged=self.N_TAGGED) - assert expected in preds, expected not in preds - - @parameterized.expand(parse_test_case_file('ru/data_text_normalization/test_cases_electronic.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm_electronic(self, expected, test_input): - preds = self.normalizer.normalize(test_input, n_tagged=self.N_TAGGED) - assert expected in preds - - @parameterized.expand(parse_test_case_file('ru/data_text_normalization/test_cases_whitelist.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm_whitelist(self, expected, test_input): - preds = self.normalizer.normalize(test_input, n_tagged=self.N_TAGGED) - assert expected in preds - - @parameterized.expand(parse_test_case_file('ru/data_text_normalization/test_cases_word.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm_word(self, expected, test_input): - preds = self.normalizer.normalize(test_input, n_tagged=self.N_TAGGED) - assert expected in preds diff --git a/tests/nemo_text_processing/ru/test_sparrowhawk_inverse_text_normalization.sh b/tests/nemo_text_processing/ru/test_sparrowhawk_inverse_text_normalization.sh deleted file mode 100644 index 1c975e07071e..000000000000 --- a/tests/nemo_text_processing/ru/test_sparrowhawk_inverse_text_normalization.sh +++ /dev/null @@ -1,79 +0,0 @@ -#! /bin/sh - -PROJECT_DIR=/workspace/tests - -runtest () { - input=$1 - cd /workspace/sparrowhawk/documentation/grammars - - # read test file - while read testcase; do - IFS='~' read spoken written <<< $testcase - denorm_pred=$(echo $spoken | normalizer_main --config=sparrowhawk_configuration.ascii_proto 2>&1 | tail -n 1) - - # trim white space - written="$(echo -e "${written}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - denorm_pred="$(echo -e "${denorm_pred}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - - # input expected actual - assertEquals "$spoken" "$written" "$denorm_pred" - done < "$input" -} - -testITNCardinal() { - input=$PROJECT_DIR/ru/data_inverse_text_normalization/test_cases_cardinal.txt - runtest $input -} - -testITNOrdinal() { - input=$PROJECT_DIR/ru/data_inverse_text_normalization/test_cases_ordinal.txt - runtest $input -} - -testITNDate() { - input=$PROJECT_DIR/ru/data_inverse_text_normalization/test_cases_date.txt - runtest $input -} - -testITNDecimal() { - input=$PROJECT_DIR/ru/data_inverse_text_normalization/test_cases_decimal.txt - runtest $input -} - -testITNElectronic() { - input=$PROJECT_DIR/ru/data_inverse_text_normalization/test_cases_electronic.txt - runtest $input -} - -testITNTime() { - input=$PROJECT_DIR/ru/data_inverse_text_normalization/test_cases_time.txt - runtest $input -} - -testITNMeasure() { - input=$PROJECT_DIR/ru/data_inverse_text_normalization/test_cases_measure.txt - runtest $input -} - -testITNMoney() { - input=$PROJECT_DIR/ru/data_inverse_text_normalization/test_cases_money.txt - runtest $input -} - -testITNWhitelist() { - input=$PROJECT_DIR/ru/data_inverse_text_normalization/test_cases_whitelist.txt - runtest $input -} - -testITNTelephone() { - input=$PROJECT_DIR/ru/data_inverse_text_normalization/test_cases_telephone.txt - runtest $input -} - -testITNWord() { - input=$PROJECT_DIR/ru/data_inverse_text_normalization/test_cases_word.txt - runtest $input -} - -# Load shUnit2 -. $PROJECT_DIR/../shunit2/shunit2 diff --git a/tests/nemo_text_processing/utils.py b/tests/nemo_text_processing/utils.py deleted file mode 100644 index df306c38200b..000000000000 --- a/tests/nemo_text_processing/utils.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -CACHE_DIR = None -RUN_AUDIO_BASED_TESTS = False - - -def set_cache_dir(path: str = None): - """ - Sets cache directory for TN/ITN unittests. Default is None, e.g. no cache during tests. - """ - global CACHE_DIR - CACHE_DIR = path - - -def set_audio_based_tests(run_audio_based: bool = False): - """ - Sets audio-based test mode for TN/ITN unittests. Default is False, e.g. audio-based tests will be skipped. - """ - global RUN_AUDIO_BASED_TESTS - RUN_AUDIO_BASED_TESTS = run_audio_based - - -def parse_test_case_file(file_name: str): - """ - Prepares tests pairs for ITN and TN tests - """ - test_pairs = [] - with open(os.path.dirname(os.path.abspath(__file__)) + os.path.sep + file_name, 'r') as f: - for line in f: - spoken, written = line.split('~') - test_pairs.append((spoken, written.strip("\n"))) - return test_pairs - - -def get_test_cases_multiple(file_name: str = 'data_text_normalization/en/test_cases_normalize_with_audio.txt'): - """ - Prepares tests pairs for audio based TN tests - """ - test_pairs = [] - with open(os.path.dirname(os.path.abspath(__file__)) + os.path.sep + file_name, 'r') as f: - written = None - normalized_options = [] - for line in f: - if line.startswith('~'): - if written: - test_pairs.append((written, normalized_options)) - normalized_options = [] - written = line.strip().replace('~', '') - else: - normalized_options.append(line.strip()) - test_pairs.append((written, normalized_options)) - return test_pairs diff --git a/tests/nemo_text_processing/vi/__init__.py b/tests/nemo_text_processing/vi/__init__.py deleted file mode 100644 index bc443be41c4c..000000000000 --- a/tests/nemo_text_processing/vi/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_cardinal.txt b/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_cardinal.txt deleted file mode 100644 index 53853297c9b6..000000000000 --- a/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_cardinal.txt +++ /dev/null @@ -1,93 +0,0 @@ -một trăm~100 -mười tám~18 -hai mốt~21 -hai mươi mốt~21 -ba mươi mốt~31 -ba mốt~31 -bốn ba~43 -bốn mươi tư~44 -năm lăm~55 -năm mươi bốn~54 -sáu bảy~67 -sáu mươi bẩy~67 -bẩy mươi~70 -bảy hai~72 -tám mươi~80 -chín mươi lăm~95 -một trăm linh hai~102 -một trăm lẻ tư~104 -một trăm hai mươi~120 -một trăm hai~120 -hai trăm~200 -hai trăm linh một~201 -một trăm mười một~111 -một trăm mốt~110 -một nghìn~1000 -một ngàn~1000 -năm trăm tư~540 -tám trăm rưỡi~850 -một nghìn hai mươi~1020 -một ngàn chín lăm~1095 -chín tỷ bảy trăm tám chín triệu ba trăm tám mươi hai ngàn năm trăm ba mươi lăm~9789382535 -tám tỉ chín trăm triệu~8900000000 -một trăm bốn bảy nghìn bốn trăm năm mốt~147451 -một trăm bốn mươi bảy nghìn bốn trăm năm mươi mốt~147451 -một triệu không trăm năm mươi sáu ngàn một trăm bảy mươi ba~1056173 -mười một triệu không trăm năm sáu nghìn một trăm bẩy ba~11056173 -một tỉ năm trăm chín ba triệu không trăm bảy mươi hai nghìn chín trăm sáu mốt~1593072961 -một trăm tỉ năm trăm chín ba triệu không trăm bảy mươi hai nghìn chín trăm sáu mốt~100593072961 -chín trăm năm mươi tỉ năm trăm chín ba triệu không trăm bảy mươi hai nghìn chín trăm sáu mốt~950593072961 -một trăm tỷ một trăm mười~100000000110 -một tỉ một triệu một trăm~1001000100 -âm hai mươi lăm nghìn ba bảy~-25037 -trừ hai lăm ngàn không trăm ba mươi bẩy~-25037 -trừ một nghìn chín trăm ba bảy~-1937 -âm một ngàn chín~-1900 -âm sáu mươi~-60 -trừ sáu mươi~-60 -âm một triệu không trăm linh một~-1000001 -không~không -một~một -hai~hai -lăm~lăm -mốt~mốt -tư~tư -mười~10 -mười một~11 -mười hai~12 -mười ba~13 -mười bốn~14 -mười lăm~15 -mười sáu~16 -mười bẩy~17 -mười bảy~17 -mười tám~18 -hai mươi~20 -ba mươi~30 -bốn mươi~40 -năm mươi~50 -sáu mươi~60 -bảy mươi~70 -bẩy mươi~70 -tám mươi~80 -chín mươi~90 -hai triệu mười~2000010 -một ngàn mười ba~1013 -một nghìn mười ba~1013 -một nghìn lẻ một~1001 -một ngàn linh một~1001 -một ngàn mốt~1100 -năm ngàn rưỡi~5500 -một nghìn hai sáu~1026 -một ngàn một trăm hai sáu~1126 -ba nghìn năm~3000 năm -bẩy ngàn năm~7000 năm -sáu triệu năm~6 triệu năm -năm năm~năm năm -ba năm~ba năm -tám nghìn mốt~8100 -một trăm một~100 một -một trăm linh một~101 -một trăm mốt~110 -một trăm mười~110 -hai triệu ba nghìn~2003000 \ No newline at end of file diff --git a/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_date.txt b/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_date.txt deleted file mode 100644 index 182c710e25b3..000000000000 --- a/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_date.txt +++ /dev/null @@ -1,16 +0,0 @@ -hai tư tháng bảy năm hai nghìn mười ba~24 tháng 7 năm 2013 -ngày hai mươi tư tháng bẩy năm hai không mười ba~ngày 24 tháng 7 năm 2013 -mùng một tháng một năm một chín chín chín~mùng 1 tháng 1 năm 1999 -ngày ba mươi tháng tư~ngày 30 tháng 4 -hai mươi tư tháng bảy~24 tháng 7 -ngày ba mươi tháng sáu~ngày 30 tháng 6 -tháng mười hai~tháng 12 -tháng năm~tháng năm -tháng chín~tháng 9 -tháng mười năm hai ngàn linh chín~tháng 10 năm 2009 -năm một tám năm hai~năm 1852 -năm chín trăm ba tám~năm 938 -năm ba trăm lẻ tám~năm 308 -năm bẩy trăm bốn mươi tư~năm 744 -học kỳ này sẽ kết thúc vào tháng tư ngày mười tháng năm là tổng kết~học kỳ này sẽ kết thúc vào tháng 4 ngày 10 tháng 5 là tổng kết -học kỳ này sẽ kết thúc vào tháng năm ngày một tháng sáu là tổng kết~học kỳ này sẽ kết thúc vào tháng năm ngày 1 tháng 6 là tổng kết \ No newline at end of file diff --git a/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_decimal.txt b/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_decimal.txt deleted file mode 100644 index 9888ff64e4e0..000000000000 --- a/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_decimal.txt +++ /dev/null @@ -1,25 +0,0 @@ -không phẩy hai triệu~0.2 triệu -một triệu hai~1.2 triệu -mười tám vạn~18 vạn -bốn trăm sáu mươi triệu~460 triệu -bốn trăm sáu triệu~460 triệu -bốn triệu sáu~4.6 triệu -một trăm hai mươi tỉ~120 tỉ -một trăm hai mươi tỷ~120 tỷ -mười vạn~10 vạn -sáu mươi phẩy hai bốn không~60.240 -sáu mươi chấm hai tư~60.24 -tám trăm mười tám chấm ba không ba~818.303 -tám trăm mười tám phẩy ba không ba~818.303 -không phẩy năm~0.5 -không chấm năm~0.5 -mười triệu rưỡi~10.5 triệu -mười triệu năm~10 triệu năm -mười triệu mốt~10.1 triệu -mười triệu tư~10.4 triệu -chín chín tỷ chín~99.9 tỷ -trừ chín chín tỷ chín~-99.9 tỷ -âm chín chín chấm chín lăm tỷ~-99.95 tỷ -hai mươi chấm tư~20.4 -mười hai chấm mốt~12 chấm mốt -chín trăm chín ba chấm lăm~993 chấm lăm \ No newline at end of file diff --git a/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_electronic.txt b/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_electronic.txt deleted file mode 100644 index 04168797eba0..000000000000 --- a/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_electronic.txt +++ /dev/null @@ -1,10 +0,0 @@ -a chấm b c a còng g mail chấm com~a.bc@gmail.com -a chấm b c a móc g mail chấm com~a.bc@gmail.com -c d f a móc a b c chấm e d u~cdf@abc.edu -a b c a móc g mail chấm a b c~abc@gmail.abc -a b c a vòng g mail chấm a b c~abc@gmail.abc -a b c a móc a b c chấm com~abc@abc.com -a s d f một hai ba a móc a b c chấm com~asdf123@abc.com -a một b hai a vòng a b c chấm com~a1b2@abc.com -a b ba chấm s d d chấm ba a móc g mail chấm com~ab3.sdd.3@gmail.com -a b ba chấm s d d chấm ba a còng g mail chấm com~ab3.sdd.3@gmail.com \ No newline at end of file diff --git a/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_fraction.txt b/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_fraction.txt deleted file mode 100644 index 3249d734f497..000000000000 --- a/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_fraction.txt +++ /dev/null @@ -1,14 +0,0 @@ -một trên hai~1/2 -hai trên mười lăm~2/15 -bốn lăm phần hai trăm mốt~45/210 -hai ba chia mười hai~23/12 -một chia bẩy~1/7 -một chia tám~1/8 -ba mươi phần một trăm~30/100 -hai phần ba~2/3 -chín phần chín~9/9 -một phần mười~1/10 -trừ năm phần mười hai~-5/12 -âm ba trên bốn~-3/4 -trừ ba mươi phần một trăm~-30/100 -âm tám phần ba~-8/3 \ No newline at end of file diff --git a/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_measure.txt b/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_measure.txt deleted file mode 100644 index 8b8c97b96de5..000000000000 --- a/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_measure.txt +++ /dev/null @@ -1,22 +0,0 @@ -hai trăm mét~200 m -hai trăm lẻ tư mét~204 m -năm mươi sáu phẩy ba ki lô mét vuông~56.3 km² -hai trăm ki lô mét trên giờ~200 km/h -hai trăm kilomet trên giờ~200 km/h -năm xăng ti mét trên giây~5 cm/s -bốn mươi hai nghìn hai trăm năm chín mét vuông~42259 m² -trừ sáu sáu kí~-66 kg -một triệu không trăm hai tám xen ti mét khối~1000028 cm³ -năm mươi kí lô~50 kg -hai mét khối~2 m³ -chín mươi gram~90 g -bốn trăm bốn mươi mi li lít~440 ml -ba trăm muy crô mét~300 μm -sáu lăm inch~65 inch -hai vôn~2 v -ba mươi phần trăm~30 % -sáu mươi nghìn hai trăm bốn mươi mi li ampe~60240 mA -sáu mươi sáu phút~66 phút -hai phút~2 phút -năm giây~5 s -năm trăm sáu bảy giây~567 s \ No newline at end of file diff --git a/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_money.txt b/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_money.txt deleted file mode 100644 index 2d99fd4bba03..000000000000 --- a/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_money.txt +++ /dev/null @@ -1,17 +0,0 @@ -hai đô~2$ -không phẩy không một euro~0.01€ -không chấm hai ơ rô~0.2€ -không phẩy hai hai euro~0.22€ -hai phẩy hai đô la mỹ~2.2$ -hai đô rưỡi~2.5$ -hai mươi euro~20€ -hai mươi chấm một euro~20.1€ -một đồng~1₫ -mười nghìn năm trăm đồng~10500₫ -năm phẩy sáu đồng~5.6₫ -hai mươi đồng rưỡi~20.5₫ -tám mươi nghìn một trăm won~80100₩ -tám mươi ngàn một trăm uôn~80100₩ -ba ringgit~3RM -không phẩy ba ringgit~0.3RM -không euro~0€ \ No newline at end of file diff --git a/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_ordinal.txt b/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_ordinal.txt deleted file mode 100644 index 740bc0bd2781..000000000000 --- a/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_ordinal.txt +++ /dev/null @@ -1,12 +0,0 @@ -thứ nhất~thứ 1 -thứ nhì~thứ 2 -thứ hai~thứ 2 -thứ ba~thứ 3 -thứ bốn~thứ 4 -thứ tư~thứ 4 -thứ năm~thứ 5 -thứ sáu~thứ 6 -thứ bảy~thứ 7 -thứ bẩy~thứ 7 -thứ tám~thứ 8 -thứ chín~thứ 9 \ No newline at end of file diff --git a/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_telephone.txt b/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_telephone.txt deleted file mode 100644 index 0fa73bcaabb8..000000000000 --- a/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_telephone.txt +++ /dev/null @@ -1,4 +0,0 @@ -không chín ba sáu năm năm năm bốn bốn chín~0936555449 -không một hai tám bốn hai hai năm~01284225 -một hai ba bốn năm sáu bảy tám chín~123456789 -chín tám bảy sáu năm bốn ba hai một không~9876543210 \ No newline at end of file diff --git a/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_time.txt b/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_time.txt deleted file mode 100644 index 4718c6d6c1b6..000000000000 --- a/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_time.txt +++ /dev/null @@ -1,22 +0,0 @@ -tám giờ~8h -bảy giờ~7h -bẩy giờ~7h -hai mươi giờ~20h -mười tám giờ~18h -tám giờ bảy phút~8:07 -tám giờ hai mươi~8:20 -chín giờ rưỡi~9:30 -chín rưỡi~chín rưỡi -năm giờ hai mươi phút ba lăm giây~5:20:35 -sáu giờ mười phút năm giây~6:10:05 -mười sáu giờ kém mười lăm~15:45 -hai mươi giờ kém bốn phút~19:56 -năm giờ kém hai mươi phút~4:40 -năm giờ kém năm mươi sáu phút~4:04 -ba phút hai mươi giây~3p20s -bốn phút rưỡi~4p30s -mười hai phút ba giây~12p03s -năm chín phút năm mươi chín giây~59p59s -tám phút bốn lăm giây~8p45s -tám giờ hai ba phút gmt~8:23 gmt -mười lăm giờ cst~15h cst \ No newline at end of file diff --git a/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_whitelist.txt b/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_whitelist.txt deleted file mode 100644 index 4977bc62c0e5..000000000000 --- a/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_whitelist.txt +++ /dev/null @@ -1 +0,0 @@ -~ \ No newline at end of file diff --git a/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_word.txt b/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_word.txt deleted file mode 100644 index b34357d4137a..000000000000 --- a/tests/nemo_text_processing/vi/data_inverse_text_normalization/test_cases_word.txt +++ /dev/null @@ -1,17 +0,0 @@ -~ -yahoo!~yahoo! -x~x -—~— -hôm~hôm -nay~nay -ăn~ăn -gì~gì -covid~covid -ngay~ngay -khi~khi -xảy~xảy -ra~ra -hiện~hiện -trường~trường -vụ~vụ -án~án \ No newline at end of file diff --git a/tests/nemo_text_processing/vi/test_cardinal.py b/tests/nemo_text_processing/vi/test_cardinal.py deleted file mode 100644 index 0a888f84bfd6..000000000000 --- a/tests/nemo_text_processing/vi/test_cardinal.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestCardinal: - - inverse_normalizer = ( - InverseNormalizer(lang='vi', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('vi/data_inverse_text_normalization/test_cases_cardinal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/vi/test_date.py b/tests/nemo_text_processing/vi/test_date.py deleted file mode 100644 index 90885b6e4313..000000000000 --- a/tests/nemo_text_processing/vi/test_date.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestDate: - inverse_normalizer = ( - InverseNormalizer(lang='vi', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('vi/data_inverse_text_normalization/test_cases_date.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/vi/test_decimal.py b/tests/nemo_text_processing/vi/test_decimal.py deleted file mode 100644 index e1b246e1d7d2..000000000000 --- a/tests/nemo_text_processing/vi/test_decimal.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestDecimal: - inverse_normalizer = ( - InverseNormalizer(lang='vi', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('vi/data_inverse_text_normalization/test_cases_decimal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/vi/test_electronic.py b/tests/nemo_text_processing/vi/test_electronic.py deleted file mode 100644 index 4df5810d9ff8..000000000000 --- a/tests/nemo_text_processing/vi/test_electronic.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestElectronic: - inverse_normalizer = ( - InverseNormalizer(lang='vi', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('vi/data_inverse_text_normalization/test_cases_electronic.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/vi/test_fraction.py b/tests/nemo_text_processing/vi/test_fraction.py deleted file mode 100644 index acd465cfd9a5..000000000000 --- a/tests/nemo_text_processing/vi/test_fraction.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestFraction: - inverse_normalizer = ( - InverseNormalizer(lang='vi', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('vi/data_inverse_text_normalization/test_cases_fraction.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/vi/test_measure.py b/tests/nemo_text_processing/vi/test_measure.py deleted file mode 100644 index 991cbc487612..000000000000 --- a/tests/nemo_text_processing/vi/test_measure.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestMeasure: - inverse_normalizer = ( - InverseNormalizer(lang='vi', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('vi/data_inverse_text_normalization/test_cases_measure.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/vi/test_money.py b/tests/nemo_text_processing/vi/test_money.py deleted file mode 100644 index c626eef41f27..000000000000 --- a/tests/nemo_text_processing/vi/test_money.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestMoney: - inverse_normalizer = ( - InverseNormalizer(lang='vi', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('vi/data_inverse_text_normalization/test_cases_money.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/vi/test_ordinal.py b/tests/nemo_text_processing/vi/test_ordinal.py deleted file mode 100644 index 239234dda948..000000000000 --- a/tests/nemo_text_processing/vi/test_ordinal.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestOrdinal: - inverse_normalizer = ( - InverseNormalizer(lang='vi', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('vi/data_inverse_text_normalization/test_cases_ordinal.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/vi/test_sparrowhawk_inverse_text_normalization.sh b/tests/nemo_text_processing/vi/test_sparrowhawk_inverse_text_normalization.sh deleted file mode 100644 index cc1defd4659f..000000000000 --- a/tests/nemo_text_processing/vi/test_sparrowhawk_inverse_text_normalization.sh +++ /dev/null @@ -1,79 +0,0 @@ -#! /bin/sh - -PROJECT_DIR=/workspace/tests - -runtest () { - input=$1 - cd /workspace/sparrowhawk/documentation/grammars - - # read test file - while read testcase; do - IFS='~' read spoken written <<< $testcase - denorm_pred=$(echo $spoken | normalizer_main --config=sparrowhawk_configuration.ascii_proto 2>&1 | tail -n 1) - - # trim white space - written="$(echo -e "${written}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - denorm_pred="$(echo -e "${denorm_pred}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - - # input expected actual - assertEquals "$spoken" "$written" "$denorm_pred" - done < "$input" -} - -testITNCardinal() { - input=$PROJECT_DIR/vi/data_inverse_text_normalization/test_cases_cardinal.txt - runtest $input -} - -testITNDate() { - input=$PROJECT_DIR/vi/data_inverse_text_normalization/test_cases_date.txt - runtest $input -} - -testITNDecimal() { - input=$PROJECT_DIR/vi/data_inverse_text_normalization/test_cases_decimal.txt - runtest $input -} - -testITNOrdinal() { - input=$PROJECT_DIR/vi/data_inverse_text_normalization/test_cases_ordinal.txt - runtest $input -} - -testITNFraction() { - input=$PROJECT_DIR/vi/data_inverse_text_normalization/test_cases_fraction.txt - runtest $input -} - -testITNTime() { - input=$PROJECT_DIR/vi/data_inverse_text_normalization/test_cases_time.txt - runtest $input -} - -testITNMeasure() { - input=$PROJECT_DIR/vi/data_inverse_text_normalization/test_cases_measure.txt - runtest $input -} - -testITNMoney() { - input=$PROJECT_DIR/vi/data_inverse_text_normalization/test_cases_money.txt - runtest $input -} - -testITNTelephone() { - input=$PROJECT_DIR/vi/data_inverse_text_normalization/test_cases_telephone.txt - runtest $input -} - -testITNElectronic() { - input=$PROJECT_DIR/vi/data_inverse_text_normalization/test_cases_electronic.txt - runtest $input -} - -testITNWord() { - input=$PROJECT_DIR/vi/data_inverse_text_normalization/test_cases_word.txt - runtest $input -} - -# Load shUnit2 -. $PROJECT_DIR/../shunit2/shunit2 diff --git a/tests/nemo_text_processing/vi/test_telephone.py b/tests/nemo_text_processing/vi/test_telephone.py deleted file mode 100644 index f1fa16578abe..000000000000 --- a/tests/nemo_text_processing/vi/test_telephone.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestTelephone: - inverse_normalizer = ( - InverseNormalizer(lang='vi', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('vi/data_inverse_text_normalization/test_cases_telephone.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/vi/test_time.py b/tests/nemo_text_processing/vi/test_time.py deleted file mode 100644 index 9502cad5428e..000000000000 --- a/tests/nemo_text_processing/vi/test_time.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestTime: - inverse_normalizer = ( - InverseNormalizer(lang='vi', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('vi/data_inverse_text_normalization/test_cases_time.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/vi/test_whitelist.py b/tests/nemo_text_processing/vi/test_whitelist.py deleted file mode 100644 index 2ab06085f99e..000000000000 --- a/tests/nemo_text_processing/vi/test_whitelist.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestWhitelist: - inverse_normalizer = ( - InverseNormalizer(lang='vi', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('vi/data_inverse_text_normalization/test_cases_whitelist.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/vi/test_word.py b/tests/nemo_text_processing/vi/test_word.py deleted file mode 100644 index e247dff796e6..000000000000 --- a/tests/nemo_text_processing/vi/test_word.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pytest -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - -try: - from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer - - PYNINI_AVAILABLE = True -except (ImportError, ModuleNotFoundError): - PYNINI_AVAILABLE = False - - -class TestWord: - inverse_normalizer = ( - InverseNormalizer(lang='vi', cache_dir=CACHE_DIR, overwrite_cache=False) if PYNINI_AVAILABLE else None - ) - - @parameterized.expand(parse_test_case_file('vi/data_inverse_text_normalization/test_cases_word.txt')) - @pytest.mark.skipif( - not PYNINI_AVAILABLE, - reason="`pynini` not installed, please install via nemo_text_processing/pynini_install.sh", - ) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_denorm(self, test_input, expected): - pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) - assert pred == expected diff --git a/tests/nemo_text_processing/zh/__init__.py b/tests/nemo_text_processing/zh/__init__.py deleted file mode 100644 index a1cf281f0908..000000000000 --- a/tests/nemo_text_processing/zh/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/tests/nemo_text_processing/zh/data_text_normalization/test_cases_char.txt b/tests/nemo_text_processing/zh/data_text_normalization/test_cases_char.txt deleted file mode 100644 index 4fedd2cd92fc..000000000000 --- a/tests/nemo_text_processing/zh/data_text_normalization/test_cases_char.txt +++ /dev/null @@ -1,2 +0,0 @@ -你~你 -好~好 \ No newline at end of file diff --git a/tests/nemo_text_processing/zh/data_text_normalization/test_cases_date.txt b/tests/nemo_text_processing/zh/data_text_normalization/test_cases_date.txt deleted file mode 100644 index ec0c76e8a8a2..000000000000 --- a/tests/nemo_text_processing/zh/data_text_normalization/test_cases_date.txt +++ /dev/null @@ -1,5 +0,0 @@ -2002/01/28~二零零二年一月二十八日 -2002-01-28~二零零二年一月二十八日 -2002.01.28~二零零二年一月二十八日 -2001/11~二零零一年十一月 -03/12~零三年十二月 \ No newline at end of file diff --git a/tests/nemo_text_processing/zh/data_text_normalization/test_cases_fraction.txt b/tests/nemo_text_processing/zh/data_text_normalization/test_cases_fraction.txt deleted file mode 100644 index ccfb5db1718d..000000000000 --- a/tests/nemo_text_processing/zh/data_text_normalization/test_cases_fraction.txt +++ /dev/null @@ -1,3 +0,0 @@ -1/5~五分之一 -1/16~十六分之一 -3/2~二分之三 \ No newline at end of file diff --git a/tests/nemo_text_processing/zh/data_text_normalization/test_cases_math.txt b/tests/nemo_text_processing/zh/data_text_normalization/test_cases_math.txt deleted file mode 100644 index 6b34a5c107c8..000000000000 --- a/tests/nemo_text_processing/zh/data_text_normalization/test_cases_math.txt +++ /dev/null @@ -1,3 +0,0 @@ -78:96~七十八比九十六 --23℃~负二十三摄氏度 -±2~正负二 \ No newline at end of file diff --git a/tests/nemo_text_processing/zh/data_text_normalization/test_cases_measure.txt b/tests/nemo_text_processing/zh/data_text_normalization/test_cases_measure.txt deleted file mode 100644 index f392d216fcd2..000000000000 --- a/tests/nemo_text_processing/zh/data_text_normalization/test_cases_measure.txt +++ /dev/null @@ -1,6 +0,0 @@ -25kg~二十五千克 -38°C~三十八摄氏度 -120m²~一百二十平方米 -10ms~十毫秒 -6.3%~百分之六点三 -0.4%~百分之零点四 \ No newline at end of file diff --git a/tests/nemo_text_processing/zh/data_text_normalization/test_cases_money.txt b/tests/nemo_text_processing/zh/data_text_normalization/test_cases_money.txt deleted file mode 100644 index c66297e52686..000000000000 --- a/tests/nemo_text_processing/zh/data_text_normalization/test_cases_money.txt +++ /dev/null @@ -1,7 +0,0 @@ -¥1~一人民币 -USD1~一美元 -£1~一英镑 -HKD20~二十港元 -J¥1.5~一点五日元 -€6~六欧元 -CAD3~三加元 \ No newline at end of file diff --git a/tests/nemo_text_processing/zh/data_text_normalization/test_cases_preprocess.txt b/tests/nemo_text_processing/zh/data_text_normalization/test_cases_preprocess.txt deleted file mode 100644 index e1b592ebcda0..000000000000 --- a/tests/nemo_text_processing/zh/data_text_normalization/test_cases_preprocess.txt +++ /dev/null @@ -1 +0,0 @@ -你啊好~你好 diff --git a/tests/nemo_text_processing/zh/data_text_normalization/test_cases_time.txt b/tests/nemo_text_processing/zh/data_text_normalization/test_cases_time.txt deleted file mode 100644 index 2d6b788fff41..000000000000 --- a/tests/nemo_text_processing/zh/data_text_normalization/test_cases_time.txt +++ /dev/null @@ -1,4 +0,0 @@ -12:00~十二点 -5:02~五点零二分 -5:35:36~五点三十五分三十六秒 -8:00a.m.~上午八点 \ No newline at end of file diff --git a/tests/nemo_text_processing/zh/test_char.py b/tests/nemo_text_processing/zh/test_char.py deleted file mode 100644 index 1ca553eca3d0..000000000000 --- a/tests/nemo_text_processing/zh/test_char.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from nemo_text_processing.text_normalization.normalize import Normalizer -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - - -class TestChar: - normalizer_zh = Normalizer(lang='zh', cache_dir=CACHE_DIR, overwrite_cache=False, input_case='cased') - - @parameterized.expand(parse_test_case_file('zh/data_text_normalization/test_cases_char.txt')) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm_char(self, test_input, expected): - preds = self.normalizer_zh.normalize(test_input) - assert expected == preds diff --git a/tests/nemo_text_processing/zh/test_date.py b/tests/nemo_text_processing/zh/test_date.py deleted file mode 100644 index d8079e3a6b3a..000000000000 --- a/tests/nemo_text_processing/zh/test_date.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from nemo_text_processing.text_normalization.normalize import Normalizer -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - - -class TestDate: - normalizer_zh = Normalizer(lang='zh', cache_dir=CACHE_DIR, overwrite_cache=False, input_case='cased') - - @parameterized.expand(parse_test_case_file('zh/data_text_normalization/test_cases_date.txt')) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm_date(self, test_input, expected): - preds = self.normalizer_zh.normalize(test_input) - assert expected == preds diff --git a/tests/nemo_text_processing/zh/test_fraction.py b/tests/nemo_text_processing/zh/test_fraction.py deleted file mode 100644 index 03b508b2156e..000000000000 --- a/tests/nemo_text_processing/zh/test_fraction.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from nemo_text_processing.text_normalization.normalize import Normalizer -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - - -class TestFraction: - normalizer_zh = Normalizer(lang='zh', cache_dir=CACHE_DIR, overwrite_cache=False, input_case='cased') - - @parameterized.expand(parse_test_case_file('zh/data_text_normalization/test_cases_fraction.txt')) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm_fraction(self, test_input, expected): - preds = self.normalizer_zh.normalize(test_input) - assert expected == preds diff --git a/tests/nemo_text_processing/zh/test_math.py b/tests/nemo_text_processing/zh/test_math.py deleted file mode 100644 index e8740aa2dcbb..000000000000 --- a/tests/nemo_text_processing/zh/test_math.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from nemo_text_processing.text_normalization.normalize import Normalizer -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - - -class TestMath: - normalizer_zh = Normalizer(lang='zh', cache_dir=CACHE_DIR, overwrite_cache=False, input_case='cased') - - @parameterized.expand(parse_test_case_file('zh/data_text_normalization/test_cases_math.txt')) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm_math(self, test_input, expected): - preds = self.normalizer_zh.normalize(test_input) - assert expected == preds diff --git a/tests/nemo_text_processing/zh/test_measure.py b/tests/nemo_text_processing/zh/test_measure.py deleted file mode 100644 index 32df2885515c..000000000000 --- a/tests/nemo_text_processing/zh/test_measure.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from nemo_text_processing.text_normalization.normalize import Normalizer -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - - -class TestMeasure: - normalizer_zh = Normalizer(lang='zh', cache_dir=CACHE_DIR, overwrite_cache=False, input_case='cased') - - @parameterized.expand(parse_test_case_file('zh/data_text_normalization/test_cases_measure.txt')) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm_measure(self, test_input, expected): - preds = self.normalizer_zh.normalize(test_input) - assert expected == preds diff --git a/tests/nemo_text_processing/zh/test_money.py b/tests/nemo_text_processing/zh/test_money.py deleted file mode 100644 index d06a2b812942..000000000000 --- a/tests/nemo_text_processing/zh/test_money.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from nemo_text_processing.text_normalization.normalize import Normalizer -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - - -class TestMoney: - normalizer_zh = Normalizer(lang='zh', cache_dir=CACHE_DIR, overwrite_cache=False, input_case='cased') - - @parameterized.expand(parse_test_case_file('zh/data_text_normalization/test_cases_money.txt')) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm_money(self, test_input, expected): - preds = self.normalizer_zh.normalize(test_input) - assert expected == preds diff --git a/tests/nemo_text_processing/zh/test_preprocess.py b/tests/nemo_text_processing/zh/test_preprocess.py deleted file mode 100644 index f817517b56a5..000000000000 --- a/tests/nemo_text_processing/zh/test_preprocess.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from nemo_text_processing.text_normalization.normalize import Normalizer -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - - -class TestPreprocess: - normalizer_zh = Normalizer(lang='zh', cache_dir=CACHE_DIR, overwrite_cache=False, input_case='cased') - - @parameterized.expand(parse_test_case_file('zh/data_text_normalization/test_cases_preprocess.txt')) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm_preprocess(self, test_input, expected): - preds = self.normalizer_zh.normalize(test_input) - assert expected == preds diff --git a/tests/nemo_text_processing/zh/test_sparrowhawk_normalization.sh b/tests/nemo_text_processing/zh/test_sparrowhawk_normalization.sh deleted file mode 100644 index 6c3a6598f515..000000000000 --- a/tests/nemo_text_processing/zh/test_sparrowhawk_normalization.sh +++ /dev/null @@ -1,60 +0,0 @@ -#! /bin/sh - -PROJECT_DIR=/workspace/tests - -runtest () { - input=$1 - cd /workspace/sparrowhawk/documentation/grammars - - # read test file - while read testcase; do - IFS='~' read written spoken <<< $testcase - # replace non breaking space with breaking space - denorm_pred=$(echo $written | normalizer_main --config=sparrowhawk_configuration.ascii_proto 2>&1 | tail -n 1 | sed 's/\xC2\xA0/ /g') - - # # trim white space - spoken="$(echo -e "${spoken}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - denorm_pred="$(echo -e "${denorm_pred}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - - # input expected actual - assertEquals "$written" "$spoken" "$denorm_pred" - done < "$input" -} - -testTNMoneyText() { - input=$PROJECT_DIR/zh/data_text_normalization/test_cases_money.txt - runtest $input -} -testTNCharText() { - input=$PROJECT_DIR/zh/data_text_normalization/test_cases_char.txt - runtest $input -} -testTNTimeText() { - input=$PROJECT_DIR/zh/data_text_normalization/test_cases_time.txt - runtest $input -} -testTNDateText() { - input=$PROJECT_DIR/zh/data_text_normalization/test_cases_date.txt - runtest $input -} -# testTNMathText() { -# input=$PROJECT_DIR/zh/data_text_normalization/test_cases_math.txt -# runtest $input -# } -testTNFractionText() { - input=$PROJECT_DIR/zh/data_text_normalization/test_cases_fraction.txt - runtest $input -} - -# testTNPreprocessText() { -# input=$PROJECT_DIR/zh/data_text_normalization/test_cases_preprocess.txt -# runtest $input -# } -testTNMeasureText() { - input=$PROJECT_DIR/zh/data_text_normalization/test_cases_measure.txt - runtest $input -} - - -# Load shUnit2 -. $PROJECT_DIR/../shunit2/shunit2 diff --git a/tests/nemo_text_processing/zh/test_time.py b/tests/nemo_text_processing/zh/test_time.py deleted file mode 100644 index d36737afb751..000000000000 --- a/tests/nemo_text_processing/zh/test_time.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from nemo_text_processing.text_normalization.normalize import Normalizer -from parameterized import parameterized - -from ..utils import CACHE_DIR, parse_test_case_file - - -class TestTime: - normalizer_zh = Normalizer(lang='zh', cache_dir=CACHE_DIR, overwrite_cache=False, input_case='cased') - - @parameterized.expand(parse_test_case_file('zh/data_text_normalization/test_cases_time.txt')) - @pytest.mark.run_only_on('CPU') - @pytest.mark.unit - def test_norm_time(self, test_input, expected): - preds = self.normalizer_zh.normalize(test_input) - assert expected == preds diff --git a/tools/ctc_segmentation/requirements.txt b/tools/ctc_segmentation/requirements.txt index cfcd12173eda..2c6cdaa1e755 100644 --- a/tools/ctc_segmentation/requirements.txt +++ b/tools/ctc_segmentation/requirements.txt @@ -1,2 +1,3 @@ ctc_segmentation==1.7.1 num2words +nemo_text_processing==0.1.6rc0 diff --git a/tools/text_processing_deployment/Dockerfile b/tools/text_processing_deployment/Dockerfile deleted file mode 100644 index 7d3ea46b2da2..000000000000 --- a/tools/text_processing_deployment/Dockerfile +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Dockerfile for C++ (inverse) text normalization backend Sparrowhawk https://github.com/google/sparrowhawk - -# set base image (host OS) -FROM conda/miniconda3 - -# set the working directory in the container -WORKDIR /workspace - -# install dependencies -RUN conda install conda-build -y -RUN apt-get update && apt-get install -y --reinstall build-essential && apt-get upgrade -y && apt-get install -y git && apt-get install make -RUN git clone https://github.com/google/re2 -RUN cd re2 && make && make install -RUN apt-get install build-essential -y && apt-get install wget -y -RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v2.5.0/protobuf-2.5.0.tar.gz -RUN tar xzvf protobuf-2.5.0.tar.gz -RUN cd protobuf-2.5.0 && ./configure && make && make install && ldconfig -RUN conda install -c conda-forge thrax=1.3.4 -y -RUN git clone https://github.com/yzhang123/sparrowhawk.git -RUN cd sparrowhawk && git checkout test && apt-get install -y autoconf && bash autoreconf && ./configure && make && make install && ldconfig -RUN git clone https://github.com/kward/shunit2.git -RUN echo "DONE" \ No newline at end of file diff --git a/tools/text_processing_deployment/README.rst b/tools/text_processing_deployment/README.rst deleted file mode 100644 index dfeadd12fda5..000000000000 --- a/tools/text_processing_deployment/README.rst +++ /dev/null @@ -1,10 +0,0 @@ -**Text Processing Deployment** -========================================= - -Introduction ------------- - -This folder provides scripts to deploy WFST-based grammars in `NeMo Text Processing `_ for -for production. - -See `documentation `_ for details. \ No newline at end of file diff --git a/tools/text_processing_deployment/docker/build.sh b/tools/text_processing_deployment/docker/build.sh deleted file mode 100644 index a179572bd77e..000000000000 --- a/tools/text_processing_deployment/docker/build.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FORCE_REBUILD=${1:-""} -docker build . --rm -t sparrowhawk $FORCE_REBUILD \ No newline at end of file diff --git a/tools/text_processing_deployment/docker/launch.sh b/tools/text_processing_deployment/docker/launch.sh deleted file mode 100644 index 227a27dccd2a..000000000000 --- a/tools/text_processing_deployment/docker/launch.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -MODE=${1:-"export"} -LANGUAGE=${2:-"en"} -SCRIPT_DIR=$(cd $(dirname $0); pwd) -: ${CLASSIFY_DIR:="$SCRIPT_DIR/../$LANGUAGE/classify"} -: ${VERBALIZE_DIR:="$SCRIPT_DIR/../$LANGUAGE/verbalize"} -: ${CMD:=${3:-"/bin/bash"}} - -MOUNTS="" -MOUNTS+=" -v $CLASSIFY_DIR:/workspace/sparrowhawk/documentation/grammars/en_toy/classify" -MOUNTS+=" -v $VERBALIZE_DIR:/workspace/sparrowhawk/documentation/grammars/en_toy/verbalize" - -WORK_DIR="/workspace/sparrowhawk/documentation/grammars" -if [[ $MODE == "test_tn_grammars" ]]; then - CMD="bash test_sparrowhawk_normalization.sh" - WORK_DIR="/workspace/tests/${LANGUAGE}" -elif [[ $MODE == "test_itn_grammars" ]]; then - CMD="bash test_sparrowhawk_inverse_text_normalization.sh" - WORK_DIR="/workspace/tests/${LANGUAGE}" -fi - -echo $MOUNTS -docker run -it --rm \ - --shm-size=4g \ - --ulimit memlock=-1 \ - --ulimit stack=67108864 \ - $MOUNTS \ - -v $SCRIPT_DIR/../../../tests/nemo_text_processing/:/workspace/tests/ \ - -w $WORK_DIR \ - sparrowhawk:latest $CMD \ No newline at end of file diff --git a/tools/text_processing_deployment/export_grammars.sh b/tools/text_processing_deployment/export_grammars.sh deleted file mode 100644 index 981caadadab5..000000000000 --- a/tools/text_processing_deployment/export_grammars.sh +++ /dev/null @@ -1,80 +0,0 @@ -#!/bin/bash - -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This script compiles and exports WFST-grammars from nemo_text_processing, builds C++ production backend Sparrowhawk (https://github.com/google/sparrowhawk) in docker, -# plugs grammars into Sparrowhawk and returns prompt inside docker. -# For inverse text normalization run: -# bash export_grammars.sh --GRAMMARS=itn_grammars --LANGUAGE=en -# echo "two dollars fifty" | ../../src/bin/normalizer_main --config=sparrowhawk_configuration.ascii_proto -# For text normalization run: -# bash export_grammars.sh --GRAMMARS=tn_grammars --LANGUAGE=en -# echo "\$2.5" | ../../src/bin/normalizer_main --config=sparrowhawk_configuration.ascii_proto -# -# To test TN grammars, run: -# bash export_grammars.sh --GRAMMARS=tn_grammars --LANGUAGE=en --MODE=test -# -# To test ITN grammars, run: -# bash export_grammars.sh --GRAMMARS=itn_grammars --LANGUAGE=en --MODE=test - -GRAMMARS="itn_grammars" # tn_grammars -INPUT_CASE="cased" # lower_cased, only for tn_grammars -LANGUAGE="en" # language, {'en', 'es', 'de','zh'} supports both TN and ITN, {'pt', 'ru', 'fr', 'vi'} supports ITN only -MODE="export" -OVERWRITE_CACHE="True" # Set to False to re-use .far files -FORCE_REBUILD="False" # Set to True to re-build docker file - -for ARG in "$@" -do - key=$(echo $ARG | cut -f1 -d=) - value=$(echo $ARG | cut -f2 -d=) - - if [[ $key == *"--"* ]]; then - v="${key/--/}" - declare $v="${value}" - fi -done - - -CACHE_DIR=${LANGUAGE} -echo "GRAMMARS = $GRAMMARS" -echo "MODE = $MODE" -echo "LANGUAGE = $LANGUAGE" -echo "INPUT_CASE = $INPUT_CASE" -echo "CACHE_DIR = $CACHE_DIR" -echo "OVERWRITE_CACHE = $OVERWRITE_CACHE" -echo "FORCE_REBUILD = $FORCE_REBUILD" - - -if [[ ${OVERWRITE_CACHE,,} == "true" ]]; then - OVERWRITE_CACHE="--overwrite_cache " - python3 pynini_export.py --output_dir=. --grammars=${GRAMMARS} --input_case=${INPUT_CASE} --language=${LANGUAGE} --cache_dir=${CACHE_DIR} ${OVERWRITE_CACHE}|| exit 1 - else OVERWRITE_CACHE="" -fi - -if [[ ${FORCE_REBUILD,,} == "true" ]]; then - FORCE_REBUILD="--no-cache" - else FORCE_REBUILD="" -fi - -find . -name "Makefile" -type f -delete -bash docker/build.sh $FORCE_REBUILD - -if [[ $MODE == "test" ]]; then - MODE=${MODE}_${GRAMMARS} -fi - -bash docker/launch.sh $MODE $LANGUAGE diff --git a/tools/text_processing_deployment/pynini_export.py b/tools/text_processing_deployment/pynini_export.py deleted file mode 100644 index e0f5935a3d0c..000000000000 --- a/tools/text_processing_deployment/pynini_export.py +++ /dev/null @@ -1,173 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# Copyright 2015 and onwards Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import os -import time -from argparse import ArgumentParser - -import pynini -from nemo_text_processing.text_normalization.en.graph_utils import generator_main - -# This script exports compiled grammars inside nemo_text_processing into OpenFst finite state archive files -# tokenize_and_classify.far and verbalize.far for production purposes - - -def itn_grammars(**kwargs): - d = {} - d['classify'] = { - 'TOKENIZE_AND_CLASSIFY': ITNClassifyFst( - cache_dir=kwargs["cache_dir"], overwrite_cache=kwargs["overwrite_cache"] - ).fst - } - d['verbalize'] = {'ALL': ITNVerbalizeFst().fst, 'REDUP': pynini.accep("REDUP")} - return d - - -def tn_grammars(**kwargs): - d = {} - d['classify'] = { - 'TOKENIZE_AND_CLASSIFY': TNClassifyFst( - input_case=kwargs["input_case"], - deterministic=True, - cache_dir=kwargs["cache_dir"], - overwrite_cache=kwargs["overwrite_cache"], - ).fst - } - d['verbalize'] = {'ALL': TNVerbalizeFst(deterministic=True).fst, 'REDUP': pynini.accep("REDUP")} - return d - - -def export_grammars(output_dir, grammars): - """ - Exports tokenizer_and_classify and verbalize Fsts as OpenFst finite state archive (FAR) files. - - Args: - output_dir: directory to export FAR files to. Subdirectories will be created for tagger and verbalizer respectively. - grammars: grammars to be exported - """ - - for category, graphs in grammars.items(): - out_dir = os.path.join(output_dir, category) - if not os.path.exists(out_dir): - os.makedirs(out_dir) - time.sleep(1) - if category == "classify": - category = "tokenize_and_classify" - generator_main(f"{out_dir}/{category}.far", graphs) - - -def parse_args(): - parser = ArgumentParser() - parser.add_argument("--output_dir", help="output directory for grammars", required=True, type=str) - parser.add_argument( - "--language", help="language", choices=["en", "de", "es", "pt", "ru", 'fr', 'vi', 'zh'], type=str, default='en' - ) - parser.add_argument( - "--grammars", help="grammars to be exported", choices=["tn_grammars", "itn_grammars"], type=str, required=True - ) - parser.add_argument( - "--input_case", help="input capitalization", choices=["lower_cased", "cased"], default="cased", type=str - ) - parser.add_argument("--overwrite_cache", help="set to True to re-create .far grammar files", action="store_true") - parser.add_argument( - "--cache_dir", - help="path to a dir with .far grammar file. Set to None to avoid using cache", - default=None, - type=str, - ) - return parser.parse_args() - - -if __name__ == '__main__': - args = parse_args() - - if args.language in ['pt', 'ru', 'fr', 'vi'] and args.grammars == 'tn_grammars': - raise ValueError('Only ITN grammars could be deployed in Sparrowhawk for the selected languages.') - - if args.language == 'en': - from nemo_text_processing.inverse_text_normalization.en.taggers.tokenize_and_classify import ( - ClassifyFst as ITNClassifyFst, - ) - from nemo_text_processing.inverse_text_normalization.en.verbalizers.verbalize import ( - VerbalizeFst as ITNVerbalizeFst, - ) - from nemo_text_processing.text_normalization.en.taggers.tokenize_and_classify import ( - ClassifyFst as TNClassifyFst, - ) - from nemo_text_processing.text_normalization.en.verbalizers.verbalize import VerbalizeFst as TNVerbalizeFst - elif args.language == 'de': - from nemo_text_processing.inverse_text_normalization.de.taggers.tokenize_and_classify import ( - ClassifyFst as ITNClassifyFst, - ) - from nemo_text_processing.inverse_text_normalization.de.verbalizers.verbalize import ( - VerbalizeFst as ITNVerbalizeFst, - ) - from nemo_text_processing.text_normalization.de.taggers.tokenize_and_classify import ( - ClassifyFst as TNClassifyFst, - ) - from nemo_text_processing.text_normalization.de.verbalizers.verbalize import VerbalizeFst as TNVerbalizeFst - elif args.language == 'ru': - from nemo_text_processing.inverse_text_normalization.ru.taggers.tokenize_and_classify import ( - ClassifyFst as ITNClassifyFst, - ) - from nemo_text_processing.inverse_text_normalization.ru.verbalizers.verbalize import ( - VerbalizeFst as ITNVerbalizeFst, - ) - elif args.language == 'es': - from nemo_text_processing.inverse_text_normalization.es.taggers.tokenize_and_classify import ( - ClassifyFst as ITNClassifyFst, - ) - from nemo_text_processing.inverse_text_normalization.es.verbalizers.verbalize import ( - VerbalizeFst as ITNVerbalizeFst, - ) - from nemo_text_processing.text_normalization.es.taggers.tokenize_and_classify import ( - ClassifyFst as TNClassifyFst, - ) - from nemo_text_processing.text_normalization.es.verbalizers.verbalize import VerbalizeFst as TNVerbalizeFst - elif args.language == 'pt': - from nemo_text_processing.inverse_text_normalization.pt.taggers.tokenize_and_classify import ( - ClassifyFst as ITNClassifyFst, - ) - from nemo_text_processing.inverse_text_normalization.pt.verbalizers.verbalize import ( - VerbalizeFst as ITNVerbalizeFst, - ) - elif args.language == 'fr': - from nemo_text_processing.inverse_text_normalization.fr.taggers.tokenize_and_classify import ( - ClassifyFst as ITNClassifyFst, - ) - from nemo_text_processing.inverse_text_normalization.fr.verbalizers.verbalize import ( - VerbalizeFst as ITNVerbalizeFst, - ) - elif args.language == 'vi': - from nemo_text_processing.inverse_text_normalization.vi.taggers.tokenize_and_classify import ( - ClassifyFst as ITNClassifyFst, - ) - from nemo_text_processing.inverse_text_normalization.vi.verbalizers.verbalize import ( - VerbalizeFst as ITNVerbalizeFst, - ) - elif args.language == 'zh': - from nemo_text_processing.text_normalization.zh.taggers.tokenize_and_classify import ( - ClassifyFst as TNClassifyFst, - ) - from nemo_text_processing.text_normalization.zh.verbalizers.verbalize import VerbalizeFst as TNVerbalizeFst - - output_dir = os.path.join(args.output_dir, args.language) - export_grammars( - output_dir=output_dir, - grammars=locals()[args.grammars]( - input_case=args.input_case, cache_dir=args.cache_dir, overwrite_cache=args.overwrite_cache - ), - ) diff --git a/tutorials/text_processing/ITN_with_Thutmose_Tagger.ipynb b/tutorials/text_processing/ITN_with_Thutmose_Tagger.ipynb deleted file mode 100644 index b72cee51003b..000000000000 --- a/tutorials/text_processing/ITN_with_Thutmose_Tagger.ipynb +++ /dev/null @@ -1,1140 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "VFOY_ljrReXk" - }, - "outputs": [], - "source": [ - "\"\"\"\n", - "You can run either this notebook locally (if you have all the dependencies and a GPU) or on Google Colab.\n", - "\n", - "Instructions for setting up Colab are as follows:\n", - "1. Open a new Python 3 notebook.\n", - "2. Import this notebook from GitHub (File -> Upload Notebook -> \"GITHUB\" tab -> copy/paste GitHub URL)\n", - "3. Connect to an instance with a GPU (Runtime -> Change runtime type -> select \"GPU\" for hardware accelerator)\n", - "4. Run this cell to set up dependencies.\n", - "\"\"\"\n", - "\n", - "import os\n", - "\n", - "# install NeMo\n", - "BRANCH = 'main'\n", - "\n", - "GITHUB_ACCOUNT = 'NVIDIA' # change this if using a fork\n", - "\n", - "# either provide a path to local NeMo repository with NeMo already installed or git clone\n", - "\n", - "# option #1: local path to NeMo repo with NeMo already installed\n", - "NEMO_DIR_PATH = \"NeMo\"\n", - "\n", - "# option #2: download NeMo repo\n", - "if 'google.colab' in str(get_ipython()) or not os.path.exists(NEMO_DIR_PATH):\n", - " !git clone -b $BRANCH https://github.com/{GITHUB_ACCOUNT}/NeMo\n", - " %cd NeMo\n", - " !python -m pip install git+https://github.com/{GITHUB_ACCOUNT}/NeMo.git@$BRANCH#egg=nemo_toolkit[all]\n", - " %cd .." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "V8RfIztARxKH" - }, - "outputs": [], - "source": [ - "# If you're not using Colab, you might need to upgrade jupyter notebook to avoid the following error:\n", - "# 'ImportError: IProgress not found. Please update jupyter and ipywidgets.'\n", - "\n", - "!pip install ipywidgets\n", - "!jupyter nbextension enable --py widgetsnbextension\n", - "\n", - "# Please restart the kernel after running this cell" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "oaTOPJHhTteF" - }, - "source": [ - "# Task Description\n", - "**Inverse text normalization (ITN)** is an important post-processing step within an automatic speech recognition (ASR) system. \n", - "ITN transforms spoken-domain text into its written form:\n", - "\n", - "> **Input:** \"on may third we paid one hundred and twenty three dollars\"\n", - "\n", - "> **Output:** \"on may 3 we paid \\$123\".\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "sxo-kHP7frEX" - }, - "source": [ - "# Thutmose Tagger approach\n", - "We aim to do the following:\n", - "1. Align ITN expressions from the [Google Text normalization dataset](https://www.kaggle.com/richardwilliamsproat/text-normalization-for-english-russian-and-polish) on a granular level using [GIZA++](https://github.com/moses-smt/giza-pp), to get a monotonic one-to-one correspondence between each *spoken word* and corresponding *fragments* in written form. \n", - "2. Get a restricted vocabulary of target fragments (tags) that can cover most spoken-written pair conversions.\n", - "3. Build training dataset, where the input is the sentence in spoken form and the output is tags for all input words. \n", - "4. Train a token classifier neural model (see Figure below). \n", - "5. Apply a simple postprocessing procedure upon the tag sequence to get the final output" - ] - }, - { - "cell_type": "markdown", - "source": [ - "![Thutmose Tagger Architecture](images/thutmose_tagger_architecture.png)" - ], - "metadata": { - "id": "RG403l1gKyRy" - } - }, - { - "cell_type": "markdown", - "metadata": { - "id": "aMPeNtAracI9" - }, - "source": [ - "# Dataset\n", - "\n", - "The full English part of [Google Text normalization dataset](https://www.kaggle.com/richardwilliamsproat/text-normalization-for-english-russian-and-polish) consists of 1.1 billion words. For this tutorial we use only small subset of it.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "EqnuAgNNcVY-" - }, - "outputs": [], - "source": [ - "!rm -r en_data_small\n", - "!wget \"https://multilangaudiosamples.s3.us-east-2.amazonaws.com/en_data_small.zip\"\n", - "!unzip en_data_small" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "CkJ3LLaxRNFh" - }, - "outputs": [], - "source": [ - "## actually we do not need separate dev and test data in this tutorial, so just copy it \n", - "!cp -r en_data_small/test en_data_small/dev" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "HLoXgnpMVACe" - }, - "source": [ - "\n", - "The dataset contains unnormalized (i.e. written form) and normalized (i.e. spoken form) sentence pairs that are aligned *on a phrase-level*. The normalized text is synthetic - obtained with the [Kestrel TTS text normalization system](https://www.cambridge.org/core/journals/natural-language-engineering/article/abs/kestrel-tts-text-normalization-system/F0C18A3F596B75D83B75C479E23795DA), so it’s not considered 100% correct.\n", - "\n", - "```\n", - "PLAIN Retrieved \n", - "DATE 18 April 2013 the eighteenth of april twenty thirteen\n", - "PUNCT . sil\n", - " \n", - "PLAIN Neuhorst \n", - "PUNCT ( sil\n", - "PLAIN Canada \n", - "DATE 2006 two thousand six\n", - "PLAIN Census \n", - "PLAIN population \n", - "CARDINAL 126 one hundred twenty six\n", - "PUNCT ) sil\n", - "PLAIN is \n", - "PLAIN a \n", - "PLAIN small \n", - "PLAIN hamlet \n", - "PLAIN in \n", - "PLAIN Saskatchewan \n", - "PUNCT , sil\n", - "PLAIN Canada \n", - "PLAIN about \n", - "CARDINAL 30 thirty\n", - "PLAIN minutes \n", - "PLAIN north \n", - "PLAIN of \n", - "PLAIN Saskatoon \n", - "PUNCT . sil\n", - " \n", - "```\n", - "\n", - "The following classes appear in the dataset:\n", - "* ADDRESS\n", - "* CARDINAL\n", - "* DATE\n", - "* DECIMAL\n", - "* DIGIT\n", - "* ELECTRONIC\n", - "* FRACTION\n", - "* LETTERS\n", - "* MEASURE\n", - "* MONEY\n", - "* ORDINAL\n", - "* PLAIN\n", - "* PUNCT\n", - "* TELEPHONE\n", - "* TIME\n", - "* VERBATIM\n", - "\n", - "\n", - "\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "source": [ - "# 1. Align ITN expressions on a granular-level" - ], - "metadata": { - "id": "rewQY1pbPeq8" - } - }, - { - "cell_type": "markdown", - "source": [ - "Let's download and compile GIZA++ as we will need it soon" - ], - "metadata": { - "id": "5cLXx7qdPpUK" - } - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "LNLjIDUJdY5f" - }, - "outputs": [], - "source": [ - "!git clone https://github.com/moses-smt/giza-pp.git giza-pp\n", - "%cd giza-pp\n", - "!ls\n", - "!make\n", - "%cd .." - ] - }, - { - "cell_type": "markdown", - "source": [ - "Do some imports" - ], - "metadata": { - "id": "2AfIeiu_P0Ik" - } - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "mz8_O4kfS0fH" - }, - "outputs": [], - "source": [ - "from nemo.collections import nlp as nemo_nlp\n", - "from nemo.utils.exp_manager import exp_manager\n", - "import nemo\n", - "\n", - "import wget \n", - "import torch\n", - "import pytorch_lightning as pl\n", - "from omegaconf import OmegaConf\n", - "import pandas as pd" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "uYW_qsDgkhCw" - }, - "source": [ - "First we need to prepare the input data for the aligner (GIZA++).\n", - "\n", - "We regard the corpus of ITN phrase-pairs as a parallel corpus. Parallel means that each pair has an equivalent meaning, though they may consist of any number of tokens and the task of an aligner is to find which source tokens corresponds to which target tokens.\n", - "The spoken phrase is tokenized by word boundary, while the written phrase is tokenized as follows: \n", - "1. All alphabetic sequences are separate tokens\n", - "2. In numeric sequences each character is a separate token.\n", - "3. All non-alphanumeric characters are separate tokens.\n", - "4. We add an underscore symbol to mark the beginning and end of a\n", - "sequence for future detokenization. \n", - "\n", - "Example\n", - "> **Spoken:** `january thirtieth two thousand five`\n", - "\n", - "> **Written initial:** `jan 30, 2005`\n", - "\n", - "> **Written tokenized**: `_jan_ _3 0 , 2 0 0 5_`" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "miXYxOv_mNVo" - }, - "source": [ - "The script [prepare_corpora_for_alignment.py](https://github.com/NVIDIA/NeMo/blob/main/examples/nlp/text_normalization_as_tagging/dataset_preparation/prepare_corpora_for_alignment.py) prepares the described parallel corpora. It extracts all unique ITN phrase-pairs from the Google TN dataset, tokenizes them as described above and stores in separate folders for each semiotic class. It also generates a bash script for running the alignment. At the end it prints how many examples it has found:\n", - "```\n", - "content/alignment/punct has 920953 instances\n", - "content/alignment/date has 150499 instances\n", - "content/alignment/letters has 68340 instances\n", - "content/alignment/cardinal has 61029 instances\n", - "...\n", - "``` " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "_A12y5zNn4O0" - }, - "outputs": [], - "source": [ - "WORK_DIR=!pwd # returns array containing a single path, \n", - "WORK_DIR=WORK_DIR[0]\n", - "\n", - "CORPUS_LANG=\"en\"\n", - "if 'google.colab' in str(get_ipython()) or not os.path.exists(NEMO_DIR_PATH):\n", - " NEMO_PATH=WORK_DIR + \"/NeMo\"\n", - "else:\n", - " NEMO_PATH=NEMO_DIR_PATH\n", - "GIZA_BIN_DIR=WORK_DIR + \"/giza-pp/GIZA++-v2\"\n", - "MCKLS_BINARY=WORK_DIR + \"/giza-pp/mkcls-v2/mkcls\"\n", - "CORPUS_DIR=WORK_DIR + \"/en_data_small\"\n", - "ALIGNMENT_DIR=WORK_DIR + \"/alignment\"\n", - "\n", - "!mkdir {ALIGNMENT_DIR}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "BguRSoIXesFx" - }, - "outputs": [], - "source": [ - "!python {NEMO_PATH}/examples/nlp/text_normalization_as_tagging/dataset_preparation/prepare_corpora_for_alignment.py \\\n", - " --data_dir={CORPUS_DIR} \\\n", - " --out_dir={ALIGNMENT_DIR} \\\n", - " --giza_dir={GIZA_BIN_DIR} \\\n", - " --mckls_binary={MCKLS_BINARY} \\\n", - " --lang={CORPUS_LANG}" - ] - }, - { - "cell_type": "markdown", - "source": [ - "Let's exclude punct class, as our itn task doesn't require to restore punctuation marks" - ], - "metadata": { - "id": "v8LscfJrLUeg" - } - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "VNOQ4nW2yF6I" - }, - "outputs": [], - "source": [ - "!rm -r {ALIGNMENT_DIR}/punct\n" - ] - }, - { - "cell_type": "markdown", - "source": [ - "Let's run GIZA++ alignment. \n", - "In this tutorial we only work with three semiotic classes: date, letters and cardinal (in real setting all classes are used, excluding punct).\n", - "\n", - "**Attention**: the environment variable USER should be defined with any value, otherwise GIZA++ ends with segmentation fault. " - ], - "metadata": { - "id": "uUQMhEKGT7gv" - } - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "UPxcPu0_Xh2Y" - }, - "outputs": [], - "source": [ - "!chmod +x {ALIGNMENT_DIR}/date/run.sh\n", - "!chmod +x {ALIGNMENT_DIR}/letters/run.sh\n", - "!chmod +x {ALIGNMENT_DIR}/cardinal/run.sh" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "gOCv-ctbU3Rv" - }, - "outputs": [], - "source": [ - "## It is necessary to specify environment variable USER=, otherwise GIZA++ terminates with a segfault \n", - "\n", - "%cd {ALIGNMENT_DIR}/date\n", - "!export USER=\"user\"; ./run.sh\n", - "%cd ../..\n", - "\n", - "%cd {ALIGNMENT_DIR}/letters\n", - "!export USER=\"user\"; ./run.sh\n", - "%cd ../..\n", - "\n", - "%cd {ALIGNMENT_DIR}/cardinal\n", - "!export USER=\"user\"; ./run.sh\n", - "%cd ../.." - ] - }, - { - "cell_type": "markdown", - "source": [ - "GIZA++ will generate many files in our class folders, but we need only two files with final alignments, those with suffixes `A3.final`. The two files correspond to the alignments produced by two GIZA++ runs - direct and reverse (switching source and target corpus). This is a common practice, it allows us to find safer alignment points - tokens that were aligned to one another in both runs. The script [extract_giza_alignments.py](https://github.com/NVIDIA/NeMo/blob/main/examples/nlp/text_normalization_as_tagging/dataset_preparation/extract_giza_alignments.py) heuristically combines these two GIZA++ alignments. It also applies a bunch of regular expressions to correct some alignment mistakes." - ], - "metadata": { - "id": "ueJYVF0cU3ic" - } - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "j5WpPkzHNICP" - }, - "outputs": [], - "source": [ - "!python {NEMO_PATH}/examples/nlp/text_normalization_as_tagging/dataset_preparation/extract_giza_alignments.py \\\n", - " --mode=itn \\\n", - " --giza_dir={ALIGNMENT_DIR}/date \\\n", - " --giza_suffix=\"A3.final\" \\\n", - " --out_filename=itn.out \\\n", - " --lang={CORPUS_LANG}\n", - "!python {NEMO_PATH}/examples/nlp/text_normalization_as_tagging/dataset_preparation/extract_giza_alignments.py \\\n", - " --mode=itn \\\n", - " --giza_dir={ALIGNMENT_DIR}/letters \\\n", - " --giza_suffix=\"A3.final\" \\\n", - " --out_filename=itn.out \\\n", - " --lang={CORPUS_LANG}\n", - "!python {NEMO_PATH}/examples/nlp/text_normalization_as_tagging/dataset_preparation/extract_giza_alignments.py \\\n", - " --mode=itn \\\n", - " --giza_dir={ALIGNMENT_DIR}/cardinal \\\n", - " --giza_suffix=\"A3.final\" \\\n", - " --out_filename=itn.out \\\n", - " --lang={CORPUS_LANG}" - ] - }, - { - "cell_type": "markdown", - "source": [ - "When we prepared the input corpus of ITN pairs for GIZA++, we made them unique and stored the frequencies in a separate file `freq`. Now let's append the frequencies to the resulting alignments." - ], - "metadata": { - "id": "vpqiKrS6XBlP" - } - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "fxZ5jjUPlOFv" - }, - "outputs": [], - "source": [ - "!paste -d\"\\t\" {ALIGNMENT_DIR}/date/freq {ALIGNMENT_DIR}/date/itn.out > {ALIGNMENT_DIR}/date/itn.out2\n", - "!paste -d\"\\t\" {ALIGNMENT_DIR}/letters/freq {ALIGNMENT_DIR}/letters/itn.out > {ALIGNMENT_DIR}/letters/itn.out2\n", - "!paste -d\"\\t\" {ALIGNMENT_DIR}/cardinal/freq {ALIGNMENT_DIR}/cardinal/itn.out > {ALIGNMENT_DIR}/cardinal/itn.out2" - ] - }, - { - "cell_type": "markdown", - "source": [ - "Let's look at what we get. The output should look like\n", - "![Top of file with aligned expressions](images/thutmose_tagger_alignment_top.png)\n", - "...\n", - "![Bottom of file with aligned expressions](images/thutmose_tagger_alignment_bottom.png)\n" - ], - "metadata": { - "id": "yzt87qeEX5o0" - } - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "tJNFvVhG4SMo" - }, - "outputs": [], - "source": [ - "df = pd.read_csv(ALIGNMENT_DIR + \"/cardinal/itn.out2\", sep=\"\\t\", header=None)\n", - "df.columns = [\"freq\", \"verdict\", \"spoken\", \"written initial tokens\", \"left-side alignment\", \"right-side alignment\"]\n", - "is_spoken_multiword = df[\"spoken\"].apply(lambda x: \" \" in x)\n", - "df2 = df[is_spoken_multiword].sort_values(\"freq\", ascending=False).reset_index(drop=True)\n", - "df2.head(20)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "cEcXh1MzDWFy" - }, - "outputs": [], - "source": [ - "df2.tail(10)" - ] - }, - { - "cell_type": "markdown", - "source": [ - "# 2. Get a restricted vocabulary of target fragments (tags)\n", - "\n", - "There can be some inconsistencies in the automatic alignments, but nevertheless we now have **one-to-one correspondence** between input words and output fragments. Let's collect all fragments in a vocabulary! The output should look like this\n", - "![Tag vocabulary](images/thutmose_tagger_tag_vocabulary.png)\n", - "\n" - ], - "metadata": { - "id": "OdEuRQKXYG3D" - } - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "89zwtEQmQJZ1" - }, - "outputs": [], - "source": [ - "!python {NEMO_PATH}/examples/nlp/text_normalization_as_tagging/dataset_preparation/prepare_corpora_after_alignment.py \\\n", - " --mode=get_replacement_vocab \\\n", - " --giza_dir={ALIGNMENT_DIR} \\\n", - " --alignment_filename=itn.out2 \\\n", - " --data_dir=\"\" \\\n", - " --vocab_filename={WORK_DIR}/replacement_vocab_full.txt \\\n", - " --out_filename=\"\" \\\n", - " --lang={CORPUS_LANG}\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "tx4gXO7CGzyQ" - }, - "outputs": [], - "source": [ - "df = pd.read_csv(\"replacement_vocab_full.txt.cardinal\", sep=\"\\t\", header=None)\n", - "df.columns = [\"replacement tag\", \"freq\"]\n", - "df" - ] - }, - { - "cell_type": "markdown", - "source": [ - "Tags with low frequencies are likely to be derived from sporadic alignment mistakes, so let's truncate them, and put together the tags from all our semiotic classes." - ], - "metadata": { - "id": "Ts_G3TnLEQn4" - } - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "2TKbJELTFFXG" - }, - "outputs": [], - "source": [ - "!head -n 150 replacement_vocab_full.txt.cardinal > replacement_vocab_cardinal.txt\n", - "!head -n 150 replacement_vocab_full.txt.date > replacement_vocab_date.txt\n", - "!head -n 150 replacement_vocab_full.txt.letters > replacement_vocab_letters.txt\n", - "!cat replacement_vocab_cardinal.txt \\\n", - " replacement_vocab_date.txt \\\n", - " replacement_vocab_letters.txt > replacement_vocab.select.txt\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "x6eEYkVlJDK-" - }, - "source": [ - "After concatenation the vocabulary file can contain duplicates of the same tags coming from different semiotic classes, but this is not important at this moment. The final vocabulary will be created later." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "DoDHW-p1FUso" - }, - "outputs": [], - "source": [ - "!wc -l replacement_vocab.select.txt" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "xcQK5cHQH_NH" - }, - "outputs": [], - "source": [ - "!python {NEMO_PATH}/examples/nlp/text_normalization_as_tagging/dataset_preparation/prepare_corpora_after_alignment.py \\\n", - " --mode=filter_by_vocab \\\n", - " --giza_dir={ALIGNMENT_DIR} \\\n", - " --alignment_filename=itn.out2 \\\n", - " --data_dir=\"\" \\\n", - " --vocab_filename={WORK_DIR}/replacement_vocab.select.txt \\\n", - " --out_filename=itn.select.out \\\n", - " --lang={CORPUS_LANG}\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "yWhCQJ5DLgoZ" - }, - "source": [ - "The script `prepare_corpora_after_alignment.py --mode=filter_by_vocab` discards examples that are not fully covered with our selected replacement vocabulary. We can see that number of lines slightly decreases.\n", - "```\n", - "4997 content/alignment/cardinal/itn.out2\n", - "4681 content/alignment/cardinal/itn.select.out\n", - "```" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "_nqeRRjmKoWg" - }, - "outputs": [], - "source": [ - "!wc -l {ALIGNMENT_DIR}/cardinal/itn.out2\n", - "!wc -l {ALIGNMENT_DIR}/cardinal/itn.select.out\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "kUQwwCkLMKEX" - }, - "source": [ - "The format of lines also slightly changes: we add the name of semiotic class, choose only one alignment(left-side or right-side) based on class, and remove unnecessary columns.\n", - "\n", - "![Final alignment](images/thutmose_tagger_final_alignment.png)\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "83Voerh_K8gR" - }, - "outputs": [], - "source": [ - "df = pd.read_csv(ALIGNMENT_DIR + \"/cardinal/itn.select.out\", sep=\"\\t\", header=None)\n", - "df.columns = [\"semiotic class\", \"spoken\", \"written initial fragments\", \"alignment\"]\n", - "df.head(10)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "C6cAEYNHOKex" - }, - "source": [ - "# 3. Build training dataset \n", - "\n", - "Now it's time to create a tag-labeled dataset, containing _full sentences_. After previous step we got a large dictionary of ITN phrase conversions *that we know how to tag*. Once again we loop through the Google TN dataset and process each sentence in the following way:\n", - "\n", - "* If a sentence contains at least one ITN conversion, that is missing from our dictionary, this sentence is discarded.\n", - "* Otherwise we assign tags to the input words\n", - " 1. All words outside ITN conversion spans are tagged as ``.\n", - " 2. Tags for words inside ITN spans are taken from the dictionary\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "DQVCzljrMyHu" - }, - "outputs": [], - "source": [ - "!python {NEMO_PATH}/examples/nlp/text_normalization_as_tagging/dataset_preparation/prepare_corpora_after_alignment.py \\\n", - " --mode=get_labeled_corpus \\\n", - " --giza_dir={ALIGNMENT_DIR} \\\n", - " --alignment_filename=itn.select.out \\\n", - " --data_dir={CORPUS_DIR}/dev \\\n", - " --vocab_filename=\"\" \\\n", - " --out_filename={CORPUS_DIR}/dev.labeled \\\n", - " --lang={CORPUS_LANG}\n", - "\n", - "!python {NEMO_PATH}/examples/nlp/text_normalization_as_tagging/dataset_preparation/prepare_corpora_after_alignment.py \\\n", - " --mode=get_labeled_corpus \\\n", - " --giza_dir={ALIGNMENT_DIR} \\\n", - " --alignment_filename=itn.select.out \\\n", - " --data_dir={CORPUS_DIR}/train \\\n", - " --vocab_filename=\"\" \\\n", - " --out_filename={CORPUS_DIR}/train.labeled \\\n", - " --lang=${CORPUS_LANG}\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "bBfuML8TQrwz" - }, - "source": [ - "The resulting file consists of three columns:\n", - "* input words\n", - "* target tags\n", - "* semiotic spans (if any)\n", - "\n", - "The semiotic spans are separated by semicolon, each span consists of class, begin and end in terms of input word positions e.g. \"DATE 6 9\".\n", - "\n", - "```\n", - "it can be summarized as an error driven transformation based tagger\t \t\n", - "this plan was first enacted in nineteen eighty four and continued to be followed for nineteen years\t _19 8 4_ _19_ \tDATE 6 9;CARDINAL 15 16\n", - "```\n", - "The semiotic spans are used for two purposes:\n", - " \n", - "1. During validation step we calculate accuracy w.r.t. semiotic spans. For example, a DATE span is correct if **all** tag predictions inside this span match the ground truth labels.\n", - "2. The model has additional classification head that predicts a semiotic class label for each of the input words. These predictions are used in the post-processing step for better handling of swaps.\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "-TjToCTDN8t8" - }, - "outputs": [], - "source": [ - "!head {CORPUS_DIR}/dev.labeled" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Bf9Evpn8RWp4" - }, - "source": [ - "Get the final label vocabulary, based on our labeled corpora. The output file should look like this\n", - "```\n", - "KEEP\n", - "DELETE\n", - "DELETE|_20\n", - "DELETE|_19\n", - "DELETE|_2\n", - "DELETE|_200\n", - "DELETE|,20\n", - "DELETE|9_\n", - "DELETE|9\n", - "DELETE|8_\n", - "```" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "2RY2pZwEPdlZ" - }, - "outputs": [], - "source": [ - "!python {NEMO_PATH}/examples/nlp/text_normalization_as_tagging/dataset_preparation/get_label_vocab.py \\\n", - " --train_filename={CORPUS_DIR}/train.labeled \\\n", - " --dev_filename={CORPUS_DIR}/dev.labeled \\\n", - " --out_filename={CORPUS_DIR}/label_map.txt\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "9cGtBQSwRj-p" - }, - "outputs": [], - "source": [ - "!head {CORPUS_DIR}/label_map.txt" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "KL4DINweSgUQ" - }, - "outputs": [], - "source": [ - "!echo \"ADDRESS\" > {CORPUS_DIR}/semiotic_classes.txt\n", - "!echo \"CARDINAL\" >> {CORPUS_DIR}/semiotic_classes.txt\n", - "!echo \"DATE\" >> {CORPUS_DIR}/semiotic_classes.txt\n", - "!echo \"DECIMAL\" >> {CORPUS_DIR}/semiotic_classes.txt\n", - "!echo \"DIGIT\" >> {CORPUS_DIR}/semiotic_classes.txt\n", - "!echo \"ELECTRONIC\" >> {CORPUS_DIR}/semiotic_classes.txt\n", - "!echo \"FRACTION\" >> {CORPUS_DIR}/semiotic_classes.txt\n", - "!echo \"LETTERS\" >> {CORPUS_DIR}/semiotic_classes.txt\n", - "!echo \"MEASURE\" >> {CORPUS_DIR}/semiotic_classes.txt\n", - "!echo \"MONEY\" >> {CORPUS_DIR}/semiotic_classes.txt\n", - "!echo \"ORDINAL\" >> {CORPUS_DIR}/semiotic_classes.txt\n", - "!echo \"PLAIN\" >> {CORPUS_DIR}/semiotic_classes.txt\n", - "!echo \"PUNCT\" >> {CORPUS_DIR}/semiotic_classes.txt\n", - "!echo \"TELEPHONE\" >> {CORPUS_DIR}/semiotic_classes.txt\n", - "!echo \"TIME\" >> {CORPUS_DIR}/semiotic_classes.txt\n", - "!echo \"VERBATIM\" >> {CORPUS_DIR}/semiotic_classes.txt\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "b7JrZxs-WTS8" - }, - "outputs": [], - "source": [ - "!mkdir {WORK_DIR}/datasets\n", - "\n", - "!cp {CORPUS_DIR}/label_map.txt {WORK_DIR}/datasets/label_map.txt\n", - "!cp {CORPUS_DIR}/semiotic_classes.txt {WORK_DIR}/datasets/semiotic_classes.txt\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "E-fXGmAb63z0" - }, - "source": [ - "Now the file `train.labeled` contains all sentences from initial Google TN data, that we have been able to cover with out tag dictionary. \n", - "From it we can create different datasets for our neural model, trying different sizes and sampling strategies.\n", - "\n", - "Let's create a toy dataset of 5'000 sentences for train set and 5'000 sentences for dev set. Test set is not used - see Evaluation section below." - ] - }, - { - "cell_type": "code", - "source": [ - "DATASET = WORK_DIR + \"/datasets/itn_sample10k\"\n", - "!mkdir {DATASET}\n", - "!head -n 5000 {CORPUS_DIR}/train.labeled > {DATASET}/train.tsv\n", - "!head -n 5000 {CORPUS_DIR}/dev.labeled > {DATASET}/valid.tsv\n", - "!cp {DATASET}/valid.tsv {DATASET}/test.tsv\n" - ], - "metadata": { - "id": "KFwzGmuJlC0N" - }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "markdown", - "source": [ - "# 4. Train a token classifier neural model\n", - "Now let's run training" - ], - "metadata": { - "id": "X1vWojxlmffT" - } - }, - { - "cell_type": "code", - "source": [ - "!python {NEMO_PATH}/examples/nlp/text_normalization_as_tagging/normalization_as_tagging_train.py \\\n", - " lang=en \\\n", - " data.validation_ds.data_path={DATASET}/valid.tsv \\\n", - " data.train_ds.data_path={DATASET}/train.tsv \\\n", - " model.language_model.pretrained_model_name=bert-base-uncased \\\n", - " model.label_map={WORK_DIR}/datasets/label_map.txt \\\n", - " model.semiotic_classes={WORK_DIR}/datasets/semiotic_classes.txt \\\n", - " trainer.accelerator=gpu \\\n", - " trainer.max_epochs=1\n" - ], - "metadata": { - "id": "APBdPcihmFBa" - }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "markdown", - "source": [ - "Each validation step generates three classification reports where rows correspond to different semiotic classes, `support` column is how many examples of this class occurred in the target of validation set, and `recall` column is the classifier **accuracy** on this class, i.e. percentage of _whole examples_ whose predicted tags match the target.\n", - "\n", - "1. Tag classification report. `PLAIN` class includes words that are tagged as ``.\n", - "2. Tag classification report for **multiword** examples only. They are less trivial and it is harder to achieve high accuracy on them.\n", - "3. Classification report for semiotic classes." - ], - "metadata": { - "id": "kjZU6fkvS0V5" - } - }, - { - "cell_type": "code", - "source": [ - "# the log can be found in nemo_experiments folder\n", - "!cat nemo_experiments/training/*/nemo_log_globalrank-0_localrank-0.txt" - ], - "metadata": { - "id": "gO1nez6AWJeW" - }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "markdown", - "source": [ - "# Inference" - ], - "metadata": { - "id": "hX-9t7XBqJbo" - } - }, - { - "cell_type": "markdown", - "source": [ - "Let's run the inference of our toy model.\n", - "First, copy the model, that we've just trained." - ], - "metadata": { - "id": "9x80qIKCsBQ7" - } - }, - { - "cell_type": "code", - "source": [ - "!cp nemo_experiments/training/*/checkpoints/training.nemo ." - ], - "metadata": { - "id": "dYfyklDTXuUM" - }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "markdown", - "source": [ - "Generate some input sentences." - ], - "metadata": { - "id": "pVCV2Hchs-gG" - } - }, - { - "cell_type": "code", - "source": [ - "!echo \"on the ninth of may four days after her arrival at new orleans west carnifax was decommissioned and returned to the u s s b\" > test_sent.txt\n", - "!echo \"retrieved the fourth of october twenty fifteen\" >> test_sent.txt" - ], - "metadata": { - "id": "30KlsQ6uY6vu" - }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "markdown", - "source": [ - "Run the inference." - ], - "metadata": { - "id": "uqyBEKn-tDXe" - } - }, - { - "cell_type": "code", - "source": [ - "!python {NEMO_PATH}/examples/nlp/text_normalization_as_tagging/normalization_as_tagging_infer.py \\\n", - " pretrained_model=./training.nemo \\\n", - " inference.from_file=./test_sent.txt \\\n", - " inference.out_file=./test_sent.output" - ], - "metadata": { - "id": "SDSm6lg6ZOM_" - }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "code", - "source": [ - "!cat test_sent.output" - ], - "metadata": { - "id": "jrGJb9DcZ83E" - }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "markdown", - "source": [ - "The inference output consists of 5 columns:\n", - "\n", - "1. Final output text.\n", - "2. Input text.\n", - "3. Sequence of predicted tags.\n", - "4. Sequence of tags after post-processing (some swaps may be applied).\n", - "5. Sequence of predicted semiotic classes - one class for each input word.\n", - "\n", - "```\n", - "on ninth may four days after her arrival at new orleans west carnifax was decommissioned and returned to the u s s b\ton the ninth of may four days after her arrival at new orleans west carnifax was decommissioned and returned to the u s s b\t \t \tPLAIN DATE DATE DATE DATE DATE PLAIN PLAIN PLAIN PLAIN PLAIN PLAIN PLAIN PLAIN PLAIN PLAIN PLAIN PLAIN PLAIN PLAIN PLAIN LETTERS LETTERS LETTERS LETTERS\n", - "retrieved 20 october 20 20\tretrieved the fourth of october twenty fifteen\t _20 _20 _20\t _20 _20 _20\tPLAIN DATE DATE DATE DATE DATE DATE```\n" - ], - "metadata": { - "id": "eYqtL7waaiZS" - } - }, - { - "cell_type": "markdown", - "source": [ - "We see that our toy model works and even manages to replace some numbers.\n", - "\n", - "To train a full-fledged model, you need more data.\n", - "\n", - "See also the scripts for the whole pipeline:\n", - "\n", - "> [prepare_dataset_en.sh](https://github.com/NVIDIA/NeMo/blob/main/examples/nlp/text_normalization_as_tagging/prepare_dataset_en.sh)\n", - "\n", - "> [normalization_as_tagging_train.py](https://github.com/NVIDIA/NeMo/blob/main/examples/nlp/text_normalization_as_tagging/normalization_as_tagging_train.py)\n", - "\n", - "> [run_infer.sh](https://github.com/NVIDIA/NeMo/blob/main/examples/nlp/text_normalization_as_tagging/run_infer.sh)\n", - "\n" - ], - "metadata": { - "id": "AY9sQCIcUEGO" - } - }, - { - "cell_type": "markdown", - "source": [ - "# Inference with a pretrained model\n", - "\n", - "We can also run inference with a pretrained model [itn_en_thutmose_bert](https://catalog.ngc.nvidia.com/orgs/nvidia/teams/nemo/models/itn_en_thutmose_bert).\n", - "This is how to use it directly from python." - ], - "metadata": { - "id": "cMYFQLbaY3m-" - } - }, - { - "cell_type": "code", - "source": [ - "thutmose = nemo_nlp.models.ThutmoseTaggerModel.from_pretrained('itn_en_thutmose_bert')\n", - "thutmose.summarize()" - ], - "metadata": { - "id": "8Uor5qqcYgGF" - }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "code", - "source": [ - "from nemo.collections.nlp.data.text_normalization_as_tagging.utils import spoken_preprocessing\n", - "\n", - "lines = [\"on the ninth of may four days after her arrival at new orleans west carnifax was decommissioned and returned to the u s s b\",\n", - " \"retrieved the fourth of october twenty fifteen\"]\n", - "\n", - "\n", - "batch, all_preds = [], []\n", - "for i, line in enumerate(lines):\n", - " s = spoken_preprocessing(line) # this is the same input transformation as in corpus preparation\n", - " batch.append(s.strip())\n", - " outputs = thutmose._infer(batch)\n", - "for x in outputs:\n", - " all_preds.append(x)\n", - "\n", - "if len(all_preds) != len(lines):\n", - " raise ValueError(\n", - " \"number of input lines and predictions is different: predictions=\"\n", - " + str(len(all_preds))\n", - " + \"; lines=\"\n", - " + str(len(lines))\n", - " )\n", - "\n", - "for i in range(len(all_preds)):\n", - " print (all_preds[i])" - ], - "metadata": { - "id": "FcOiYPJwZzS0" - }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "markdown", - "source": [ - "Or we can use the inference script" - ], - "metadata": { - "id": "sJguLcWhaFdE" - } - }, - { - "cell_type": "code", - "source": [ - "!python {NEMO_PATH}/examples/nlp/text_normalization_as_tagging/normalization_as_tagging_infer.py \\\n", - " pretrained_model=itn_en_thutmose_bert \\\n", - " inference.from_file=./test_sent.txt \\\n", - " inference.out_file=./test_sent.output" - ], - "metadata": { - "id": "4R1hRpU-aMUs" - }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "code", - "source": [ - "!cat test_sent.output" - ], - "metadata": { - "id": "E0qVTUyvaQt1" - }, - "execution_count": null, - "outputs": [] - } - ], - "metadata": { - "colab": { - "collapsed_sections": [], - "name": "ITN_with_Thutmose_Tagger.ipynb", - "provenance": [] - }, - "kernelspec": { - "display_name": "Python 3", - "name": "python3" - }, - "language_info": { - "name": "python" - }, - "accelerator": "GPU" - }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file diff --git a/tutorials/text_processing/Text_(Inverse)_Normalization.ipynb b/tutorials/text_processing/Text_(Inverse)_Normalization.ipynb deleted file mode 100755 index f8123146f55f..000000000000 --- a/tutorials/text_processing/Text_(Inverse)_Normalization.ipynb +++ /dev/null @@ -1,468 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "a5fA5qAm5Afg" - }, - "outputs": [], - "source": [ - "\"\"\"\n", - "You can run either this notebook locally or on Google Colab.\n", - "\n", - "Instructions for setting up Colab are as follows:\n", - "1. Open a new Python 3 notebook.\n", - "2. Import this notebook from GitHub (File -> Upload Notebook -> \"GITHUB\" tab -> copy/paste GitHub URL)\n", - "3. Optional: Restart the runtime (Runtime -> Restart Runtime) for any upgraded packages to take effect\n", - "\"\"\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "> **_NOTE:_** Find the official NeMo documentation at \n", - "https://docs.nvidia.com/deeplearning/nemo/user-guide/docs/en/stable/nlp/text_normalization/wfst/intro.html " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Overview\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "F-IrnmXMTevr" - }, - "source": [ - "A sentence can be split up into semiotic tokens stemming from a variety of classes, where the spoken form differs from the written form. Examples are *dates*, *decimals*, *cardinals*, *measures* etc. The good TN or ITN system will be able to handle a variety of **semiotic classes**." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "-IT1Xr9iW2Xr" - }, - "source": [ - "# How to use\n", - "## 1. Installation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "## Install NeMo, which installs both nemo and nemo_text_processing package\n", - "BRANCH = 'main'\n", - "!python -m pip install git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[nlp]\n", - "\n", - "# install Pynini for text normalization\n", - "! wget https://raw.githubusercontent.com/NVIDIA/NeMo/main/nemo_text_processing/install_pynini.sh\n", - "! bash install_pynini.sh" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# try to import of nemo_text_processing an other dependencies\n", - "import nemo_text_processing\n", - "import os" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Text Normalization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "Bfs7fa9lXDDh" - }, - "outputs": [], - "source": [ - "# create text normalization instance that works on cased input\n", - "from nemo_text_processing.text_normalization.normalize import Normalizer\n", - "normalizer = Normalizer(input_case='cased', lang='en')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# the normalizer class offers the following parameterization. \n", - "print(normalizer.__doc__)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "> **_NOTE:_** Standard Text Normalization uses `deterministic=True`, outputting a single output for a given input string\n", - "\n", - "\n", - "\n", - "### 2.1 Run TN on input string" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Normalizer.normalize() offers the following parameterization\n", - "print(normalizer.normalize.__doc__)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# run normalization on example string input\n", - "written = \"We paid $123 for this desk.\"\n", - "normalized = normalizer.normalize(written, verbose=True, punct_post_process=True)\n", - "print(normalized)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "intermediate semiotic class information is shown if verbose=True. \n", - "\n", - "Long input text could be split into sentences as follows:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "written = \"Mr. Smith paid $111 in U.S.A. on Dec. 17th. We paid $123 for this desk.\"\n", - "\n", - "# split long text into sentences\n", - "sentences = normalizer.split_text_into_sentences(written)\n", - "\n", - "for sent in sentences:\n", - " print(sent)\n", - "\n", - "# normalize each sentence separately using normalize() or all sentences at once with normalize_list()\n", - "normalizer.normalize_list(sentences)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "### 2.2 Run TN on list of input strings" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "UD-OuFmEOX3T" - }, - "outputs": [], - "source": [ - "# create temporary data folder and example input file\n", - "DATA_DIR = 'tmp_data_dir'\n", - "os.makedirs(DATA_DIR, exist_ok=True)\n", - "INPUT_FILE = f'{DATA_DIR}/inference.txt'\n", - "! echo -e 'The alarm went off at 10:00a.m. \\nI received $123' > $INPUT_FILE" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "d4T0gXHwY3JZ" - }, - "outputs": [], - "source": [ - "# check input file was properly created\n", - "! cat $INPUT_FILE" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# load input file into 'data' - a list of strings\n", - "data = []\n", - "with open(INPUT_FILE, 'r') as fp:\n", - " for line in fp:\n", - " data.append(line.strip())\n", - "data" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "F5wSJTI8ZFRg" - }, - "outputs": [], - "source": [ - "# run normalization on 'data'\n", - "normalizer.normalize_list(data, punct_post_process=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "RMT5lkPYzZHK" - }, - "source": [ - "### 2.3 Evaluate TN on written-normalized text pairs \n", - "\n", - "The evaluation data needs to have the following format:\n", - "\n", - "'on 22 july 2022 they worked until 12:00' and the normalization is represented as " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# example evaluation sentence\n", - "eval_text = \"\"\"PLAIN\\ton\\t\n", - "DATE\\t22 july 2012\\tthe twenty second of july twenty twelve\n", - "PLAIN\\tthey\\t\n", - "PLAIN\\tworked\\t\n", - "PLAIN\\tuntil\\t\n", - "TIME\\t12:00\\ttwelve o'clock\n", - "\\t\n", - "\"\"\"\n", - "EVAL_FILE = f'{DATA_DIR}/eval.txt'\n", - "with open(EVAL_FILE, 'w') as fp:\n", - " fp.write(eval_text)\n", - "! cat $EVAL_FILE" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "RMT5lkPYzZHK" - }, - "source": [ - "That is, every sentence is broken into semiotic tokens line by line and concluded by end of sentence token ``. In case of a plain token it's `[SEMIOTIC CLASS] [TAB] [WRITTEN] [TAB] `, otherwise `[SEMIOTIC CLASS] [TAB] [WRITTEN] [TAB] [NORMALIZED]`.\n", - "This format was introduced in [Google Text normalization dataset](https://arxiv.org/abs/1611.00068). " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Parse evaluation file into written and normalized sentence pairs\n", - "from nemo_text_processing.text_normalization.data_loader_utils import load_files, training_data_to_sentences\n", - "eval_data = load_files([EVAL_FILE])\n", - "sentences_un_normalized, sentences_normalized, sentences_class_types = training_data_to_sentences(eval_data)\n", - "print(list(zip(sentences_un_normalized, sentences_normalized)))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# run prediction\n", - "sentences_prediction = normalizer.normalize_list(sentences_un_normalized)\n", - "print(sentences_prediction)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# measure sentence accuracy\n", - "from nemo_text_processing.text_normalization.data_loader_utils import evaluate\n", - "sentences_accuracy = evaluate(\n", - " preds=sentences_prediction, labels=sentences_normalized, input=sentences_un_normalized\n", - " )\n", - "print(\"- Accuracy: \" + str(sentences_accuracy))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3. Inverse Text Normalization\n", - "ITN supports equivalent API as TN. Here we are only going to show inverse normalization on input string" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# create inverse text normalization instance\n", - "from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer\n", - "inverse_normalizer = InverseNormalizer(lang='en')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# run ITN on example string input\n", - "spoken = \"we paid one hundred twenty three dollars for this desk\"\n", - "un_normalized = inverse_normalizer.inverse_normalize(spoken, verbose=True)\n", - "print(un_normalized)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 4. Audio-based Text Normalization\n", - "Audio-based text normalization uses extended [WFST](https://en.wikipedia.org/wiki/Finite-state_machine) grammars to provide a range of possible normalization options.\n", - "The following example shows the workflow: (Disclaimer: exact values in graphic do not need to be real system's behavior)\n", - "1. text \"627\" is sent to extended TN WFST grammar\n", - "2. grammar output 5 different options of verbalization based on text input alone\n", - "3. in case an audio file is presented we compare the audio transcript with the verbalization options to find out which normalization is correct based on character error rate. The transcript is generated using a pretrained NeMo ASR model. \n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following shows an example of how to generate multiple normalization options:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# import non-deterministic WFST-based TN module\n", - "from nemo_text_processing.text_normalization.normalize_with_audio import NormalizerWithAudio" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# initialize normalizer, this may take some time to generate the extended grammars. \n", - "# Thus, we recommend to cache the grammars by specifying a cache directory\n", - "normalizer = NormalizerWithAudio(\n", - " lang=\"en\",\n", - " input_case=\"cased\",\n", - " overwrite_cache=False,\n", - " cache_dir=\"cache_dir\",\n", - " )\n", - "# create up to 10 normalization options\n", - "print(normalizer.normalize(\"123\", n_tagged=10, punct_post_process=True))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 5. Parallel execution\n", - "\n", - "`Normalizer.normalize()` as well as `InverseNormalizer.inverse_normalize()` are functions without side effect.\n", - "Thus, if you need to normalize large amounts of input examples, these can be executed in parallel." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "ENMDNl9C4TkF" - }, - "source": [ - "# Tutorial on how to customize grammars\n", - "\n", - "https://colab.research.google.com/github/NVIDIA/NeMo/blob/stable/tutorials/text_processing/WFST_Tutorial.ipynb\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "lcvT3P2lQ_GS" - }, - "source": [ - "# References and Further Reading:\n", - "\n", - "\n", - "- [Zhang, Yang, Bakhturina, Evelina, Gorman, Kyle and Ginsburg, Boris. \"NeMo Inverse Text Normalization: From Development To Production.\" (2021)](https://arxiv.org/abs/2104.05055)\n", - "- [Ebden, Peter, and Richard Sproat. \"The Kestrel TTS text normalization system.\" Natural Language Engineering 21.3 (2015): 333.](https://www.cambridge.org/core/journals/natural-language-engineering/article/abs/kestrel-tts-text-normalization-system/F0C18A3F596B75D83B75C479E23795DA)\n", - "- [Gorman, Kyle. \"Pynini: A Python library for weighted finite-state grammar compilation.\" Proceedings of the SIGFSM Workshop on Statistical NLP and Weighted Automata. 2016.](https://www.aclweb.org/anthology/W16-2409.pdf)\n", - "- [Mohri, Mehryar, Fernando Pereira, and Michael Riley. \"Weighted finite-state transducers in speech recognition.\" Computer Speech & Language 16.1 (2002): 69-88.](https://cs.nyu.edu/~mohri/postscript/csl01.pdf)" - ] - } - ], - "metadata": { - "accelerator": "GPU", - "colab": { - "collapsed_sections": [ - "lcvT3P2lQ_GS" - ], - "name": "Text_Normalization_Tutorial.ipynb", - "private_outputs": true, - "provenance": [], - "toc_visible": true - }, - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} \ No newline at end of file diff --git a/tutorials/text_processing/WFST_Tutorial.ipynb b/tutorials/text_processing/WFST_Tutorial.ipynb deleted file mode 100644 index ed7127241dd5..000000000000 --- a/tutorials/text_processing/WFST_Tutorial.ipynb +++ /dev/null @@ -1,7054 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "Qq1Hz6CKWdwl", - "outputId": "3d8f5bd6-f10e-431d-9039-eb88164fbb95" - }, - "outputs": [], - "source": [ - "\"\"\"\n", - "You can run either this notebook locally or on Google Colab.\n", - "\n", - "Instructions for setting up Colab are as follows:\n", - "1. Open a new Python 3 notebook.\n", - "2. Import this notebook from GitHub (File -> Upload Notebook -> \"GITHUB\" tab -> copy/paste GitHub URL)\n", - "3. Optional: Restart the runtime (Runtime -> Restart Runtime) for any upgraded packages to take effect\n", - "\"\"\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## Prerequisites:\n", - "1. Please make sure to read the [Text Processing Documentation](https://docs.nvidia.com/deeplearning/nemo/user-guide/docs/en/stable/nlp/text_normalization/wfst/intro.html) and [Text Normalization Introduction Tutorial](https://colab.research.google.com/github/NVIDIA/NeMo/blob/stable/tutorials/text_processing/Text_(Inverse)_Normalization.ipynb) **before** this notebook. This notebook is a in-depth tutorial on how to customize and develop your own text normalization or inverse text normalization grammars.\n", - "2. download NeMo source code" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "## Install NeMo, which installs both nemo and nemo_text_processing package\n", - "BRANCH = 'main'\n", - "!python -m pip install git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[nemo_text_processing]\n", - "\n", - "# install Pynini for text normalization\n", - "! wget https://raw.githubusercontent.com/NVIDIA/NeMo/main/nemo_text_processing/install_pynini.sh\n", - "! bash install_pynini.sh" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import pynini\n", - "import nemo_text_processing\n", - "from pynini.lib import pynutil" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nemo_text_processing.text_normalization.en.graph_utils import GraphFst, NEMO_DIGIT, delete_space, NEMO_SIGMA, NEMO_NOT_QUOTE, delete_extra_space, NEMO_NON_BREAKING_SPACE\n", - "from nemo_text_processing.text_normalization.normalize import Normalizer\n", - "\n", - "from nemo_text_processing.inverse_text_normalization.fr.taggers.cardinal import CardinalFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.taggers.decimal import DecimalFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.taggers.money import MoneyFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.taggers.ordinal import OrdinalFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.taggers.punctuation import PunctuationFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.taggers.time import TimeFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.taggers.whitelist import WhiteListFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.taggers.word import WordFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.verbalizers.cardinal import CardinalFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.verbalizers.decimal import DecimalFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.verbalizers.money import MoneyFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.verbalizers.ordinal import OrdinalFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.verbalizers.time import TimeFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.verbalizers.whitelist import WhiteListFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.verbalizers.word import WordFst\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "T0JxcvuPHvn9" - }, - "source": [ - "NeMo's Text Processing module uses Weighted Finite State Transducers (WFST) to deploy grammars for both efficient text normalization (TN) and inverse text normalization (ITN). In this tutorial, you will learn to build a normalization grammar from the ground up to use in your own text processing tasks. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Table of Contents\n", - "- WFSTs\n", - "- NeMo Inverse Text Processing\n", - "- Getting Started\n", - "- Cardinal WFST\n", - "- Ordinal WFST\n", - "- Decimal WFST\n", - "- Money WFST\n", - "- Time WFST\n", - "- WhiteList WFST\n", - "- Word and Punctuation WFST\n", - "- Other Classes\n", - "- Tokenize and Classify\n", - "- Verbalize and Verbalize Final\n", - "- Deployment" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "lMUovcMsfXyI" - }, - "source": [ - "# WFSTs " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Y1ejNMLbH1jM" - }, - "source": [ - "WFSTs are a form of [Finite State Machines](https://en.wikipedia.org/wiki/Finite-state_machine) used to graph relations between regular languages (or [regular expressions](https://en.wikipedia.org/wiki/Regular_expression)). For our purposes, they can be defined by two major properties:\n", - "\n", - "1. Mappings between accepted input and output expressions for text substitution\n", - "2. Path weighting to direct graph traversal" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "nNg45ZuaP_A8" - }, - "source": [ - "For example, consider a simple normalization task of mapping the word \"cent\" (French for \"one hundred\") to the numerical representation `100`. We would begin with a Finite State representation of the regex `/cent/`:" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "uxo7gUkW_XKT" - }, - "source": [ - "![cent.png](images/cent.PNG)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "fahsjMVFlbCa" - }, - "source": [ - "And then create a mapping to the text string `100`:" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "IMJ-fNSk_jXC" - }, - "source": [ - "![cent_to_100.png](images/cent_to_100.PNG)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "bPKW0I4yAGUb" - }, - "source": [ - "*Note: Null characters are expressed as `ε` by convention*" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "_0NK3aW5nG9C" - }, - "source": [ - "This would give us a WFST with universal path weights. (By default, `pynini` uses [tropical semirings](https://en.wikipedia.org/wiki/Tropical_semiring) for arcs, giving each arc a default weight of `0`.)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "CzBc9D3qTGJ-" - }, - "source": [ - "Now, let us consider expanding our model. To indicate values between `100` and `200`, French uses the number scheme of `cent + digit`. For example, `120` would be pronounced as \"cent-vingt\". To create the appropriate output string, we would now want to map \"cent\" to `1` and the remaining aspect of our string to the appropriate digit representation." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "GRrKNQRjFDoL" - }, - "source": [ - "![cent_vingt_to_120.png](images/cent_vingt_to_120.PNG)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "jLpm4mufAfUz" - }, - "source": [ - "However this would make our graph [non-deterministic](https://en.wikipedia.org/wiki/Nondeterministic_algorithm) - it will have multiple possibilities for termination. Now an input of \"cent-vingt\" could have the outcome of `100` or `10020` when only one is correct. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "![cent_vingt_bad.png](images/cent_vingt_bad.PNG)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "c-GJTpgIAf7S" - }, - "source": [ - "To correct this, we may add a new end state and a weight to the path that accepts the input without `s`:" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "6GJcsdttGg_S" - }, - "source": [ - "![cent_vingt_good.png](images/cent_vingt_good.PNG)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "mHft1gzsAipc" - }, - "source": [ - "Now, we can guarantee an ideal mapping by relying on a shortest-path (smallest-weight) heuristic: traversal of the graph will prioritize longer inputs, only converting \"cent\" to `100` when a larger input isn't available. As such, we've now removed the undesired output `10020` while preserving our desired coverage in string mapping. \n", - "\n", - "This use of weights to ensure predictable behavior allows WFSTs to exploit the efficiency of standard graph traversal algorithms while also maintaining versatility. " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "8Ik4PBXafSSB" - }, - "source": [ - "# NeMo Inverse Text Processing " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "b2fcWKhqYVF5" - }, - "source": [ - "Following [Google's Kestrel](https://www.researchgate.net/publication/277932107_The_Kestrel_TTS_text_normalization_system) framework, NeMo deploys two composite WFSTs for text normalization. They are as follows:\n", - "1. A *classifier* (or tagger) to label potential tokens by 'semiotic class' (e.g. currency, ordinal number, street address)\n", - "2. A *verbalizer* to render a tagged token in conventional written form\n", - "\n", - "For example, consider the sentence: <>\n", - "\n", - "For an ITN task, a tokenizer would identify the following tokens:\n", - "\n", - "`[\"le\" ,\"premier\", \"juillet\", \"il\", \"a\", \"mangé\", \"trente-cinq\", \"pommes\"]`\n", - "\n", - "and provide each a class token: \n", - "\n", - "- `tokens { name: \"le\" }`\n", - "- `tokens { date { day: \"1\" month: \"juillet\" } } ` \n", - "- `tokens { name: \"il\" }` \n", - "- `tokens { name: \"a\" }` \n", - "- `tokens { name: \"mangé\" }`\n", - "- `tokens { cardinal { integer: \"35\" } }` \n", - "- `tokens { name: \"pommes\" }`\n", - "\n", - "These tokens are then passed to a 'verbalizer' WFST, which renders each token in a conventional written form:\n", - "\n", - "- `tokens { name: \"le\" }` -> `le` \n", - "- `tokens { date { day: \"1\" month: \"juillet\" } } ` -> `1ᵉʳ` \n", - "- `tokens { name: \"il\" }` -> `juillet`\n", - "- `tokens { name: \"il\" }` -> `il` \n", - "- `tokens { name: \"a\" }` -> `a`\n", - "- `tokens { name: \"mangé\" }` -> `mangé` \n", - "- `tokens { cardinal { integer: \"35\" } }` -> `35` \n", - "- `tokens { name: \"pommes\" }` -> `pommes`\n", - "\n", - "and merged into a normalized string:\n", - "\n", - "`le 1ᵉʳ juillet il a mangé 35 pommes`\n", - "\n", - "With the equivalent TN task being the reverse process. " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "_n-5JExAbvwr" - }, - "source": [ - ">**_Note:_**\n", - ">A few things to note specific to inverse text normalization: \n", - ">- Each class token has a unique set of field names that must be parsed by the classifier. The default field names for NeMo are chosen to mirror the syntax in [Sparrowhawk](https://github.com/google/sparrowhawk) to enable deployment. If these fields are not exact, you will not be able to use Sparrowhawk.\n", - ">- NeMo assumes no punctuation (unless explicitly provided in the grammar) and all lower casing to ease integration with upstream ASR.\n", - ">- The `name` class token is default for any token that does not require processing. It will be left 'as is.'\n", - ">- You may note how the tokenizer performed the conversion of `premier` to `1` while the verbalizer normalized `1` -> `1ᵉʳ`. Such decisions are implementation dependent and will vary depending on preference and language. (That is, normalization from `premier` -> `1ᵉʳ` could have been a tokenization step.)\n", - ">- By default, NeMo will create several permutations of key values in a token to ease normalization. That is, given the token `tokens { date { day: \"1\" month: \"juillet\" } }`, it will also produce paths for `tokens { date { month: \"juillet\" day: \"1\" } }`. To prevent this and avoid ambiguity in verbalizer input, tokens can be assigned a `preserve_order` attribute to prevent permutation. (e.g. `tokens { date { day: \"1\" month: \"juillet\" preserve_order: true } }`) (We will discuss this [later in the tutorial](#verbalizer).)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## WFST Classes" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "NeMo Text Processing's base languages supports a range of semiotic classes to permit integration with Sparrowhawk.\n", - "For this tutorial, we will be focusing on the following classes:\n", - "- CARDINAL\n", - "- ORDINAL\n", - "- DECIMAL\n", - "- MONEY\n", - "- TIME\n", - "- WHITELIST\n", - "- WORD\n", - "- PUNCTUATION\n", - "\n", - "While not comprehensive, these classes will provide enough foundation and exposure to edge cases that you will feel comfortable constructing for other cases.\n", - "\n", - "**NOTE**: *If you intend to only develop for personal use with NeMo, you may rename these classes as desired. However, Sparrowhawk integration\n", - "REQUIRES use of only these tags and their assigned attributes. For list of Sparrowhawk tokens and attributes, [consult the Sparrowhawk repository](https://github.com/yzhang123/sparrowhawk/blob/test/src/proto/semiotic_classes.proto)*" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Further Reading" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you wish to learn more about NeMo Text Processing, you may wish to consult the following:\n", - "- [Y. Zhang, E. Bakhturina, K. Gorman, and B. Ginsburg, \"NeMo Inverse Text Normalization: From Development To Production\"](https://arxiv.org/pdf/2104.05055.pdf)\n", - "- [NeMo's Text Normalization Documentation](https://docs.nvidia.com/deeplearning/nemo/user-guide/docs/en/stable/nlp/text_normalization/wfst/intro.html) \n", - "- [NeMo's Text Normalization Deployment Documentation](https://docs.nvidia.com/deeplearning/nemo/user-guide/docs/en/stable/nlp/text_normalization/wfst/wfst_text_processing_deployment.html)\n", - "- NeMo's [Text Normalization Introduction Tutorial](https://colab.research.google.com/github/NVIDIA/NeMo/blob/stable/tutorials/text_processing/Text_Normalization.ipynb)\n", - "- [Sparrowhawk Documentation](https://github.com/google/sparrowhawk)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For further information regarding WFSTs, please see:\n", - "- [D. Jufasky and J. Martin, *Natural Language Processing*, Ch. 2](https://web.stanford.edu/~jurafsky/slp3/2.pdf)\n", - "- [K. Gorman and R. Sproat, *Finite-State Text Processing*](http://www.morganclaypoolpublishers.com/catalog_Orig/product_info.php?products_id=1636)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "XFdXRcnUfI25" - }, - "source": [ - "# Getting Started \n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "K3Zl3VwqdYqL" - }, - "source": [ - "To begin tokenizer development, make sure you have [installed NeMo from source](https://github.com/NVIDIA/NeMo)." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "rGg7Bf13FXgc" - }, - "source": [ - "For this tutorial, we will focus on developing an Inverse Text Normalization system, such as one you may encounter in downstream ASR processing. As such, we will navigate to\n", - "`nemo_text_processing/inverse_text_normalization` and create a directory for our target language (French) and subdirectories\n", - "for `taggers` and `verbalizers`. You may also wish to create a `data` subdirectory to ease navigation.\n", - "\n", - "(Note, for text normalization, the suggested directory structure would be the same within the `nemo_text_processing/text_normalization` folder. In fact, many of NeMo's grammars actively share between.)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "T58E4pU4FN3A" - }, - "source": [ - "```bash\n", - "git clone https://github.com/NVIDIA/NeMo\n", - "cd NeMo && ./reinstall.sh\n", - "cd nemo_text_processing/inverse_text_normalization/\n", - "export LANGUAGE=fr # Change this to your desired language\n", - "mkdir $LANGUAGE\n", - "mkdir $LANGUAGE/taggers\n", - "mkdir $LANGUAGE/verbalizers\n", - "mkdir $LANGUAGE/data\"\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "O1vfz-bUFpwz" - }, - "source": [ - "All WFSTs deployed in NeMo inherit from the `GraphFst` class.\n", - "While in most cases you can simply import from a pre-existing `graph_utils.py`, you may occasionally find it helpful for deployment to keep a copy \n", - "in your working directory for language specific edits. (For our purposes, we will be utilizing `nemo_text_processing.text_normalization.en.graph_utils`, which serves as default for NeMo's grammars.)\n", - "\n", - "You may also wish to keep a copy of `utils.py` (found in each language system's directory)\n", - "in your working directory to assist with pathing. (Make sure to adjust the imports towards your language.)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "3OME84EmOQ4h", - "outputId": "6eea17f9-aae9-4176-ae35-3d1f0e94b4ea" - }, - "source": [ - "```bash\n", - "cp ../text_normalization/en/graph_utils.py $LANGUAGE/\n", - "cp ../text_normalization/en/utils.py $LANGUAGE/\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Dependencies" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For development, we utilize `nemo_text_processing` and `pynini` (a Python library for efficient WFST construction and traversal, installed with `nemo` by default). " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "While this tutorial will attempt to make use of `pynini` tools transparent, it does assume some familiarity with its syntax. For a more in-depth guide, the following will provide a function overview:\n", - "\n", - "- [K. Gorman, Pynini: A Python library for weighted finite-state grammar compilation](https://aclanthology.org/W16-2409.pdf)\n", - "- [Pynini Documentation](https://www.openfst.org/twiki/bin/view/GRM/PyniniDocs) " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We will also import the `pynutil` module for access to some extra functionality, along with writing a simple helper function for printing `pynini` graphs through the previously discussed 'shortest-path' heuristic." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "sz18Ui8-8Kf4" - }, - "outputs": [], - "source": [ - "from pynini.lib import pynutil\n", - "\n", - "def apply_fst(text, fst):\n", - " \"\"\" Given a string input, returns the output string\n", - " produced by traversing the path with lowest weight.\n", - " If no valid path accepts input string, returns an\n", - " error.\n", - " \"\"\"\n", - " try:\n", - " print(pynini.shortestpath(text @ fst).string())\n", - " except pynini.FstOpError:\n", - " print(f\"Error: No valid output with given input: '{text}'\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Cardinal WFST " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "rOyLZb9DgLoh" - }, - "source": [ - "The vast majority of ITN tasks require the ability to recognize and denormalize numbers. As such, we will begin with developing a Classifier and Verbalizer for Cardinal (integer) numbers. (e.g. `-3,-2,-1,0,1,2,3,4,5....99,100,101...`)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "9GZQkH1V89kh" - }, - "source": [ - "## Grammar" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We will begin by first constructing a Cardinal WFST, using French as an example language. While your target language will obviously differ greatly from our example, you will likely find some several similarities, such as:\n", - "- Use of a (semi) regular decimal (base-10) counting system. (A common - but not universal - feature of natural languages.)\n", - "- Incorporation of several irregularities requiring contingencies in our WFST construction. (e.g. a pseudo vigesimal (base-20) series.)\n", - "- Use of gender and number agreement in enumeration." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Digits" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "NzJ2DIwc_TT3" - }, - "source": [ - "We shall begin with the first decimal place. As these numbers serve as the building blocks for the rest of our WFST, we shall begin by explicitly calling their WFST mappings with `pynini.string_map`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "u0H4qg4BjYfB" - }, - "outputs": [], - "source": [ - "zero = pynini.string_map([(\"zéro\",\"0\")]) # French only pronounces zeroes as stand alone\n", - "digits = pynini.string_map([ # pynini function that creates explicit input-output mappings for a WFST\n", - "\t\t\t\t(\"un\",\"1\"),\n", - "\t\t\t\t(\"une\",\"1\"),\n", - "\t\t\t\t(\"deux\",\"2\"),\n", - "\t\t\t\t(\"trois\",\"3\"),\n", - "\t\t\t\t(\"quatre\",\"4\"),\n", - "\t\t\t\t(\"cinq\",\"5\"),\n", - "\t\t\t\t(\"six\",\"6\"),\n", - "\t\t\t\t(\"sept\",\"7\"),\n", - "\t\t\t\t(\"huit\",\"8\"),\n", - "\t\t\t\t(\"neuf\",\"9\")\n", - "])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "0nHjY-NNjdWQ" - }, - "source": [ - "We may also simply write a `tsv` file in a separate data folder " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "- zéro\t0\n", - "- un\t1\n", - "- une\t1\n", - "- deux\t2\n", - "- trois\t3\n", - "- quatre\t4\n", - "- cinq\t5\n", - "- six\t6\n", - "- sept\t7\n", - "- huit\t8\n", - "- neuf\t9" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "xicKcZLEzQTg" - }, - "source": [ - "and import with `string_file`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`digits = pynini.string_file(\"data/digits.tsv\")`\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If utils.py is in working directory you may also use `get_abs_path`, which will always call paths relative to your {LANGUAGE} directory:\n", - "\n", - "`from nemo_text_processing.inverse_normalization.{LANGUAGE}.utils import get_abs_path`\n", - "\n", - "`digits = pynini.string_file(get_abs_path(\"data/digits.tsv\"))`" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "yPccmicQkYAB" - }, - "source": [ - "While we will use `string_map` throughout this tutorial, please note that NeMo employs the later option for maintainability and recommends its use instead." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Teens" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "FQJiJcVMrNmC" - }, - "source": [ - "Let us consider our next set of numbers:\n", - "- 10 - dix\n", - "- 11 - onze\n", - "- 12 - douze\n", - "- 13 - treize\n", - "- 14 - quatorze\n", - "- 15 - quinze\n", - "- 16 - seize\n", - "- 17 - dix-sept\n", - "- 18 - dix-huit\n", - "- 19 - dix-neuf\n", - "\n", - "Like before, we can simply use `string_map` to compose a WFST for them. But note how there is some redundancy in the number set: `17`, `18`, and `19` are all of the form `dix + digit`. It would be more efficient simply to reuse our prior WFST in these cases than simply creating new arcs, states, and weights. \n", - "\n", - "We can achieve this using pynini's string concatenation function to extend the accepted input strings. First we will create an WFST for `11-16`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "orSgBwyXsfY5" - }, - "outputs": [], - "source": [ - "teens = pynini.string_map([\n", - "\t\t\t\t(\"onze\",\"11\"),\n", - "\t\t\t\t(\"douze\",\"12\"),\n", - "\t\t\t\t(\"treize\",\"13\"),\n", - "\t\t\t\t(\"quatorze\",\"14\"),\n", - "\t\t\t\t(\"quinze\",\"15\"),\n", - "\t\t\t\t(\"seize\",\"16\"),\n", - "])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "s1yIgigdtriQ" - }, - "source": [ - "Now, we will create a `tens` WFST that is responsible for mapping all instances of \"dix\" and concatenate (accomplished with the overloaded `+` operator) with the prior `digits` WFST. (Deleting any possible hyphens in-between with a build in `delete_hyphen`.)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "CzwZrFCkt87W" - }, - "outputs": [], - "source": [ - "tens = pynini.string_map([(\"dix\", \"1\")])\n", - "delete_hyphen = pynini.closure(pynutil.delete(\"-\"), 0, 1) # Applies a closure from 0-1 of operation. Equivalent to regex /?/\n", - "\n", - "graph_tens = tens + delete_hyphen + digits" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "2knCwybmuTDn" - }, - "source": [ - "We now can combine the `teens` and `graph_tens` WFST together through the union operation (done with the overloaded `|` operator), allowing our choice of either graph." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "WIRJ4PE7uRrl" - }, - "outputs": [], - "source": [ - "graph_tens_and_teens = graph_tens | teens" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "TGkzKoeuxbeA" - }, - "source": [ - "Let's see if it works through the string function:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "v2iD0_HnxdUV", - "outputId": "1d8f434f-ff8a-4c85-b8d0-1127e4587ddf" - }, - "outputs": [], - "source": [ - "apply_fst(\"dix-huit\", graph_tens_and_teens)\n", - "apply_fst(\"seize\", graph_tens_and_teens)\n", - "apply_fst(\"dix\", graph_tens_and_teens)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Yh2f-3rux8_2" - }, - "source": [ - "The first two worked, but why did we get an error with \"dix\"? If you look back, you'll notice that while our graph has a mapping from \"dix\" to `1` - the concatenation with `digits` makes the assumption that some input from those strings will follow. That is, we left no opportunity for an *omission* of `digits`.\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "OM_eJYlV1UVp" - }, - "source": [ - "![dix_to_digits.png](images/dix_to_digits.PNG)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "M4xCMKRA1Wzw" - }, - "source": [ - "You may also note that this issue would hold also if we wanted to normalize only digits - our graph would error out since it's expecting a `tens` or input first. \n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "XJHnlJCm1dPv" - }, - "source": [ - "We can fix both of these problems by allowing an option to simply insert a zero without any extra input. (Much like our \"cent\" example.)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "9_vvJ9Bl1dYQ" - }, - "source": [ - "![dix_to_digits_with_insert.png](images/dix_to_digits_with_insert.PNG)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "hJq3uoMN2OcC" - }, - "source": [ - "This may be accomplished through use of the `pynutil.insert` function:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "7h9xuNfA081P" - }, - "outputs": [], - "source": [ - "graph_digits = digits | pynutil.insert(\"0\") # inserts zero if no digit follows" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "fA_L_6Ky2SHm" - }, - "source": [ - "And for `graph_tens`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "jelVA81o2RXu" - }, - "outputs": [], - "source": [ - "tens = tens | pynutil.insert(\"0\") | tens + delete_hyphen\n", - "graph_tens = tens + graph_digits" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Gb5uhpGr3I4X" - }, - "source": [ - "Bringing everything together:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "bLkDddkA3Stu" - }, - "outputs": [], - "source": [ - "graph_teens_and_tens = graph_tens | teens\n", - "graph_all = graph_teens_and_tens | zero " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "DESDKScv3r3P" - }, - "source": [ - "Let us now check our tests:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "7wrDNXuD3oh9", - "outputId": "661d2526-5aa0-4640-9285-bca15cd56c75" - }, - "outputs": [], - "source": [ - "apply_fst(\"dix-huit\", graph_all) \n", - "apply_fst(\"seize\" , graph_all)\n", - "apply_fst(\"dix\" , graph_all) \n", - "apply_fst(\"une\" , graph_all) \n", - "apply_fst(\"trois\" , graph_all) \n", - "apply_fst(\"quatre\" , graph_all) \n", - "apply_fst(\"zéro\" , graph_all)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Tz_k3NoB66Bv" - }, - "source": [ - "Now we have no more error - albeit at the cost of leading zeroes. (We will take care of this later in the section.)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Tens" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "2dJZAhE57an3" - }, - "source": [ - "Now that we've taken care of the teens, we can proceed with the rest of the tens. Like many languages, French employs a (fairly) regular schema of: `tens_digit + ones_digit` for 20-100. Indeed, we can summarize 20-69 in the following template:\n", - "\n", - "- 20 - vingt\n", - "- 21 - vingt-et-un\n", - "- 22 - vingt-deux\n", - "- 23 - vingt-trois\n", - "- 24 - vingt-quatre\n", - "- 25 - vingt-cinq\n", - "- 26 - vingt-six\n", - "- 27 - vingt-sept\n", - "- 28 - vingt-huit\n", - "- 29 - vingt-neuf\n", - "- 30 - trente\n", - "- 31 - trente-et-un\n", - "- 32 - trente-deux\n", - "- 33 - trente-trois\n", - "...\n", - "- 40 - quarante\n", - "...\n", - "- 50 - cinquante\n", - "...\n", - "- 60 - soixante\n", - "..." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "BuaxVG35UKcs" - }, - "source": [ - "Expanding `tens` is fairly easy to accommodate this template: we simply extend our earlier `string_map` for the new terms in the 'tens place.' From there, we once again concatenate the `digits` WFST (along with a simple WFST to delete the occurrence of the \"-et-\" term that occasionally occurs.)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "qAnXlRkR32wt" - }, - "outputs": [], - "source": [ - "tens = pynini.string_map([\n", - "\t\t\t\t(\"dix\", \"1\"),\n", - "\t\t\t\t(\"vingt\",\"2\"),\n", - "\t\t\t\t(\"trente\",\"3\"),\n", - "\t\t\t\t(\"quarante\",\"4\"),\n", - "\t\t\t\t(\"cinquante\",\"5\"),\n", - "\t\t\t\t(\"soixante\",\"6\"),\n", - "\t\t])\n", - "\n", - "graph_et = pynutil.delete(\"-et-\")\n", - "\n", - "tens = tens | pynutil.insert(\"0\") | tens + pynutil.delete(\"-\") | tens + graph_et\n", - "\n", - "graph_tens = tens + graph_digits\n", - "graph_teens_and_tens = graph_tens | teens\n", - "graph_all = graph_teens_and_tens | zero " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "-hJwqPDx8I2R" - }, - "source": [ - "#### Special Cases: 70-99" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "zvBLvJdY9XPA" - }, - "source": [ - "However, things get tricky once we go beyond the 60s. Here, standard French possesses a notorious psuedo-vigecimal (base-20) system. For numbers 70-99:\n", - "\n", - "- 70 - soixante-dix <- Literally in English: \"sixty-ten\"\n", - "- 71 - soixante-et-onze <- Literally in English: \"sixty-and-eleven\"\n", - "- 72 - soixante-douze\n", - "- 73 - soixante-treize\n", - "- 74 - soixante-quatorze\n", - "- 75 - soixante-quinze\n", - "- 76 - soixante-seize\n", - "- 77 - soixante-dix-sept\n", - "- 78 - soixante-dix-huit\n", - "- 79 - soixante-dix-neuf\n", - "- 80 - quatre-vingts <- Literally in English: \"four-twenties\"\n", - "- 81 - quatre-vingt-un\n", - "- 82 - quatre-vingt-deux\n", - "- 83 - quatre-vingt-trois\n", - "- 84 - quatre-vingt-quatre\n", - "- 85 - quatre-vingt-cinq\n", - "- 86 - quatre-vingt-six\n", - "- 87 - quatre-vingt-sept\n", - "- 88 - quatre-vingt-huit\n", - "- 89 - quatre-vingt-nuef\n", - "- 90 - quatre-vingt-dix <- Literally in English: \"four-twenties-ten\"\n", - "- 91 - quatre-vingt-onze\n", - "- 92 - quatre-vingt-douze\n", - "- 93 - quatre-vingt-treize\n", - "- 94 - quatre-vingt-quatorze\n", - "- 95 - quatre-vingt-quinze\n", - "- 96 - quatre-vingt-seize\n", - "- 97 - quatre-vingt-dix-sept\n", - "- 98 - quatre-vingt-dix-huit\n", - "- 99 - quatre-vingt-dix-neuf" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "HQNiwFDyVV_3" - }, - "source": [ - "As before, we want to take advantage of as much redundancy as we can without creating additional ambiguities that will impede graph traversal. \n", - "\n", - "We first note that - despite repeating prior words - \"quatre-vingt\" can be mapped to `8` without introducing ambiguity. This is because, despite \"quatre\" and \"vingt\" being present in our prior graphs, our WFST has no pathing for them in this exact order. As such, we can simply add it to `tens` and immediately improve our coverage for 81-89. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "AvJqaHhE9Wbd" - }, - "outputs": [], - "source": [ - "tens = pynini.string_map([\n", - "\t\t\t\t(\"dix\", \"1\"),\n", - "\t\t\t\t(\"vingt\",\"2\"),\n", - "\t\t\t\t(\"trente\",\"3\"),\n", - "\t\t\t\t(\"quarante\",\"4\"),\n", - "\t\t\t\t(\"cinquante\",\"5\"),\n", - "\t\t\t\t(\"soixante\",\"6\"),\n", - " (\"quatre-vingt\", \"8\")\n", - "\t\t])\n", - "tens = tens | pynutil.insert(\"0\") | tens + delete_hyphen | tens + graph_et\n", - "graph_tens = tens + graph_digits\n", - "graph_teens_and_tens = graph_tens | teens\n", - "graph_all = graph_teens_and_tens | zero " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "0_DtcpZxZTzX" - }, - "source": [ - "Of course, now we permit the occurrence of:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "V2leANlDhCvj", - "outputId": "db8d5d02-c848-4e50-df23-d8499538281c" - }, - "outputs": [], - "source": [ - "apply_fst(\"quatre-vingt\", graph_all)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "_X_ef3sihCHH" - }, - "source": [ - "which is invalid (French uses the plural \"quatre-vingt**s**\" here.) " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "vgKT903Y6rIQ" - }, - "source": [ - "Should we alter the grammar because of this? Such a decision will largely be dependent on your intended implementation and design aims. If you see the question of 'legal' tokens as a responsibility of your upstream model, then there is no need for any alteration: \"quatre-vingt\" as a standalone token will simply not occur, so there is no input to be concerned with.\n", - "\n", - "However, if your ITN grammars are developed for an environment with low-fidelity ASR and/or where mistaken transcriptions incur heavy loss (e.g. ASR for driving directions, telephone-numbers, banking) then you may wish to err on the side of caution." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Hf_FghLT7jdY" - }, - "source": [ - "If we wanted to go for the latter, we would want to mark that \"quatre-vingts\" maps **only** to `80`. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "JliFTF3mZSsJ" - }, - "outputs": [], - "source": [ - "quatre_vingt_plural = pynini.string_map([\n", - " (\"quatre-vingts\", \"80\")\n", - "\t\t])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "81_b3XPbicT1" - }, - "source": [ - "And that \"quatre vingt\" can only accompany non-zero digits:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "E4_dmg6uin2j" - }, - "outputs": [], - "source": [ - "quatre_vingt_singular = pynini.string_map([\n", - " (\"quatre-vingt-\", \"8\") # Note that the hyphen can be assumed now\n", - "\t\t])\n", - "graph_digits_without_zero = pynini.string_map([\n", - "\t\t\t\t(\"un\",\"1\"),\n", - "\t\t\t\t(\"une\",\"1\"),\n", - "\t\t\t\t(\"deux\",\"2\"),\n", - "\t\t\t\t(\"trois\",\"3\"),\n", - "\t\t\t\t(\"quatre\",\"4\"),\n", - "\t\t\t\t(\"cinq\",\"5\"),\n", - "\t\t\t\t(\"six\",\"6\"),\n", - "\t\t\t\t(\"sept\",\"7\"),\n", - "\t\t\t\t(\"huit\",\"8\"),\n", - "\t\t\t\t(\"neuf\",\"9\")\n", - "])\n", - "graph_eighties = (quatre_vingt_singular + graph_digits_without_zero) | quatre_vingt_plural" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "mL7jpekV8VgP" - }, - "source": [ - "For the `70`'s and `90`'s, we would likewise need to form exclusive configurations for their number series, rewriting digits to recognize \"onze\", \"douze\", \"treize\"... as `1,2,3....` (Note, we'll have to separate `71` and `91` to manage \"soixante-**et**-onze\" vs. \"quatre-vingt-onze\".)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "y3dYkwK29zCX" - }, - "outputs": [], - "source": [ - "seventy_and_ninety = pynini.string_map([\n", - " (\"soixante-dix\", \"70\"),\n", - " (\"quatre-vingt-dix\", \"90\"),\n", - "\t\t])\n", - "\n", - "seventy_and_ninety_tens = pynini.string_map([\n", - " (\"soixante-\", \"7\"),\n", - " (\"quatre-vingt-\", \"9\"),\n", - "\t\t])\n", - "\n", - "seventy_and_ninety_one = pynini.string_map([\n", - " (\"soixante-et-onze\", \"71\"),\n", - " (\"quatre-vingt-onze\", \"91\"),\n", - "\t\t])\n", - "\n", - "seventy_and_ninety_digits = digits = pynini.string_map([ \n", - "\t\t\t\t(\"douze\",\"2\"),\n", - "\t\t\t\t(\"treize\",\"3\"),\n", - "\t\t\t\t(\"quatorze\",\"4\"),\n", - "\t\t\t\t(\"quinze\",\"5\"),\n", - "\t\t\t\t(\"seize\",\"6\"),\n", - "\t\t\t\t(\"dix-sept\",\"7\"), # For 97-99, digits are used as normal.\n", - "\t\t\t\t(\"dix-huit\",\"8\"),\n", - "\t\t\t\t(\"dix-neuf\",\"9\")\n", - "])\n", - "\n", - "graph_seventies_and_nineties = (seventy_and_ninety_tens + seventy_and_ninety_digits) | seventy_and_ninety | seventy_and_ninety_one " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "4NCrCwEH9HVg" - }, - "source": [ - "Now we union them with our original `tens` series:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "psGCgxaH-btn" - }, - "outputs": [], - "source": [ - "tens = pynini.string_map([\n", - "\t\t\t\t(\"dix\", \"1\"),\n", - "\t\t\t\t(\"vingt\",\"2\"),\n", - "\t\t\t\t(\"trente\",\"3\"),\n", - "\t\t\t\t(\"quarante\",\"4\"),\n", - "\t\t\t\t(\"cinquante\",\"5\"),\n", - "\t\t\t\t(\"soixante\",\"6\"),\n", - "\t\t])\n", - "tens = tens | pynutil.insert(\"0\") | tens + delete_hyphen | tens + graph_et\n", - "\n", - "graph_tens = tens + graph_digits\n", - "graph_tens_with_special_cases = graph_tens | graph_seventies_and_nineties | graph_eighties\n", - "graph_teens_and_tens = graph_tens_with_special_cases | teens\n", - "graph_all = graph_teens_and_tens | zero " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "xWjSAGRX_s0H" - }, - "source": [ - "Making sure test cases work:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "kapWmgos-xcn", - "outputId": "5e9c6f5c-1450-495f-cadf-2945355b651c" - }, - "outputs": [], - "source": [ - "apply_fst(\"quatre-vingt-treize\" , graph_all)\n", - "apply_fst(\"quatre-vingts\", graph_all)\n", - "apply_fst(\"quatre-vingt-deux\", graph_all)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "hNUepfKZ_vS_" - }, - "source": [ - "And the other cases fail as expected:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "wo2pCOXGAgYn", - "outputId": "0bbe2792-8bc9-40f7-dd28-4745bd1390e3" - }, - "outputs": [], - "source": [ - "apply_fst(\"quatre-vingt\", graph_all)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "4VPuCTTtigh-" - }, - "source": [ - "Of course, there are other ways we could have reconfigured the grammar: we could simply make specific graphs for multiples of ten (`10,20,30..`) and all cases where \"-et-\" occurs (`21,31,41,51...91`). \n", - "\n", - "But this ignores a more important question: was any of this necessary in the first place? All these extra grammars did was simply expand coverage for thirty additional cardinals. And they still didn't exclude all faulty inputs! Note the following cases:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "KICvpeewCFyH", - "outputId": "174dd910-7329-4a5f-a5b0-5e796a174217" - }, - "outputs": [], - "source": [ - "apply_fst(\"dix-une\", graph_all) # supposed to be \"onze\"\n", - "apply_fst(\"dix-deux\", graph_all) # supposed to be \"douze\"\n", - "apply_fst(\"vingt-un\", graph_all) # supposed to be \"vingt-et-un\"\n", - "apply_fst(\"trente-un\", graph_all) # supposed to be \"trente-et-un\"" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "0D130jIVCLp2" - }, - "source": [ - "We *still* need to address possible edge cases!\n", - "\n", - "All of this is to say that knowing your input domain before construction is imperative, as small decisions can easily determine your output range later down the line.\n", - "\n", - "Indeed, if you're particularly concerned with limiting input possibilities, it may be valid simply to write all unique options within a `string_map`. While a tad inelegant, it certainly assists in controlling your outputs." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "RSp9w5ayA9ii" - }, - "outputs": [], - "source": [ - "graph_tens_special = pynini.string_map([\n", - "\t\t\t\t(\"soixante-dix\", \"70\"),\n", - "\t\t\t\t(\"soixante-et-onze\",\"71\"),\n", - " (\"soixante-douze\",\"72\"),\n", - "\t\t\t\t(\"soixante-treize\",\"73\"),\n", - "\t\t\t\t(\"soizante-quatorze\",\"74\"),\n", - "\t\t\t\t(\"soixante-quinze\",\"75\"),\n", - "\t\t\t\t(\"soixante-seize\",\"76\"),\n", - " (\"soixante-dix-sept\",\"77\"),\n", - " (\"soixante-dix-huit\",\"78\"),\n", - "\t\t\t\t(\"soixante-dix-neuf\",\"79\"),\n", - " (\"quatre-vingts\", \"80\"),\n", - " (\"quatre-vingt-un\", \"81\"),\n", - " (\"quatre-vingt-une\", \"81\"),\n", - "\t\t\t\t(\"quatre-vingt-deux\",\"82\"),\n", - " (\"quatre-vingt-trois\",\"83\"),\n", - " (\"quatre-vingt-quatre\",\"84\"),\n", - " (\"quatre-vingt-cinq\",\"85\"),\n", - " (\"quatre-vingt-six\",\"86\"),\n", - " (\"quatre-vingt-sept\",\"87\"),\n", - " (\"quatre-vingt-huit\",\"88\"),\n", - " (\"quatre-vingt-neuf\",\"89\"),\n", - " (\"quatre-vingt-dix\",\"90\"),\n", - " (\"quatre-vingt-onze\",\"91\"),\n", - " (\"quatre-vingt-douze\",\"92\"),\n", - " (\"quatre-vingt-treize\",\"93\"),\n", - " (\"quatre-vingt-quatorze\",\"94\"),\n", - " (\"quatre-vingt-quinze\",\"95\"),\n", - " (\"quatre-vingt-sieze\",\"96\"),\n", - " (\"quatre-vingt-dix-sept\",\"97\"),\n", - " (\"quatre-vingt-dix-huit\",\"98\"),\n", - " (\"quatre-vingt-dix-neuf\",\"99\"),])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "NUPs1qOUg-hE" - }, - "source": [ - "Which is more efficient? Once again, it is dependent on your language and implementation. If we simply visualize each graph and their number of states:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "sQ9GsIkNzxsU", - "outputId": "d70ca927-9c43-4f49-846c-c181e725e011" - }, - "outputs": [], - "source": [ - "constructed_version = (graph_seventies_and_nineties | graph_eighties)\n", - "constructed_version.num_states()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "Xsgdu5TYx09_", - "outputId": "5812912f-883b-42e8-afbf-3ec4a0170345" - }, - "outputs": [], - "source": [ - "string_map_version = graph_tens_special\n", - "string_map_version.num_states()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "9jzn_U7s0Sit" - }, - "source": [ - "We see that their number of states (graph vertexes) are almost equal. Yet, if we use `pynini.optimize` - a method that calls a suite of WFST minimization algorithms: " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "7YtqhOY90iF0", - "outputId": "26f0f51b-b00d-4f5a-9b2f-330c9812666a" - }, - "outputs": [], - "source": [ - "constructed_version.optimize()\n", - "constructed_version.num_states()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "y93SqnOf0qa8", - "outputId": "74efcbfa-a272-4fc6-e36e-f1e31c6df221" - }, - "outputs": [], - "source": [ - "string_map_version.optimize()\n", - "string_map_version.num_states()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "2cTdQj9L0xhl" - }, - "source": [ - "We see the latter possessing a significantly larger amount of graph vertices. \n", - "\n", - "So the decision will be dependent on your ITN needs, language, concern with efficiency, and design philosophy. Further, even decisions of language dialect will have an influence. \n", - "(e.g. Belgian, Canadian, and Swiss dialects of French will dispense with elements of the vigecimal system for the decimal schema.)\n", - "\n", - ">**_Note:_** \n", - ">while `nemo_text_processing` grammars aim to minimize invalid productions, they assume input tokens are valid strings for a target language. (e.g. The mapping of \"quatre-vingt\" to `80` is permitted since it is not likely to occur in a valid French string.)* " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "V1djCnvY3CjW" - }, - "source": [ - "For more information on optimization algorithms for WFSTs, please see:\n", - "\n", - "- [M. Mohri,\"Generic epsilon-removal and input epsilon-normalization algorithms for weighted transducers\"](https://cs.nyu.edu/~mohri/pub/ijfcs.pdf)\n", - "- [M. Mohri, \"Weighted automata algorithms\"](https://cs.nyu.edu/~mohri/pub/hwa.pdf)\n", - "- [K. Thompson, \"Programming techniques: regular expression search algorithm\"](http://www.oilshell.org/archive/Thompson-1968.pdf)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Hundreds\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "dqPUdVBbi6gU" - }, - "source": [ - "\n", - "Moving on to the case of three digit cardinals (\"hundreds\"), it is likely that your chosen language becomes more regular in its schema. For instance, practically all French numbers `>100` obey the following:\n", - "\n", - "- `digit_from_1_to_9 + word_for_hundred + digit_from_1_to_99`\n", - "\n", - "For example:\n", - "- `203` - \"deux-cent-trois\"\n", - "- `530` - \"cinq-cent-trente\"\n", - "- `880` - \"huit-cent-quatre-vingt\"\n", - "\n", - "As such, we can write a simple `hundreds` WFST as:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "lOt-gc-FiF-X" - }, - "outputs": [], - "source": [ - "hundreds = graph_digits + delete_hyphen + pynutil.delete(\"cent\") + delete_hyphen + graph_all" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "Fyn1uL_NoEiz", - "outputId": "d491680b-1b3e-4762-8470-497833b82b0e" - }, - "outputs": [], - "source": [ - "apply_fst(\"deux-cent-trois\", hundreds)\n", - "apply_fst(\"huit-cent-quatre-vingts\", hundreds)\n", - "apply_fst(\"cinq-cent-trente\" , hundreds) " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "qDjq_KfnoD5C" - }, - "source": [ - "Indeed, the use of French only presents two complications:\n", - "- French uses *only* the word \"cent\" for `100`. (Instead of \"un cent\".)\n", - "- 'Pure' multiples of a hundred (`200,300,400....`) use the plural \"cents\".\n", - "\n", - "The second one is the easier of the two so let's start there. There are actually two options open to us. First, we could treat \"cents\" the same way as we did \"cent\" in the base case and simply delete it. From there, the lack of any following inputs will allow the WFST to insert the trailing zeroes as appropriate." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "m2F-sumbxqLE" - }, - "outputs": [], - "source": [ - "cents = pynini.accep(\"cent\") | pynini.accep(\"cents\") # Creates a Finite State (Accep)tor, mapping inputs back to themselves\n", - "hundreds = graph_digits + delete_hyphen + pynutil.delete(cents) + delete_hyphen + graph_all" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "VisQu_Etx-QB" - }, - "source": [ - "Or we can use it as a cue to 'shortcut' the WFST to immediately insert zeroes." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "VspiTN5Vxxjl" - }, - "outputs": [], - "source": [ - "graph_cents = pynini.cross(\"cents\", \"00\") # Creates a single input-output mapping\n", - "hundreds = graph_digits + delete_hyphen + ((pynutil.delete(\"cent\") + delete_hyphen + graph_all) | graph_cents)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "meVn5BiyyX5v" - }, - "source": [ - "For the case of solitary \"cent\", we need to make sure our output is `1` only in the case that no digit precedes the occurrence. Here we need to be confident in the structure of our WFST and that any possible ambiguity has been dealt with by this point. (Something to keep in mind as we move to the thousands.)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "277Z-zLWyWAf" - }, - "outputs": [], - "source": [ - "graph_cent = pynini.cross(\"cent\", \"1\")\n", - "graph_hundreds_first_digit = (graph_digits + delete_hyphen + pynutil.delete(cents)) | graph_cent\n", - "graph_hundreds = graph_hundreds_first_digit + delete_hyphen + graph_all" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "FNZlJsvS_Yvt", - "outputId": "e85ae561-e7a1-4b6a-e394-f0194fdb89e7" - }, - "outputs": [], - "source": [ - "apply_fst(\"trois-cents\", graph_hundreds) \n", - "apply_fst(\"cent\", graph_hundreds)\n", - "apply_fst(\"cent-trois\", graph_hundreds) " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Thousands" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "e7Dy5slLzp-K" - }, - "source": [ - "For quite a few languages, managing the WFST for the thousands place is the last aspect to figure out, as the higher powers of ten reuse the same schema. (For those working with counting systems that reserve special terms for \"ten-thousand\" (e.g. Chinese derived counting systems), you may need to extend unique coverage to the next power of ten.)\n", - "\n", - "For French, the question of thousands is rather simple: `digits_from_1_to_999 + mille + digits_from_1_to_999`\n", - "\n", - "With only the exception that any expression of one thousand drops a leading digit. \n", - "- `1,000` -> \"mille\"\n", - "- `1,001` -> \"mille-un\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "AvsnAAiPzlu_" - }, - "outputs": [], - "source": [ - "graph_one_thousand = pynini.cross(\"mille\", \"1\")\n", - "graph_many_thousand = graph_hundreds + delete_hyphen + pynutil.delete(\"mille\")\n", - "\n", - "graph_thousands = (graph_one_thousand | graph_many_thousand) + delete_hyphen + graph_hundreds" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "i3m9TG7Y4tkl", - "outputId": "d3f1f81d-c463-4934-9df7-3b8f2b67798f" - }, - "outputs": [], - "source": [ - "apply_fst(\"cent-mille-deux-cents\", graph_thousands)\n", - "apply_fst(\"deux-cent-mille-deux-cents\", graph_thousands)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "NoevSTZGGT17" - }, - "source": [ - "### Weighting" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "A2gcVIZM0-iv" - }, - "source": [ - "Question: will this cover all our grammar so far? (Hint: what assumptions were made about \"cent\"/\"cents\"?)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "cCFtPhr1BjAc", - "outputId": "048e0d93-a4a8-4f4e-d461-bfd70e911aff" - }, - "outputs": [], - "source": [ - "apply_fst(\"deux-mille-un\", graph_thousands)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Ne-7L9Cd4t-8" - }, - "source": [ - "Once again, we need to introduce the possibility of the prior power of ten not occurring in the string. There must be an option for simply inserting a string of `0` in place of the omitted \"cent\"." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "iockqXdn-aG4" - }, - "source": [ - "Further, we want to be careful with how cavalier we have been with insertions. Consider the following:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "bxJlSnj2-Xw3", - "outputId": "6722e5ef-8a7f-43e1-84fe-b3f5f18307e1" - }, - "outputs": [], - "source": [ - "apply_fst(\"mille-cent-un\", graph_thousands) # Should be 1101\n", - "apply_fst(\"mille-cent\", graph_thousands) # 1100" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "fq5zEayA-kOx" - }, - "source": [ - "It appears that our WFST has developed a tendency to simply 'ignore' some of these higher powers. Let us return to our code for `graph_hundreds` and `graph_thousands`. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "S2aV1KQ4-1iP" - }, - "outputs": [], - "source": [ - "graph_cents = pynini.cross(\"cents\", \"00\")\n", - "graph_cent = pynini.cross(\"cent\", \"1\")\n", - "graph_hundreds_first_digit = (graph_digits + delete_hyphen + pynutil.delete(cents)) | graph_cent\n", - "graph_hundreds = (graph_hundreds_first_digit + delete_hyphen | pynutil.insert(\"0\")) + graph_all \n", - "\n", - "graph_one_thousand = pynini.cross(\"mille\", \"1\")\n", - "graph_many_thousand = graph_hundreds + delete_hyphen + pynutil.delete(\"mille\")\n", - "graph_thousands = (graph_one_thousand | graph_many_thousand) + delete_hyphen + graph_hundreds" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "9avwOIkk-9qt" - }, - "source": [ - "Recall that throughout we have provided options for simply inserting zeroes in the case of omitted numbers? That tendency has finally caught up with us. The use of our previous `graph_hundreds` in `graph_many_thousands` now allows our graph to insert a string of `0`'s without penalty. \n", - "\n", - "You may note that this is very similar to the \"cents\" example brought up at the beginning, presenting a similar solution. We can control this output by making it too costly to traverse unless absolutely necessary for the graph. This can be accomplished simply by appending a weight to the insertion for hundreds:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "MQG3j0U8CUAQ" - }, - "outputs": [], - "source": [ - "graph_hundreds = (graph_hundreds_first_digit + delete_hyphen | pynutil.insert(\"0\", weight=.1)) + graph_all \n", - "\n", - "graph_one_thousand = pynini.cross(\"mille\", \"1\")\n", - "graph_many_thousand = graph_hundreds + delete_hyphen + pynutil.delete(\"mille\")\n", - "graph_thousands = (graph_one_thousand | graph_many_thousand) + delete_hyphen + graph_hundreds" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "KNHhrYZ7Ca58", - "outputId": "a7d07372-733d-4837-c1e9-1dc58ba2b87c" - }, - "outputs": [], - "source": [ - "apply_fst(\"mille-cent-un\", graph_thousands)\n", - "apply_fst(\"mille-cent\", graph_thousands)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "51yPEaf2EkbD" - }, - "source": [ - "Why choose a weight of `.1`? Quite simply: it's arbitrary. As mentioned earlier, the default graph in `pynini` is a tropical semiring, which uses the `min` function to select among two arcs for path traversal. Since all our paths so far are weight `0`, any positive value will ensure that it is a last option among path traversal. (Note, this conversely entails any negative weight path will be prioritized.)\n", - "\n", - "That we chose this number as a small value comes from a place of caution: the tropical semiring uses an additive function to calculate the total weight of an entire path to traverse a WFST. As our grammars can easily become massive, this means that small weights can have major impact down the line. Further, by constraining path weights to small values, we can have general certainty towards the maximum weight of any individual graph, allowing us to add constraints regarding maximum token length and token hierarchy. (As explained in [later sections](#classifyweights).) As such, when using weights in a localized setting, it is best to use small values to avoid unforeseen escalation. " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "iScKgvRxGt-B" - }, - "source": [ - "### Higher Powers\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "rtHEd6OE2WSg" - }, - "source": [ - "At this point, we can propose a general heuristic with escalating to higher powers of ten: they always need a way for their absence to be accommodated in the WFST. Further, they require some weighting to prevent this absence from developing into a string of omitted values. To avoid further bumps, we'll take care of this now with `graph_thousands`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "iZMN7wcE2lH5" - }, - "outputs": [], - "source": [ - "graph_one_thousand = pynini.cross(\"mille\", \"1\")\n", - "graph_many_thousand = graph_hundreds + delete_hyphen + pynutil.delete(\"mille\")\n", - "graph_thousands = (graph_one_thousand | graph_many_thousand | pynutil.insert(\"000\", weight=.001)) + delete_hyphen + graph_hundreds" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Fkc3LIH824P7" - }, - "source": [ - "\n", - "For the rest of French (and many other languages), the rest of the work is simply repeating the prior pattern for the thousands element: \n", - "`hundreds + word_for_higher_power + hundreds.....` Of course there will be some variation in this schema, but the recursion should be regular. (It is rather rare that languages appropriate unique forms for these higher counts.) " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "qGnK4ARX4Nay" - }, - "source": [ - "To finish French, we can list off the following equivalent for higher powers of ten:\n", - "- `million` - \"million/millions\" \n", - "- `billion` - \"milliard/milliards\"\n", - "- `trillion` - \"billion/billions\"\n", - "\n", - "Like the \"cent/cents\" rule, these values alternate with a plural form in the case of multiples of the value. Writing them out:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "sBu7-dub4vxz" - }, - "outputs": [], - "source": [ - "millions = pynini.accep(\"million\") | pynini.accep(\"millions\")\n", - "graph_millions = ((graph_hundreds + delete_hyphen + pynutil.delete(millions) + delete_hyphen) | pynutil.insert(\"000\", weight=.1) # We need three zeroes now\n", - " ) + graph_thousands" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "LmMeCHXr5Bb5" - }, - "outputs": [], - "source": [ - "billions = pynini.accep(\"milliards\") | pynini.accep(\"milliard\")\n", - "graph_billions = ((graph_hundreds + delete_hyphen + pynutil.delete(billions) + delete_hyphen)| pynutil.insert(\"000\",weight=.1) # We need three zeroes now\n", - " ) + graph_millions" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "CIRIeQEg5B0J" - }, - "outputs": [], - "source": [ - "trillions = pynini.accep(\"billion\") | pynini.accep(\"billions\")\n", - "graph_trillions = ((graph_hundreds + delete_hyphen + pynutil.delete(trillions) + delete_hyphen) | pynutil.insert(\"000\",weight=.1) # We need three zeroes now\n", - " ) + graph_billions" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "sRNUPx-15J1v" - }, - "source": [ - "Bringing all together:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "0dLOWm_B5SwQ" - }, - "outputs": [], - "source": [ - "graph = graph_trillions | zero" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "nBFE3BrN6IPR" - }, - "source": [ - "Let's try it out:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "6lWwtR1S6LI4", - "outputId": "3a6740ee-9e92-4500-c2c8-965131167e58" - }, - "outputs": [], - "source": [ - "example = \"deux-cent-milliard-quatre-million-deux-cent-quatre-vingt-onze\"\n", - "apply_fst(example, graph) " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Finishing Touches" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "-w3KgX6C6mff" - }, - "source": [ - "Now that we have our cardinal in place, we can take care of that stylistic issue of the leading zeroes. For this, we want to develop a 'filter' that deletes all zeroes preceding the first non-zero in the string, and leave the rest 'as is.'\n", - "\n", - "First let us create the filter by calling on `NEMO_DIGIT`- a `graph_util` WFST that only permits digits as input. With it, we'll create a WFST that will delete all leading zeroes in a sting. We then compose this (using `@`) onto our original graph, creating a new graph that accepts inputs from our original but produces only the outputs of `clean_cardinal`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 290 - }, - "id": "EA4VnRe6FO-2", - "outputId": "59e412b3-a445-4172-ee64-b0f80281a167" - }, - "outputs": [], - "source": [ - "delete_leading_zeroes = pynutil.delete(pynini.closure(\"0\")) # will delete all zeroes under closure. Equivalent to regex * operator\n", - "stop_at_non_zero = pynini.difference(NEMO_DIGIT, \"0\") # creates a graph that accepts all input-outputs from NEMO_DIGIT except 0\n", - "rest_of_cardinal = pynini.closure(NEMO_DIGIT) # accepts all digits that may follow\n", - "\n", - "clean_cardinal = delete_leading_zeroes + stop_at_non_zero + rest_of_cardinal\n", - "clean_cardinal = clean_cardinal | \"0\" # We don't want to ignore the occurrence of zero\n", - "\n", - "graph = graph @ clean_cardinal " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "piP9nqQkHpo3" - }, - "source": [ - "Now our WFST will output our numbers as normal:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "dnQ9odSpIAB7" - }, - "outputs": [], - "source": [ - "apply_fst(example, graph)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Final Notes\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "p7zt8lVsK2rY" - }, - "source": [ - "We have finally formulated a grammar that will process French cardinals into numeric representation. Of course, not every grammar you write will be for French. But several of the principles we've worked through will be invaluable in your own development. Before moving on, here's a quick summary of (almost) universal points to take away for WFST construction.\n", - "- Decide at the beginning of construction the level of constraint you wish for your grammar. Is it necessary to have a specific domain or can you rely on upstream models to narrow your input possibilities for you? \n", - "- Work iteratively upwards from the smallest place value of your numeric system. This will assist you in forming building blocks for larger values. \n", - "- Always allow for the possibility of omission of previous place values. (Not every number in the thousands will contain mention of the hundreds place.)\n", - "- For each place value, consider how the sub-grammar will affect the preceding and following place values. Are there exceptions that you've built into the grammar that may become problematic later on?\n", - "- Utilize weights for default insertions to limit path traversal to only final options. When doing so, use small values to avoid escalating problems in your larger grammar." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "nvyHg1bQIIHD" - }, - "source": [ - "With that handled, we can move on to converting this grammar into a Classifier." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "gJ1YJUvhIZwm" - }, - "source": [ - "## Classifier" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "q2L2x0crIeXQ" - }, - "source": [ - "Now that we have a grammar that will convert individual tokens into number strings, we now want to focus on building it into a classifier to properly tag candidate tokens. This requires a couple of properties:\n", - "- It recognizes any valid token and permits traversal through the WFST graph\n", - "- Conversely, it does not allow invalid tokens to traverse the WFST graph\n", - "- It properly disambiguates overlap among ambiguous cases\n", - "- It attributes the proper attributes to a classified token\n", - "\n", - "While this seems like a lot, in practice this just means that your grammar will need a few more tweaks to improve exclusivity." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "ArEYn7RWKcYI" - }, - "source": [ - "NeMo ITN performs token classification through a series of `GraphFst` classes and assumes deployment of your grammars through an object that inherits from this class. As such, you will need to instantiate your grammar as a `CardinalFST` " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 368 - }, - "id": "GWgMSybqLqiS", - "outputId": "597c00ae-0f62-417f-888c-88c81c24a3fc" - }, - "outputs": [], - "source": [ - "class CardinalFst(GraphFst):\n", - " def __init__(self):\n", - " super().__init__(name=\"cardinal\", kind=\"classify\")\n", - " # Rest of the grammar here\n", - " # ....... \n", - " #........." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "SIE8dNQlL52G" - }, - "source": [ - "While the naming convention may vary, the `name` and `kind` properties must be set accordingly to permit Sparrowhawk integration.\n", - "\n", - "Further, the resulting graph must produce the classified token within the following format:\n", - "`token { cardinal { integer: \"DIGIT_STRING\" } }`\n", - "\n", - "This is accomplished by a series of string insertions:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "aC_c64KSNTCg" - }, - "outputs": [], - "source": [ - "class CardinalFst(GraphFst):\n", - " def __init__(self):\n", - " super().__init__(name=\"cardinal\", kind=\"classify\")\n", - " # Rest of the grammar here\n", - " # ....... \n", - " #.........\n", - " self.fst = pynutil.insert(\"integer: \\\"\") + graph + pynutil.insert(\"\\\"\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "AGLQxOSzOK1F" - }, - "source": [ - "Followed by a call of the parent `GraphFst.add_tokens()` method:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "Jz-UXFipORps" - }, - "outputs": [], - "source": [ - "class CardinalFst(GraphFst):\n", - " def __init__(self):\n", - " super().__init__(name=\"cardinal\", kind=\"classify\")\n", - " # Rest of the grammar here\n", - " # ....... \n", - " #.........\n", - " self.fst = pynutil.insert(\"integer: \\\"\") + graph + pynutil.insert(\"\\\"\")\n", - " final_graph = self.add_tokens(graph)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "gh23S7BHOY0r" - }, - "source": [ - "Which will insert the appropriate formatting. Note that this formatting must be exact: a single space must follow each field name and each value must be within escaped double quotes.\n", - "\n", - "In the event that you also wish for `CardinalFst` to indicate negative values, the optional `negative: ` property may be used.\n", - "\n", - "For instance, French indicates negative values by prefacing the quantity with \"moins.\" As such:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "3JbTn35cOx0k" - }, - "outputs": [], - "source": [ - "optional_minus_graph = pynini.closure(\n", - " pynutil.insert(\"negative: \") + pynini.cross(\"moins\", \"\\\"-\\\"\") + \" \", 0, 1 # Note the extra space to separate the value from the integer field\n", - ")\n", - "\n", - "final_graph = optional_minus_graph + pynutil.insert(\"integer: \\\"\") + graph + pynutil.insert(\"\\\"\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "DCs1048v6N0K" - }, - "source": [ - "All together, your `CardinalFst` ultimately serves as a wrapper for your grammar, save with the addition of a few insertions to assist processing:\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "eo6uEz1s5TJY" - }, - "outputs": [], - "source": [ - "class CardinalFst(GraphFst):\n", - " def __init__(self):\n", - " super().__init__(name=\"cardinal\", kind=\"classify\")\n", - " \n", - " ### Cardinal Grammar....\n", - " ### .....\n", - " graph = graph_trillions | zero \n", - "\n", - " ### Formatting grammar....\n", - " ### .....\n", - " graph = graph @ clean_cardinal\n", - "\n", - " ### Token insertion\n", - " optional_minus_graph = pynini.closure(\n", - " pynutil.insert(\"negative: \") + pynini.cross(\"moins\", \"\\\"-\\\"\") + \" \", 0, 1\n", - " )\n", - "\n", - " final_graph = optional_minus_graph + pynutil.insert(\"integer: \\\"\") + graph + pynutil.insert(\"\\\"\")\n", - "\n", - " final_graph = self.add_tokens(final_graph) # inserts the cardinal tag\n", - "\n", - " self.fst = final_graph" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "MFIMdLCoZzLK" - }, - "source": [ - "Let's see a demonstration. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "4CF6Iz9NZ7R_" - }, - "outputs": [], - "source": [ - "cardinal = CardinalFst().fst\n", - "\n", - "example = \"moins deux-cent-quatre\"\n", - "\n", - "apply_fst(example, cardinal)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Verbalizer" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "uvUqpC_Q8FSt" - }, - "source": [ - "The verbalizer can be both the most crucial and simplest part of building each grammar. On one hand, it is the component that finalizes all of your previous work. If it is unable to properly normalize your text, everything has been for naught.\n", - "\n", - "On the other hand, your previous work has vastly limited the unpredictability of your input. Recall from our initial demonstration of the classifier-verbalizer system that and input like <> becomes:\n", - "\n", - "- `tokens { name: \"le\" }`\n", - "- `tokens { date { day: \"1\" month: \"juillet\" }` \n", - "- `tokens { name: \"il\" }` \n", - "- `tokens { name: \"a\" }` \n", - "- `tokens { name: \"mangé\" }`\n", - "- `tokens { cardinal { integer: \"35\" } }` \n", - "- `tokens { name: \"pommes\" }`\n", - "\n", - "Part of the purpose of the two stage set-up is that the input space for each verbalizer is obvious: it's simply the name of its semiotic class. As such, we only need to write our grammar to recognize its class, remove tokens accordingly, and then manage the attributes of each semiotic token." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "et1GgmBuAWzY" - }, - "source": [ - "We will begin as we did with our classifier and create a class to inherit from the `GraphFST` utility class:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "NNKpgWtkAgEW" - }, - "outputs": [], - "source": [ - "class CardinalFst(GraphFst):\n", - " def __init__(self):\n", - " super().__init__(name=\"cardinal\", kind=\"verbalize\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "OyAV39NsAqSN" - }, - "source": [ - "One of the useful aspects of the `GraphFst` utility is that it already possesses a built in graph that will recognize and remove semiotic tokens: `delete_tokens`. As such we need only concern ourselves with managing the properties of the Cardinal class:\n", - "- `integers`\n", - "- `negative`\n", - "\n", - "Here, the desired written format of your chosen language will dictate how you proceed. For French, we have the following rules for Cardinal numbers:\n", - "- A negative sign is written before the numeral.\n", - "- Cardinal numbers representing quantities (e.g. \"mille euros\"/ \"one thousand dollars\") are written with spaces in-between every three digits. (e.g. `1 000`)\n", - "- Cardinal numbers representing place in a sequence or addresses (\"page mille\"/\"page one thousand\") are written without spacing. (`1000`)\n", - "\n", - "The first property seems easy enough to handle: write a grammar that simply removes the `negative` formatting, leaving only `-`. (Recall that our Classifier only inserted the string if it was present.) \n", - "\n", - "For the final two, we may note that our intention to develop WFSTs for the Decimal, Measure, and Money classes already will cover most desired quantities. As such, we can leave the issue of spacing to those instances and let the Cardinal WFST default to the non-spacing case. (Note that this will be helpful with Time, Date, Telephone, Electronic, and Ordinal classes as they will not use the spacing format either. It is usually better to reserve specific formatting rules to other classes and let the Cardinal serve as a default.)\n", - "\n", - "As such, we just need our WFST to remove the `integer` property and `negative` property (if it occurs). These can be managed through the `pynutil.delete` function, as seen in the following:\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 368 - }, - "id": "6MF2I6SLU7nf", - "outputId": "0437c4af-5c96-4122-8af0-ca37723c7228" - }, - "outputs": [], - "source": [ - "class CardinalFst(GraphFst):\n", - " def __init__(self):\n", - " super().__init__(name=\"cardinal\", kind=\"verbalize\")\n", - " \n", - " # Removes the negative attribute and leaves the sign if occurs\n", - " optional_sign = pynini.closure(\n", - " pynutil.delete(\"negative:\")\n", - " + delete_space\n", - " + pynutil.delete(\"\\\"\")\n", - " + pynini.accep(\"-\")\n", - " + pynutil.delete(\"\\\"\")\n", - " + delete_space,\n", - " 0,\n", - " 1,\n", - " )\n", - " \n", - " # removes integer aspect\n", - " graph = (\n", - " pynutil.delete(\"integer:\")\n", - " + delete_space\n", - " + pynutil.delete(\"\\\"\")\n", - " + pynini.closure(NEMO_DIGIT, 1) # Accepts at least one digit\n", - " + pynutil.delete(\"\\\"\")\n", - " )\n", - " \n", - " graph = optional_sign + graph # concatenates two properties\n", - "\n", - " delete_tokens = self.delete_tokens(graph) # removes semiotic class tag\n", - "\n", - " self.fst = delete_tokens.optimize()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "QSX2KlZJbRAA" - }, - "source": [ - "Let's see if it will properly render a given token:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "JxaLm2k0bYIJ" - }, - "outputs": [], - "source": [ - "cardinal = CardinalFst().fst\n", - "example = 'cardinal { negative: \"-\" integer: \"204\" }'\n", - "\n", - "apply_fst(example, cardinal)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Bc0-QCBHWg-8" - }, - "source": [ - "That's it! We've now completed all aspects of our `CardinalFst` from grammar writing to Verbalization. While we still have quite a few semiotic classes left, you will find that they build off the `CardinalFst` quite easily, making progression much simpler and straightforward.\n", - "\n", - ">**_Note:_**\n", - ">- `delete_tokens` is called on the completed graph, despite the token class occurring first in the tokenized string. This is because the function intersects with an initial WFST that deletes the tags. As such, the function must be passed a completed graph.\n", - ">- In our initial example, all tokens were enclosed within a `token` category. Insertion and deletion of this category is managed by the main [Classifier](#tokenize-and-classify) and [Verbalizer](#verbalize-and-verbalize-final) respectively and is not a concern during individual class grammar development.\n", - ">- Earlier in the tutorial we noted that NeMo ITN permutates all WFSTs unless the `preserve_order` tag is passed as part of the Classifier. This allows you to ignore possible variation in designing the verbalizer and focus on whatever form of processing is easiest for the grammar. That is, the decision to process the `negative` property before the `integer` property is not chosen because of a consequence of the French language but instead because it is easier to write out with `pynini`. \n", - ">- Conversely, if your language is completely invariant in this regard, it may be more efficient to pass `preserve_order` through the Classifier and manage the property here in the Verbalizer. This allows NeMo ITN to avoid building states and arcs for each permutation, reducing graph size and compiling time." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "aFUrbSdJ8Wk7" - }, - "source": [ - "# Ordinal WFST " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "w1b0Z7f5Z9Ar" - }, - "source": [ - "Ordinals is the class of numbers used for enumerating order or placement of entities in a series. In some languages, they are simply derivations of cardinal numbers. For instance, English enumerates order as `first, second, third, fourth, fifth....` After the third ordinal, they become a regular pattern of `cardinal + 'th'`.\n", - "\n", - "Meanwhile, other languages may reserve specific counting systems for ordinals. For example, while Korean uses a Chinese derived counting system for several Cardinal related tasks, it uses derivations from a native counting system for ordering:\n", - "\n", - "**Cardinal**/**Ordinal** = **English**\n", - "- il/cheot-jae = \"First\"\n", - "- i/dul-jae = \"Second\"\n", - "- sam/set-jae = \"Third\"\n", - "- sa/net-jae = \"Fourth\"\n", - "- o/daseot-jae = \"Fifth\"\n", - "\n", - "If your language is of the latter variety, you will likely need to begin development of Ordinal WFST by repeating Cardinal WFST development before proceeding. (Or make it part of your previous Cardinal WFST and combining with a `union` operation.) While you can extend coverage to the level of Cardinal WFST, you will find most Ordinals to be sufficiently covered by only enumerating to a few hundreds. (e.g. Is it common in your language to speak of the \"one millionth\" in an order and/or write out `1,000,000th`?)\n", - "\n", - "For this portion of the tutorial, we will focus on the first type of ordinals - those that primarily derived by altering Cardinals." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "oq_xA8NPiANw" - }, - "source": [ - "## Grammar" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "lhjcQS6oiD_w" - }, - "source": [ - "Continuing with our example language, we first begin by laying out our expected inputs and pinpointing a regular pattern to guide our WFSTs. We note the following examples:\n", - "\n", - " **English = French**\n", - " - \"first\" = \"premier/première\"\n", - " - \"second\" = \"second/seconde/deuxième\"\n", - " - \"third\" = \"troisième\"\n", - " - \"fourth\" = \"quatrième\"\n", - " - \"fifth\" = \"cinquième\"\n", - " - \"sixth\" = \"sixième\"\n", - " - \"seventh\" = \"septième\"\n", - "\n", - "From our examples inputs, it appears that spelling of French Ordinals follows a general format of: `cardinal + ième`. The only exceptions appear to be in the case of the first and second Ordinals - for which completely different roots appear - and the fourth and the fifth Ordinals - where the former drops the \"e\" at the end of the root (`quatre -> quatr`) and the latter appends a \"u\" (`cinq -> cinqu`). \n", - "\n", - "For the expected outputs, we observe the following examples:\n", - " - \"premier/première\" -> `1ᵉʳ/1ʳᵉ`\n", - " - \"second/seconde\" -> `2ᵈ/2ᵈᵉ`\n", - " - \"deuxième\" -> `2ᵉ`\n", - " - \"troisième\" -> `3ᵉ`\n", - " - \"quatrième\" -> `4ᵉ`\n", - " - \"cinquième\" -> `5ᵉ`\n", - " - \"sixième\" -> `6ᵉ`\n", - " - \"septième\" -> `7ᵉ`\n", - "\n", - "It appears that the output is simply the cardinal number of the root with an associated superscript. Since we have already constructed the Cardinal WFST, this means that the job of constructing an Ordinal WFST is simply a case of recognizing the cardinal root for the input and then utilizing a preconstructed Cardinal grammar to render the proper form alongside an associated superscript. That is, our tasks are to:\n", - "- Identify the proper superscript for the ordinal\n", - "- Change the ordinal back into a cardinal\n", - "- Use the Cardinal WFST to transform the cardinal into normalized form\n", - "- Properly render the ordinal using the normalized cardinal and proper superscript\n", - "\n", - "As information regarding the superscript will need to be conveyed through development of the Classifier, we will begin with creating the grammar necessary for rendering the ordinal as its cardinal root. \n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "AOUVZhiwT7hE" - }, - "source": [ - "### Stripping Suffixes" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "5nw0_lOTsEik" - }, - "source": [ - "Since French forms Ordinals by appending a suffix to Cardinals, we should start by creating a WFST to remove the suffix. Assuming that our grammar processes one token at a time, this means that we just need an WFST that will accept all tokens that end with \"ième\" and then delete the suffix from that token:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "Rk89LhsxsHTO" - }, - "outputs": [], - "source": [ - "strip_morpheme = pynutil.delete(\"ième\") # deletes suffix\n", - "graph_strip_morpheme = NEMO_SIGMA + strip_morpheme # accepts all strings until passed suffix, then deletes suffix" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "pLg-PzdntV4N" - }, - "source": [ - "Now we can create a graph that permits all characters in a word token and deletes the ordinal suffix. (Note that this also means that the graph won't accept tokens without the suffix, helping us avoid false inputs.) \n", - "\n", - "We can now intersect this graph with our Cardinal WFST to now strip the suffixes from ordinals and treat them as cardinals. However, recall that our `CardinalFst` also inserted its own class tag. Obviously, we do not want to do this here as it will disrupt the formatting of the token. Instead, we should create a new subgraph *within* the `CardinalFst` class that will only produce the cardinals without tokens." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "class CardinalFst(GraphFst):\n", - " def __init__(self):\n", - " super().__init__(name=\"cardinal\", kind=\"classify\")\n", - " \n", - " ### Cardinal Grammar....\n", - " ### .....\n", - " graph = graph_trillions | zero \n", - "\n", - " ### Formatting grammar....\n", - " ### .....\n", - " graph = graph @ clean_cardinal\n", - " \n", - " ### NEW GRAPH\n", - " self.just_cardinals = graph # will produce cardinals without formatting\n", - "\n", - " ### Token insertion\n", - " optional_minus_graph = pynini.closure(\n", - " pynutil.insert(\"negative: \") + pynini.cross(\"moins\", \"\\\"-\\\"\") + \" \", 0, 1\n", - " )\n", - "\n", - " final_graph = optional_minus_graph + pynutil.insert(\"integer: \\\"\") + graph + pynutil.insert(\"\\\"\")\n", - "\n", - " final_graph = self.add_tokens(final_graph)\n", - "\n", - " self.fst = final_graph" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we call it for our graph:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "vxDgBa4_t1nD" - }, - "outputs": [], - "source": [ - "graph_cardinal = CardinalFst().just_cardinals \n", - "graph_ordinal_regular_suffix = graph_strip_morpheme @ graph_cardinal" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "hSpk5M7BuXRz" - }, - "source": [ - "Let's see if it works and gives us the desired cardinal:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "7cJ7fieouY2r" - }, - "outputs": [], - "source": [ - "example = \"sixième\" # dervied from six/6\n", - "apply_fst(example, graph_ordinal_regular_suffix)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "GtEuV7sOuxek" - }, - "source": [ - "Now we can consider the edge cases. Beyond the first and second ordinals, French exhibits irregular behavior in the following cases:\n", - "- If the cardinal root ends with an \"e\", the \"e\" is dropped before adding the suffix (e.g. \"quatrième\"). \n", - "- Cardinals ending with \"cinq\", \"neuf\", and \"dix\" change their endings to \"cinqu\", \"neuv\" , and \"diz\" before appending the suffix, respectively. \n", - "\n", - "We could start by proposing a WFST that replaces the suffix \"ième\" with \"e\" and then compose this onto the Cardinal WFST. If it is a legitimate cardinal, then there will be a path through CardinalFST and the integer will be rendered as normal. \n", - "\n", - "Meanwhile, the case of \"dix\", \"cinq\", and \"neuf\" would each require a distinct WFST as they are each a consequence of different rules of orthography and phonology. Like the case with \"e\", we could change each back to its root and then see if the CardinalWFST will permit a path with the new input. \n", - "\n", - "It is at this point that we can do a cost-benefit analysis and realize that all these cases can be managed by an explicit `string_map/string_file`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "_9KTNQeIw4sq" - }, - "outputs": [], - "source": [ - "graph_root_change = pynini.string_map([(\"quatrième\", \"quatre\"),\n", - " (\"cinquième\",\t\"cinq\"),\n", - " (\"neuvième\",\t\"neuf\"),\n", - " (\"onzième\",\t\"onze\"),\n", - " (\"douzième\",\t\"douze\"),\n", - " (\"treizième\",\t\"treize\"),\n", - " (\"quatorzième\",\t\"quatorze\"),\n", - " (\"quinzième\",\t\"quinze\"),\n", - " (\"seizième\",\t\"seize\"),\n", - " (\"trentième\",\t\"trente\"),\n", - " (\"quarantième\",\t\"quarante\"),\n", - " (\"cinquantième\",\t\"cinquante\"),\n", - " (\"soixantième\",\t\"soixante\"),\n", - " (\"millième\",\t\"mille\"),\n", - "])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "eo2_keFVqaY4" - }, - "source": [ - "We could then concatenate these with a WFST that accepts all tokens with these endings and then change the endings as desired. These will provide the cardinal roots just as effectively. " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "O7I29ezmxylx" - }, - "source": [ - "The same can be said for \"premier/première\" and \"second/seconde\":" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "3JZoz51VyGS6" - }, - "outputs": [], - "source": [ - "graph_firsts = pynini.string_map([(\"premier\", \"un\"),(\"première\", \"un\")])\n", - "graph_seconds = pynini.string_map([(\"second\", \"deux\"),(\"seconde\", \"deux\")])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "NJ9BGGAwyTQ5" - }, - "source": [ - "*Note: We graph separately to manage their different superscripts later on.*\n", - "\n", - "Depending on your language of focus, the choice of implicitly reversing the root token or explicitly mapping back to root will be the most efficient, but it is worth considering both options if only to check your understanding of the language." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "8PgVwDRRq9gr" - }, - "source": [ - "Putting our grammar together, we have:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "ko2kAeKwrRSH" - }, - "outputs": [], - "source": [ - "strip_morpheme = pynutil.delete(\"ième\") # deletes suffix\n", - "\n", - "graph_root_change = pynini.string_map([(\"quatrième\", \"quatre\"),\n", - " (\"cinquième\",\t\"cinq\"),\n", - " (\"neuvième\",\t\"neuf\"),\n", - " (\"onzième\",\t\"onze\"),\n", - " (\"douzième\",\t\"douze\"),\n", - " (\"treizième\",\t\"treize\"),\n", - " (\"quatorzième\",\t\"quatorze\"),\n", - " (\"quinzième\",\t\"quinze\"),\n", - " (\"seizième\",\t\"seize\"),\n", - " (\"trentième\",\t\"trente\"),\n", - " (\"quarantième\",\t\"quarante\"),\n", - " (\"cinquantième\",\t\"cinquante\"),\n", - " (\"soixantième\",\t\"soixante\"),\n", - " (\"millième\",\t\"mille\"),\n", - "])\n", - "\n", - "# Component will accept all tokens that end with desired strings\n", - "graph_get_cardinal = NEMO_SIGMA + (strip_morpheme | graph_root_change) \n", - "\n", - "graph_firsts = pynini.string_map([(\"premier\", \"un\"),(\"première\", \"un\")])\n", - "graph_seconds = pynini.string_map([(\"second\", \"deux\"),(\"seconde\", \"deux\")])\n", - "\n", - "graph_get_cardinal = pynini.union(graph_firsts, graph_seconds, graph_get_cardinal) \n", - "\n", - "graph_cardinal = CardinalFst().just_cardinals\n", - "\n", - "graph_ordinal = graph_get_cardinal @ graph_cardinal" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "ESxY3LsCdE8q" - }, - "outputs": [], - "source": [ - "apply_fst(\"sixième\", graph_ordinal)\n", - "apply_fst(\"première\", graph_ordinal)\n", - "apply_fst(\"seconde\", graph_ordinal)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "qo_g8UdoUFJB" - }, - "source": [ - "## Classifier" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "kemhdKAjzEIa" - }, - "source": [ - "Now that we've found a way to pass the work of the Ordinal grammar back onto the Cardinal grammar, we can move onto the Classifier. Like before, we need to inherit from `GraphFst` to properly insert token formatting and required attributes. As well, we will again use the `integer` property to tag our digit string.\n", - "\n", - "Indeed, the only major difference between the Ordinal Classifier and the Cardinal Classifier is the replacement of optional `negative` attribute with the `morphosyntactic_feature` attribute to indicate the superscript function." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "EHM4Y3TW2nXT" - }, - "source": [ - "Since we are relying on the `CardinalFst` class in our grammar, we want to consider how to instantiate an instance of it. Since our ultimate goal is to build a Classifier that unites all semiotic classes, it makes sense to simply use the `CardinalFst` that we will need to call for our ITN and pass it as an argument to our new class." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 273 - }, - "id": "KsmPhWSa3LF_", - "outputId": "9e881ca9-a926-4249-dda8-9c52175569b5" - }, - "outputs": [], - "source": [ - "def __init__(self, cardinal: GraphFst):\n", - " super().__init__(name=\"ordinal\", kind=\"classify\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "CtBQ-udB3S5Q" - }, - "source": [ - "To clear up the namespace, we will now be importing from the NeMo implementation of `CardinalFst` for French." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "L-JAcidf4QQg" - }, - "outputs": [], - "source": [ - "from nemo_text_processing.inverse_text_normalization.fr.taggers.cardinal import CardinalFst\n", - "\n", - "class OrdinalFst(GraphFst):\n", - " def __init__(self, cardinal: GraphFst):\n", - " super().__init__(name=\"ordinal\", kind=\"classify\")\n", - " graph_cardinal = cardinal.graph_no_exception # NeMo equivalent to self.just_cardinals" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "FQfkAqZavCAB" - }, - "source": [ - "We now add in our grammar:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "uUQ4BLuivGut" - }, - "outputs": [], - "source": [ - "class OrdinalFst(GraphFst):\n", - " def __init__(self, cardinal: GraphFst):\n", - " super().__init__(name=\"ordinal\", kind=\"classify\")\n", - " graph_cardinal = cardinal.graph_no_exception # may replace\n", - "\n", - " strip_morpheme = pynutil.delete(\"ième\") # deletes suffix\n", - "\n", - " graph_root_change = pynini.string_map([(\"quatrième\", \"quatre\"),\n", - " (\"cinquième\",\t\"cinq\"),\n", - " (\"neuvième\",\t\"neuf\"),\n", - " (\"onzième\",\t\"onze\"),\n", - " (\"douzième\",\t\"douze\"),\n", - " (\"treizième\",\t\"treize\"),\n", - " (\"quatorzième\",\t\"quatorze\"),\n", - " (\"quinzième\",\t\"quinze\"),\n", - " (\"seizième\",\t\"seize\"),\n", - " (\"trentième\",\t\"trente\"),\n", - " (\"quarantième\",\t\"quarante\"),\n", - " (\"cinquantième\",\t\"cinquante\"),\n", - " (\"soixantième\",\t\"soixante\"),\n", - " (\"millième\",\t\"mille\"),\n", - " ])\n", - " \n", - " # Component will accept all tokens that end with desired strings\n", - " graph_get_cardinal = NEMO_SIGMA + (strip_morpheme | graph_root_change) \n", - "\n", - " graph_firsts = pynini.string_map([(\"premier\", \"un\"),(\"première\", \"un\")])\n", - " graph_seconds = pynini.string_map([(\"second\", \"deux\"),(\"seconde\", \"deux\")])\n", - "\n", - " graph_get_cardinal = pynini.union(graph_firsts, graph_seconds, graph_get_cardinal) \n", - "\n", - " graph_ordinal = graph_get_cardinal @ graph_cardinal\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "F_6EXPRMvnp2" - }, - "source": [ - "Now we come to the `morphosyntactic_features` property - a linguistic term for aspects of a word related to grammar. If intending to deploy your WFST through Sparrowhawk, this is the only ordinal property that is permitted (outside of the universal properties like `preserve_order`) and thus must carry all information regarding how to properly normalize the ordinal. (If Sparrowhawk deployment is not necessary, you may add additional properties to the tag.)\n", - "\n", - "How should we convey this information? Since the Verbalizer will be the main interface for our tags, it really does not matter - so long as we can reliably process the features. For the purposes of French, we just need `morphosyntactic_features` to decide the following:\n", - "- Insert the specific superscripts for \"premier/première\" or \"second/seconde\"\n", - "- Insert \"ᵉ\" otherwise\n", - "\n", - "We will also introduce another aspect of French Ordinals: they can be either plural or singular, identified by the suffix \"s\" on input and superscript \"ˢ\" on output. As such, our `morphosyntactic_features` should also decide the additional property:\n", - "- Insert the plural superscript " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "atctz6p-2GtV" - }, - "source": [ - "Since the default superscript is near universal, we will just specify this in our WFST and focus on the second and first ordinals as specific cases. We will create a `graph_morpheme` component that inserts the default superscript - indicated with a standard \"e\" to avoid possible encoding issues. We will then append a WFST that will graph any possible plural marker - \"s\" - as part the `morphosyntactic_features`. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "ui99osyP2UuQ" - }, - "outputs": [], - "source": [ - "graph_morpheme = pynutil.insert(\"e\") # Insert e superscript\n", - "graph_plural = pynini.closure(pynini.accep(\"s\"), 0, 1) # We create an acceptor since we must process the possible \"s\"\n", - "\n", - "graph_morpheme_component = graph_morpheme + graph_plural\n", - "\n", - "graph_morphosyntactic_features = (pynutil.insert(\" morphosyntactic_features: \\\"\") \n", - " + graph_morpheme_component\n", - " )" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "QAlqubA25gq0" - }, - "source": [ - "Introducing the `integer` feature:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "rs2TyIBc5la6" - }, - "outputs": [], - "source": [ - "graph_reg_ordinals = graph_get_cardinal @ graph_cardinal # Rewriting ordinals to remove the first and second ordinal.\n", - "\n", - "graph_ordinal = pynutil.insert(\"integer: \\\"\") + graph_reg_ordinals + pynutil.insert(\"\\\"\")\n", - "graph_ordinal += graph_morphosyntactic_features" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "xoqk20Pi2gT8" - }, - "source": [ - "For the first and second ordinals, we can explicitly state their mappings, as these occurrences are invariable. (First and second ordinals do not need to accommodate being the endings of other terms.) As such, we can just have mappings from the token to the superscripts." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "54aqdH_P63Ea" - }, - "outputs": [], - "source": [ - "firsts = pynini.string_map([(\"premier\", \"er\"), (\"première\",\"re\")])\n", - "firsts += graph_plural # Still accepts plural marker in superscript\n", - "seconds = pynini.string_map([(\"second\", \"d\"),(\"seconde\", \"de\")])\n", - "seconds += graph_plural \n", - "\n", - "graph_firsts = pynutil.insert(\"integer: \\\"1\\\" morphosyntactic_features: \\\"\") + firsts\n", - "graph_seconds = pynutil.insert(\"integer: \\\"2\\\" morphosyntactic_features: \\\"\") + seconds" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "D2vQ4m7o7p84" - }, - "source": [ - "Placing them in our class:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "w_JKT8JMf-Mz" - }, - "outputs": [], - "source": [ - "class OrdinalFst(GraphFst):\n", - " def __init__(self, cardinal: GraphFst):\n", - " super().__init__(name=\"ordinal\", kind=\"classify\")\n", - " graph_cardinal = cardinal.graph_no_exception # may replace\n", - "\n", - " strip_morpheme = pynutil.delete(\"ième\") # deletes suffix\n", - "\n", - " graph_root_change = pynini.string_map([(\"quatrième\", \"quatre\"),\n", - " (\"cinquième\",\t\"cinq\"),\n", - " (\"neuvième\",\t\"neuf\"),\n", - " (\"onzième\",\t\"onze\"),\n", - " (\"douzième\",\t\"douze\"),\n", - " (\"treizième\",\t\"treize\"),\n", - " (\"quatorzième\",\t\"quatorze\"),\n", - " (\"quinzième\",\t\"quinze\"),\n", - " (\"seizième\",\t\"seize\"),\n", - " (\"trentième\",\t\"trente\"),\n", - " (\"quarantième\",\t\"quarante\"),\n", - " (\"cinquantième\",\t\"cinquante\"),\n", - " (\"soixantième\",\t\"soixante\"),\n", - " (\"millième\",\t\"mille\"),\n", - " ])\n", - " \n", - " # Component will accept all tokens that end with desired strings\n", - " graph_get_cardinal = NEMO_SIGMA + (strip_morpheme | graph_root_change) \n", - "\n", - " # Graph will map ordinals beyond second ordinal to their cardinals\n", - " graph_reg_ordinals = graph_get_cardinal @ graph_cardinal\n", - "\n", - " # Graphing morphosyntactic_features\n", - " graph_morpheme = pynutil.insert(\"e\") # Insert e superscript\n", - " graph_plural = pynini.accep(\"s\").ques # ques is equivalent to pynini.closure(, 0, 1)\n", - "\n", - " graph_morpheme_component = graph_morpheme + graph_plural\n", - "\n", - " graph_morphosyntactic_features = (pynutil.insert(\" morphosyntactic_features: \\\"\") \n", - " + graph_morpheme_component\n", - " )\n", - "\n", - " # Adding in the `integer` property:\n", - " graph_ordinal = pynutil.insert(\"integer: \\\"\") + graph_reg_ordinals + pynutil.insert(\"\\\"\")\n", - " graph_ordinal += graph_morphosyntactic_features \n", - "\n", - " # Case of first and second ordinals\n", - " firsts = pynini.string_map([(\"premier\", \"er\"), (\"première\",\"re\")])\n", - " firsts += graph_plural # Still accepts plural marker in superscript\n", - " seconds = pynini.string_map([(\"second\", \"d\"),(\"seconde\", \"de\")])\n", - " seconds += graph_plural \n", - "\n", - " graph_firsts = pynutil.insert(\"integer: \\\"1\\\" morphosyntactic_features: \\\"\") + firsts\n", - " graph_seconds = pynutil.insert(\"integer: \\\"2\\\" morphosyntactic_features: \\\"\") + seconds\n", - "\n", - " # All together\n", - " graph_ordinal = pynini.union(graph_ordinal, graph_firsts, graph_seconds)\n", - " self.fst = graph_ordinal.optimize()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "CpGHVg6chmA0" - }, - "source": [ - "Trying out on some examples:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "b5DL3PZRhpc8" - }, - "outputs": [], - "source": [ - "cardinal = CardinalFst()\n", - "ordinal = OrdinalFst(cardinal).fst\n", - "\n", - "apply_fst(\"premier\", ordinal)\n", - "apply_fst(\"premiers\", ordinal)\n", - "apply_fst(\"seconde\", ordinal)\n", - "apply_fst(\"douzièmes\", ordinal)\n", - "apply_fst(\"cent-cinquièmes\", ordinal)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "MNQVgiv-UK29" - }, - "source": [ - "### Special Tokens" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "UdiNAHGh71O9" - }, - "source": [ - "If you are particularly astute, you may have noticed that we have not closed the quotations around the `morphosyntactic_features` throughout, despite doing so for `integer`. This is not a typo, as there is one more aspect of the Classifier that must be addressed: special cases.\n", - "\n", - "For your language, you may notice that there are occasional exceptions to writing rules that are signaled by a specific vocabulary token in a string. As this must be communicated to our Verbalizer, it is important that we signal this vocabulary through our Classifier. \n", - "\n", - "For French, this can occur in the normalization of centuries. When using Ordinals to indicate centuries, French commonly writes with Roman numerals. For example:\n", - "- \"Fifth century\" -> \"cinquième siècle\" -> `Vᵉ siècle` \n", - "- \"Twentieth century\" -> \"vintième siècle\" -> `XXᵉ siècle` \n", - "\n", - "As such, we must allow our Classifier to pass on the information that \"siècle\" follows an ordinal to our Verbalizer, so it may normalize with Roman numerals. We accomplish this by appending a WFST that accepts special tokens that follow our Ordinals, adding them to our `morphosyntactic_features` attribute with a forward slash to delineate." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "MsWnT4BfQKcC" - }, - "outputs": [], - "source": [ - "special_tokens = pynini.accep(\"siècle\")\n", - "\n", - "graph_special_tokens = delete_space + pynutil.insert(\"/\") + special_tokens # We need to delete the space in between this token and the following one.\n", - "graph_special_tokens = pynini.closure(graph_special_tokens, 0, 1)\n", - "\n", - "graph_ordinal += graph_special_tokens + pynutil.insert(\"\\\"\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "698_n5SFQ_jP" - }, - "source": [ - "*Once again, it is advised to retain a tsv file in `data` to quickly append these key-words.*\n", - "\n", - "Having taken care of the special case, we may now call `add_tokens` and complete the graph (fully written out below)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "nZ1dkft0Riou" - }, - "outputs": [], - "source": [ - "class OrdinalFst(GraphFst):\n", - " def __init__(self, cardinal: GraphFst):\n", - " super().__init__(name=\"ordinal\", kind=\"classify\")\n", - " graph_cardinal = cardinal.graph_no_exception # may replace\n", - "\n", - " strip_morpheme = pynutil.delete(\"ième\") # deletes suffix\n", - "\n", - " graph_root_change = pynini.string_map([(\"quatrième\", \"quatre\"),\n", - " (\"cinquième\",\t\"cinq\"),\n", - " (\"neuvième\",\t\"neuf\"),\n", - " (\"onzième\",\t\"onze\"),\n", - " (\"douzième\",\t\"douze\"),\n", - " (\"treizième\",\t\"treize\"),\n", - " (\"quatorzième\",\t\"quatorze\"),\n", - " (\"quinzième\",\t\"quinze\"),\n", - " (\"seizième\",\t\"seize\"),\n", - " (\"trentième\",\t\"trente\"),\n", - " (\"quarantième\",\t\"quarante\"),\n", - " (\"cinquantième\",\t\"cinquante\"),\n", - " (\"soixantième\",\t\"soixante\"),\n", - " (\"millième\",\t\"mille\"),\n", - " ])\n", - " \n", - " # Component will accept all tokens that end with desired strings\n", - " graph_get_cardinal = NEMO_SIGMA + (strip_morpheme | graph_root_change) \n", - "\n", - " # Graph will map ordinals beyond second ordinal to their cardinals\n", - " graph_reg_ordinals = graph_get_cardinal @ graph_cardinal\n", - "\n", - " # Graphing morphosyntactic_features\n", - " graph_morpheme = pynutil.insert(\"e\") # Insert e superscript\n", - " graph_plural = pynini.accep(\"s\").ques # We create an acceptor since we must process the possible \"s\"\n", - "\n", - " graph_morpheme_component = graph_morpheme + graph_plural\n", - "\n", - " graph_morphosyntactic_features = (pynutil.insert(\" morphosyntactic_features: \\\"\") \n", - " + graph_morpheme_component\n", - " )\n", - "\n", - " # Adding in the `integer` property:\n", - " graph_ordinal = pynutil.insert(\"integer: \\\"\") + graph_reg_ordinals + pynutil.insert(\"\\\"\")\n", - " graph_ordinal += graph_morphosyntactic_features \n", - "\n", - " # Case of first and second ordinals\n", - " firsts = pynini.string_map([(\"premier\", \"er\"), (\"première\",\"re\")])\n", - " firsts += graph_plural # Still accepts plural marker in superscript\n", - " seconds = pynini.string_map([(\"second\", \"d\"),(\"seconde\", \"de\")])\n", - " seconds += graph_plural \n", - "\n", - " graph_firsts = pynutil.insert(\"integer: \\\"1\\\" morphosyntactic_features: \\\"\") + firsts\n", - " graph_seconds = pynutil.insert(\"integer: \\\"2\\\" morphosyntactic_features: \\\"\") + seconds\n", - "\n", - "\n", - " # Special tokens\n", - " special_tokens = pynini.accep(\"siècle\")\n", - "\n", - " graph_special_tokens = delete_space + pynutil.insert(\"/\") + special_tokens # We need to delete the space in between this token and the following one.\n", - " graph_special_tokens = pynini.closure(graph_special_tokens, 0, 1)\n", - "\n", - " graph_ordinal += graph_special_tokens + pynutil.insert(\"\\\"\")\n", - "\n", - " # Finishing\n", - " graph_ordinal = self.add_tokens(graph_ordinal)\n", - " self.fst = graph_ordinal.optimize()\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "7a4zBo-YS1QD" - }, - "source": [ - "## Verbalizer" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "zYbrcGyGS2rW" - }, - "source": [ - "The initial part of the Ordinal Verbalizer is similar to the Cardinal WFST: we simply need to build a Verbalizer that inherits from `GraphFST` and removes the `integer` property tag. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "KUv99A_rYjb9" - }, - "outputs": [], - "source": [ - "class OrdinalFst(GraphFst):\n", - " def __init__(self):\n", - " super().__init__(name=\"ordinal\", kind=\"verbalize\")\n", - " graph_integer = (\n", - " pynutil.delete(\"integer:\")\n", - " + delete_space\n", - " + pynutil.delete(\"\\\"\")\n", - " + pynini.closure(NEMO_DIGIT, 1)\n", - " + pynutil.delete(\"\\\"\")\n", - " )" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "zKCt_EapZXGW" - }, - "source": [ - "Now we need to manage the `morphosyntactic_features` component. The first steps seem simple enough: delete the property tag and replace the superscript indicators with the actual superscripts. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "yoa_mXMLabrU" - }, - "outputs": [], - "source": [ - " # Create mappings for all superscripts\n", - " superscript = pynini.union(\n", - " pynini.cross(\"e\", \"ᵉ\"), # only delete first quote since there may be more features\n", - " pynini.cross(\"d\", \"ᵈ\"),\n", - " pynini.cross(\"r\", \"ʳ\"),\n", - " pynini.cross(\"s\", \"ˢ\"),\n", - " )\n", - "\n", - " # Append to deletion of feature property. Note that we use plus closure for multiple superscripts.\n", - " graph_morphosyntactic_features = pynutil.delete(\" morphosyntactic_features: \\\"\") + superscript.plus" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "xOA7_MsUrSJS" - }, - "source": [ - "### Romanization" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "K_SaG0DUa2t7" - }, - "source": [ - "Now we come to the possible Romanization component. Since we need to graph the superscript components as following the number, we want to design our graph so that `morphosyntactic_features` is the last component of the graph. However, we do not know that we need Romanization until we see the `morphosyntactic_features` component. As such, we need to design our graph such that two options are available initially for an input, but only one allows full traversal." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "7dalc-tablG-" - }, - "source": [ - "![romanization.png](images/romanization.PNG)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "mPTNCddNcEEE" - }, - "source": [ - "In cases where your WFST decisions are dependent on latter parts of an input string, permitting the union of two separate paths when only one is valid usually assists, as a standard pathing heuristic will only choose the valid path. \n", - "\n", - "In the case of French, this would require us to separate our Verbalizer into two parts: one for Arabic numerals and one for Roman numerals. For the Arabic WFST, we simply conclude the graph. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "0YSy1PYOcuyD" - }, - "outputs": [], - "source": [ - "graph_integer = (\n", - " pynutil.delete(\"integer:\")\n", - " + delete_space\n", - " + pynutil.delete(\"\\\"\")\n", - " + pynini.closure(NEMO_DIGIT, 1)\n", - " + pynutil.delete(\"\\\"\")\n", - " )\n", - "graph_Arabic = graph_integer + graph_morphosyntactic_features + pynutil.delete(\"\\\"\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "nnXjUU5Pf7Sh" - }, - "source": [ - "For the Roman graph, things get a bit trickier. Ideally, we would want to build a WFST that maps each digit of `graph_Arabic` to a Roman equivalent. However, consider the following examples:\n", - "- 1 -> I\n", - "- 10 -> X\n", - "- 11 -> XI\n", - "- 100 -> C\n", - "- 101 -> CI\n", - "- 110 -> CX\n", - "- 111 -> CXI\n", - "\n", - "Since Roman numerals do not preserve powers of ten through digit placement, we will need to design separate FSTs for each digit position and apply them accordingly. As this can quickly become intensive, we will only work to enumerate the Ordinals from 1 to 100. (Note: We are doing this to accommodate centuries; there is little likelihood that any century beyond the 99th will be used in regular strings.)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "3-fQHMc2iQrz" - }, - "source": [ - "First we design our graphs for converting from Arabic to Roman numerals:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "d6PDySykiXTh" - }, - "outputs": [], - "source": [ - "digits = pynini.string_map([(\"1\", \"I\"),\n", - " (\"2\",\t\"II\"),\n", - " (\"3\",\t\"III\"),\n", - " (\"4\",\t\"IV\"),\n", - " (\"5\",\t\"V\"),\n", - " (\"6\",\t\"VI\"),\n", - " (\"7\",\t\"VII\"),\n", - " (\"8\",\t\"VIII\"),\n", - " (\"9\",\t\"IX\"),\n", - " ])\n", - "tens = pynini.string_map([(\"1\", \"X\"),\n", - " (\"2\",\t\"XX\"),\n", - " (\"3\",\t\"XXX\"),\n", - " (\"4\",\t\"XL\"),\n", - " (\"5\",\t\"L\"),\n", - " (\"6\",\t\"LX\"),\n", - " (\"7\",\t\"LXX\"),\n", - " (\"8\",\t\"LXXX\"),\n", - " (\"9\",\t\"XC\"),\n", - " ])\n", - "zero = pynutil.delete(\"0\") # No Roman representation for zero." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "wb-LmwJdk59m" - }, - "source": [ - "Now we build two separate filters: one will accept only single digit Arabic numerals and the other will accept two digit Arabic numerals. For this we can use `NEMO_DIGIT`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "DW3oD7Hbli2X" - }, - "outputs": [], - "source": [ - "map_one_digit = NEMO_DIGIT\n", - "map_two_digits = NEMO_DIGIT ** 2 # pynini overloads the exponent function to allow self-concatenation." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "xtYKLy9AmJZS" - }, - "source": [ - "We now build mappings between two digit Arabic numerals and Roman numerals, composing them onto the filters:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "dUy7uEUXmT_g" - }, - "outputs": [], - "source": [ - "graph_one_digit_romans = NEMO_DIGIT @ digits\n", - "\n", - "graph_two_digit_romans = tens + (digits | zero)\n", - "graph_two_digit_romans = map_two_digits @ graph_two_digit_romans\n", - "\n", - "graph_romans = graph_one_digit_romans | graph_two_digit_romans" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "JEinyAMdm7RJ" - }, - "source": [ - "We now take care of the occurrence of \"siècle\" before composing onto `graph_integer`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "ERO19BbynPNX" - }, - "outputs": [], - "source": [ - "graph_romans = (graph_integer @ graph_romans) + graph_morphosyntactic_features\n", - "graph_romans += pynini.cross(\"/\", \" \") + \"siècle\" + pynutil.delete(\"\\\"\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "zN-fwrCGoToQ" - }, - "source": [ - "We finalize with a union and calling `delete_tokens`, the complete Verbalizer now being::" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "kr2wcToAofWB" - }, - "outputs": [], - "source": [ - "class OrdinalFst(GraphFst):\n", - " def __init__(self):\n", - " super().__init__(name=\"ordinal\", kind=\"verbalize\")\n", - "\n", - " # Maps integer and removes attribute\n", - " graph_integer = (\n", - " pynutil.delete(\"integer:\")\n", - " + delete_space\n", - " + pynutil.delete(\"\\\"\")\n", - " + pynini.closure(NEMO_DIGIT, 1)\n", - " + pynutil.delete(\"\\\"\")\n", - " )\n", - "\n", - " # Create mappings for all superscripts\n", - " superscript = pynini.union(\n", - " pynini.cross(\"e\", \"ᵉ\"), # only delete first quote since there may be more features\n", - " pynini.cross(\"d\", \"ᵈ\"),\n", - " pynini.cross(\"r\", \"ʳ\"),\n", - " pynini.cross(\"s\", \"ˢ\"),\n", - " )\n", - "\n", - " # Append to deletion of feature property. Note that we use plus closure for multiple superscripts.\n", - " graph_morphosyntactic_features = pynutil.delete(\" morphosyntactic_features: \\\"\") + superscript.plus\n", - "\n", - " # Writing WFST for Arabic\n", - " graph_Arabic = graph_integer + graph_morphosyntactic_features + pynutil.delete(\"\\\"\")\n", - "\n", - " # Mapping Roman numerals\n", - " digits = pynini.string_map([(\"1\", \"I\"),\n", - " (\"2\",\t\"II\"),\n", - " (\"3\",\t\"III\"),\n", - " (\"4\",\t\"IV\"),\n", - " (\"5\",\t\"V\"),\n", - " (\"6\",\t\"VI\"),\n", - " (\"7\",\t\"VII\"),\n", - " (\"8\",\t\"VIII\"),\n", - " (\"9\",\t\"IX\"),\n", - " ])\n", - " tens = pynini.string_map([(\"1\", \"X\"),\n", - " (\"2\",\t\"XX\"),\n", - " (\"3\",\t\"XXX\"),\n", - " (\"4\",\t\"XL\"),\n", - " (\"5\",\t\"L\"),\n", - " (\"6\",\t\"LX\"),\n", - " (\"7\",\t\"LXX\"),\n", - " (\"8\",\t\"LXXX\"),\n", - " (\"9\",\t\"XC\"),\n", - " ])\n", - " zero = pynutil.delete(\"0\") # No Roman representation for zero.\n", - "\n", - " # filters for Roman digits\n", - " map_one_digit = NEMO_DIGIT\n", - " map_two_digits = NEMO_DIGIT ** 2 # pynini overloads the exponent function to allow self-concatenation.\n", - "\n", - " # Composing onto roman digits\n", - " graph_one_digit_romans = NEMO_DIGIT @ digits\n", - "\n", - " graph_two_digit_romans = tens + (digits | zero)\n", - " graph_two_digit_romans = map_two_digits @ graph_two_digit_romans\n", - "\n", - " graph_romans = graph_one_digit_romans | graph_two_digit_romans\n", - "\n", - " # Writing WFST for Roman\n", - " graph_romans = (graph_integer @ graph_romans) + graph_morphosyntactic_features\n", - " graph_romans += pynini.cross(\"/\", \" \") + \"siècle\" + pynutil.delete(\"\\\"\")\n", - "\n", - " # Final composition\n", - " graph = (graph_romans | graph_Arabic)\n", - "\n", - " delete_tokens = self.delete_tokens(graph)\n", - " self.fst = delete_tokens.optimize()\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Trying out our examples:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "example_regular = 'ordinal { integer: \"12\" morphosyntactic_features: \"es\" }'\n", - "example_roman = 'ordinal { integer: \"12\" morphosyntactic_features: \"es/siècle\" }'\n", - "\n", - "fst = OrdinalFst().fst\n", - "\n", - "apply_fst(example_regular, fst)\n", - "apply_fst(example_roman, fst)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "yBgLhTq9pWZe" - }, - "source": [ - "We have now completed an Ordinal WFST from the ground up, allowing a separate numbering system for special cases." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "-W1-BMVJUXXk" - }, - "source": [ - "## Final notes" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "kR7E64P4pPU_" - }, - "source": [ - "Before moving on, there are some key takeaways that you may find useful for most (if not all) languages:\n", - "- Many ordinal systems rely on alteration of Cardinals. Even in the example of Korean, it is using a pre-existing counting system and adding a suffix to indicate ordering. As such, your Ordinal WFST will likely follow this tutorial's structure of changing the Ordinal to its original root and then relying on your Cardinal WFST for the majority of processing.\n", - "- The `morphosyntactic_features` property will carry the vast majority of information necessary for normalization through your Verbalizer.\n", - "- While not all writing systems have the same quirk as using Roman numerals in reference to centuries, you will likely find cases in your language when a specific token indicates unique rules for a semiotic class. Carrying this information to the Verbalizer is usually the simplest means of preserving the token while also facilitating normalization. " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Rx8-LuJOUaa5" - }, - "source": [ - "# Decimal WFST " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "D2MRXYxz8TGA" - }, - "source": [ - "\n", - "If the Cardinal WFST is the most crucial element of a normalization grammar, the construction of the Decimal WFST is a close second. Much like in the case of constructing Ordinals from Cardinal grammars, many aspects of the Decimal WFST will be reused throughout your other semiotic classes.\n", - "\n", - "To get started, you should study the numerical conventions in your language. In particular, you should take note of the following:\n", - "- How is the decimal component of a number pronounced in your language of focus. (e.g. The English number `1.33` can be verbalized as \"one point three three\" or \"one and thirty three hundredths.\")\n", - "- What is the punctuation mark used for decimal demarcation? (In North America, several writing systems use `.` while European nations will use `,`.)\n", - "- Are there general rules regarding pronunciation/formatting of numbers past the decimal demarcation? (e.g. Does your language pronounce each digit or pronounce as a series of three digit numbers?)\n", - "\n", - "Such questions will likely require some deep familiarity with the language, and it may benefit to ask a native speaker for some input. Of course, the level of depth is dependent on your needs, but researching these questions will help your normalization system appear more organic." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "UsK78ib4N-gb" - }, - "source": [ - "## Grammar" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "p4CLOOA9OAwZ" - }, - "source": [ - "In the case of French, we have the following guidelines:\n", - "- French uses the comma ( `,` ) for decimal delineation. It is articulated as \"virgule\".\n", - "- Decimals can be read as a series of digits or grouped as Cardinal numbers arbitrarily. (e.g. \"`.333` can be \"virgule trois trois trois\" or \"virgule trois-cent-trente-trois\".) \n", - "\n", - "As such, our grammar needs to accommodate the following pattern: \n", - "\n", - "`cardinal + \"virgule\" + string_of_cardinals`\n", - "\n", - "Given our experience with our previous WFSTs, this seems simple enough. We assume we have an instance of CardinalFST available and create a subcomponent to map the integer portion of a decimal:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "XSp9FTzhf0XZ" - }, - "outputs": [], - "source": [ - "cardinal = CardinalFst().graph_no_exception # NeMo equivalent of just_cardinals\n", - "\n", - "# place cardinal under closure to permit values <=1\n", - "graph_integer = pynini.closure(cardinal, 0, 1)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "bk3_3iawgAZE" - }, - "source": [ - "Compose it on a subcomponent that detects the delineator \"virgule\":" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "UMzfAKkngH6z" - }, - "outputs": [], - "source": [ - "delete_virgule = pynutil.delete(\"virgule\")\n", - "graph_decimal = graph_integer + delete_space + delete_virgule" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "GXjbtbLYgn17" - }, - "source": [ - "And permit the occurrence of several strings of cardinals to follow:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "LMMNBJz8gtTA" - }, - "outputs": [], - "source": [ - "graph_string_of_cardinals = delete_space + graph_cardinal\n", - "graph_string_of_cardinals = pynini.closure(graph_string_of_cardinals, 1)\n", - "\n", - "graph_decimal += graph_string_of_cardinals" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "jTgnRLddhGdE" - }, - "source": [ - "Let us try an example:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "D4rjDh0ShJAp" - }, - "outputs": [], - "source": [ - "example = \"trois virgule trois cinquante-cinq\" \n", - "apply_fst(example, graph_decimal) # Should output only the cardinals in the string" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "RfD1d9JOioyl" - }, - "source": [ - "### Ambiguity?" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "3IaI1mCIe_6i" - }, - "source": [ - "Note that our decision to include multiple strings of cardinals after the decimal marker has introduced some ambiguity into our WFST. Consider if a decimal number was followed by an integer series (e.g. `2.5, 5, 6`). Now what should be an application of one DecimalFST and two applications of a CardinalFST can be interpreted as a single DecimalFST application (e.g. `2.556`). What can be done?\n", - "\n", - "While we will address this in greater depth later (see [Tokenize and Classify](#tokenize-and-classify)), the short answer is that cases such as these must be calibrated according to use and linguistic intuition. As this is an inherent ambiguity in the language and its writing system, we can never truly remove this possibility without restricting our ability to model the language. However, we can rely on a few logical assumptions to guide our decision making:\n", - "- Unless the grammar is deployed in a restrictive setting (e.g. a Financial or environment where strings of numbers are often read in series) it's not likely for a valid string to exhibit this level of ambiguity. Speakers typically try to reduce possible ambiguity in their language production and would likely rephrase to avoid issues such as these. [See Grice's maxims](https://en.wikipedia.org/wiki/Cooperative_principle).\n", - "- While a language may allow a specific string by *rule*, speakers may typically avoid them *in practice* due to conventions or difficulty. In our case, while it may be possible to read `2,100 05` as \"deux virgule dix-mille-cinq\" (\"two point ten-thousand and five\"), it's dubious that a speaker would find such easier to read than \"deux virgule une zéro zéro zéro cinq\". (The place value of large strings tend to take longer to recognize.)\n", - "\n", - "While hardly satisfying, these two points will allow us to dismiss *some* worry. With the former observation being outside our grammar's ability to manage, we accommodate the latter point by using an alternate WFST from our CardinalFST: `numbers_up_to_million`. (To utilize in your own language, create a WFST in the Cardinal class right before building up to `graph_millions`. Again, calling `optimize` is advised.)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "piNe1AWspa4J" - }, - "outputs": [], - "source": [ - "cardinal = CardinalFst().numbers_up_to_million\n", - "\n", - "# place cardinal under closure to permit values <=1\n", - "graph_integer = pynini.closure(cardinal, 0, 1)\n", - "\n", - "delete_virgule = pynutil.delete(\"virgule\")\n", - "graph_decimal = graph_integer + delete_space + delete_virgule\n", - "\n", - "graph_string_of_cardinals = delete_space + cardinal\n", - "graph_string_of_cardinals = pynini.closure(graph_string_of_cardinals, 1)\n", - "\n", - "graph_decimal += graph_string_of_cardinals" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "B1gglt0tfM5V" - }, - "source": [ - "## Classifier" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "fVkOWkncgOZc" - }, - "source": [ - "Like with our previous WFSTs, the main duty for the classifier is inserting the necessary properties for the semiotic token. For the `decimal` tag, the following properties are used:\n", - "- `integer_part` - indicates value before decimal marker\n", - "- `fractional_part` - indicates values after the decimal marker\n", - "- `negative` - indicates if value is positive or negative (Optional)\n", - "- `quantity` - designates if decimal is in regards to a specific quantity. (See Quantities.)\n", - "\n", - "We can begin by inserting the `integer_part` around our `cardinal` subcomponent and the `fractional_part` around our `graph_string_of_cardinals`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "_zw_cDszh-fB" - }, - "outputs": [], - "source": [ - "graph_integer = pynutil.insert(\"integer_part: \\\"\") + cardinal + pynutil.insert(\"\\\" \")\n", - "graph_fractional = pynutil.insert(\"fractional_part: \\\"\") + graph_string_of_cardinals + pynutil.insert(\"\\\"\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "bxlnn_7tiQMn" - }, - "source": [ - "We then concatenate them together with a component that recognizes and removes the decimal separator." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "BxNS9_AwiWHf" - }, - "outputs": [], - "source": [ - "graph_integer_or_none = graph_integer | pynutil.insert(\"integer_part: \\\"0\\\" \", weight=.1) # In cases we don't always have an integer preceding\n", - "graph_decimal_no_sign = graph_integer_or_none + delete_space + pynutil.delete(\"virgule\") + graph_fractional" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "b7uGfsi4i5UI" - }, - "source": [ - "*Note that we allow insertion of 0 if there is no integer to accommodate reading of only decimal values*\n", - "\n", - "Now we allow the possibility of negative values. (Recall French uses \"moins\" to indicate the negative.)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "VsP79naojQZR" - }, - "outputs": [], - "source": [ - "graph_negative = pynini.cross(\"moins\", \"negative: \\\"-\\\" \") + delete_space\n", - "graph_decimal = graph_negative + graph_decimal_no_sign" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "QTcvq5HqllqW" - }, - "outputs": [], - "source": [ - "example = \"moins deux virgule cent-quatre\"\n", - "apply_fst(example, graph_decimal)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "FVKuGj_9mZ75" - }, - "source": [ - "Placing within a `DecimalFst` class, we have:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "tXwr32ermesp" - }, - "outputs": [], - "source": [ - "class DecimalFst(GraphFst):\n", - " def __init__(self, cardinal: GraphFst):\n", - " super().__init__(name=\"decimal\", kind=\"classify\")\n", - " cardinal = cardinal.numbers_up_to_million\n", - " delete_virgule = pynutil.delete(\"virgule\")\n", - "\n", - " graph_integer = pynutil.insert(\"integer_part: \\\"\") + cardinal + pynutil.insert(\"\\\" \") + delete_space\n", - " graph_integer_or_none = graph_integer | pynutil.insert(\"integer_part: \\\"0\\\" \", weight=.001) # In cases we don't always have an integer preceding\n", - "\n", - " graph_string_of_cardinals = delete_space + cardinal\n", - " graph_string_of_cardinals = pynini.closure(graph_string_of_cardinals, 1)\n", - " graph_fractional = pynutil.insert(\"fractional_part: \\\"\") + graph_string_of_cardinals + pynutil.insert(\"\\\"\")\n", - "\n", - " graph_decimal_no_sign = graph_integer_or_none + pynutil.delete(\"virgule\") + graph_fractional \n", - "\n", - " graph_negative = pynini.cross(\"moins\", \"negative: \\\"-\\\" \") + delete_space\n", - " graph_negative = pynini.closure(graph_negative, 0, 1)\n", - "\n", - " graph_decimal = graph_negative + graph_decimal_no_sign\n", - "\n", - " graph = self.add_tokens(graph_decimal)\n", - " self.fst = graph.optimize()\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "gjxI5mEKfHLo" - }, - "source": [ - "### Quantities" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "3WuwWPf3py7G" - }, - "source": [ - "Recalling our earlier remarks regarding convention in language use, you may find a need to adjust the DecimalFst when processing specific values. For instance, consider the following equivalencies from English:\n", - "- `1,500,000` = \"one million five hundred thousand\" = \"one point five million\" = `1.5 million`\n", - "- `2,750,000` = \"two million seven hundred and fifty thousand\" = \"two point seven five million\" = `2.75 million`\n", - "\n", - "For large numbers, there is a tendency to use the decimal system as though one is describing a quantity. Notably, there is a minimum value for which this is comfortable. (A speaker of English may say \"three point five trillion\" but \"three point five hundred\" comes off as odd.)\n", - "\n", - "This behavior can occur in other languages. For example, the amount of `$1,500,000` may be read in French as \"une virgule cinq million de dollars\" (\"one point five million dollars\"). " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "RgMBIKlYdsGz" - }, - "source": [ - "Our Classifier can be made to accommodate this behavior: we simply need to repeat what we did for `OrdinalFst` and set aside several key terms to trigger our model. For French, we will choose all terms added for values greater than a million. (Chosen empirically.)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "vEcsUXw5fUEe" - }, - "outputs": [], - "source": [ - "suffix = pynini.union(\n", - " \"million\",\n", - " \"millions\",\n", - " \"milliard\",\n", - " \"milliards\",\n", - " \"billion\",\n", - " \"billions\",\n", - " \"billiard\",\n", - " \"billiards\",\n", - " \"trillion\",\n", - " \"trillions\",\n", - " \"trilliard\",\n", - " \"trilliards\",\n", - " )" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "wIIUAsR-fgQA" - }, - "source": [ - "We will then need to use a WFST to graph any numbers the precede these amounts. Note, unlike for our `DecimalFst`, we need to permit cardinals as well as decimals. This is because we want to be able to normalize a phrase like \"three million\" to `3 million` as this will be less obtrusive than `3,000,000`.\n", - "\n", - "As such, we will call a `CardinalFst` and a `DecimalFst` in for `graph_quantities`. Since these are both utilized for our `DecimalFst`, it would be more efficient to just pass them along as function/class variables." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "yern-idtycWg" - }, - "outputs": [], - "source": [ - "def get_quantity(decimal, cardinal_up_to_thousand):\n", - " key_values = pynini.union(\n", - " \"million\",\n", - " \"millions\",\n", - " \"milliard\",\n", - " \"milliards\",\n", - " \"billion\",\n", - " \"billions\",\n", - " \"billiard\",\n", - " \"billiards\",\n", - " \"trillion\",\n", - " \"trillions\",\n", - " \"trilliard\",\n", - " \"trilliards\",\n", - " )\n", - " # The French WFST that this borrows from has not removed leading zeroes yet.\n", - " numbers = cardinal_up_to_thousand @ (\n", - " pynutil.delete(pynini.closure(\"0\")) + pynini.difference(NEMO_DIGIT, \"0\") + pynini.closure(NEMO_DIGIT)\n", - " )\n", - " res = (\n", - " pynutil.insert(\"integer_part: \\\"\")\n", - " + numbers\n", - " + pynutil.insert(\"\\\"\")\n", - " + (\n", - " pynini.union(delete_hyphen, delete_extra_space)\n", - " ) # Can be written either as 'deux-millions' or 'deux millions' depending on whether it registers as a noun or part of cardinal.\n", - " + pynutil.insert(\" quantity: \\\"\")\n", - " + suffix\n", - " + pynutil.insert(\"\\\"\")\n", - " )\n", - " # Union with decimal to permit either a cardinal or decimal representation.\n", - " res |= decimal + delete_extra_space + pynutil.insert(\" quantity: \\\"\") + suffix + pynutil.insert(\"\\\"\")\n", - " return res" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "uT4LMo8ADBAq" - }, - "source": [ - "We can now insert this into our Classifier, producing the following:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "d2KrCuyGDLwh" - }, - "outputs": [], - "source": [ - "class DecimalFst(GraphFst):\n", - " def __init__(self, cardinal: GraphFst):\n", - " super().__init__(name=\"decimal\", kind=\"classify\")\n", - " quantities_cardinal = cardinal.graph_hundreds_component_at_least_one_none_zero_digit\n", - " cardinal = cardinal.graph_no_exception\n", - " delete_virgule = pynutil.delete(\"virgule\")\n", - "\n", - " graph_integer = pynutil.insert(\"integer_part: \\\"\") + cardinal + pynutil.insert(\"\\\" \") + delete_space\n", - " graph_integer_or_none = graph_integer | pynutil.insert(\"integer_part: \\\"0\\\" \", weight=.001) # In cases we don't always have an integer preceding\n", - "\n", - " graph_string_of_cardinals = delete_space + cardinal\n", - " graph_string_of_cardinals = pynini.closure(graph_string_of_cardinals, 1)\n", - " graph_fractional = pynutil.insert(\"fractional_part: \\\"\") + graph_string_of_cardinals + pynutil.insert(\"\\\"\")\n", - "\n", - " graph_decimal_no_sign = graph_integer_or_none + delete_virgule + graph_fractional \n", - "\n", - " graph_negative = pynini.cross(\"moins\", \"negative: \\\"-\\\" \") + delete_space\n", - " graph_negative = pynini.closure(graph_negative, 0, 1)\n", - " graph_decimal = graph_negative + graph_decimal_no_sign\n", - "\n", - " # Union default decimal with version that accepts quantities\n", - " graph_decimal |= graph_negative + get_quantity(\n", - " graph_decimal_no_sign, quantities_cardinal\n", - " )\n", - " final_graph = self.add_tokens(graph_decimal)\n", - " self.fst = final_graph.optimize()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "cD-eKqO6qTyh" - }, - "outputs": [], - "source": [ - "cardinal = CardinalFst()\n", - "decimal = DecimalFst(cardinal).fst\n", - "example = \"trois virgule cent-quatre billion\"\n", - "apply_fst(example, decimal)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "HiSLKF3RfRZA" - }, - "source": [ - "## Verbalizer" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "QnkOV5FlteQA" - }, - "source": [ - "As before, the Verbalizer is responsible for removing the formatting and rendering a given token in conventional form. As the process remains similar to Ordinals and Cardinals (deleting strings in a regular matter) we will instead focus on a unique concern for `DecimalFst`: numeral spacing.\n", - "\n", - "For some writing systems, decimal numbers and other strings are typically not written as a single string, instead using punctuation to group numbers for clarity. For example, in the United States, integer digits greater than a thousand are separated by commas for every three digits:\n", - "- `12345.678` -> `12,345.678`\n", - "\n", - "A similar rule occurs in French, save it employs spaces on each side of the decimal marker:\n", - "- `12345,6789` -> `12 345,678 9`" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "2h4WQZ1a4Cpc" - }, - "source": [ - "While simple enough, this rule poses a slight complication: it works from the left and right of the decimal separator, whereas WFSTs process linearly from the beginning (or end) of strings. As such we will need to break the formatting rule into two components: one for the integer component and one for the decimal component." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "ViOFNdZw4-qu" - }, - "source": [ - "Starting with the integer component, we need our subcomponent to recognize every three digits and insert a space before. We can achieve this with some `graph_utils` helper objects - `NEMO_DIGIT` and `NEMO_NON_BREAKING_SPACE`, which accept all digits and non-breaking spaces, respectively. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "Z36be2Vo5VbR" - }, - "outputs": [], - "source": [ - "every_three_digits = NEMO_DIGIT ** 3 # accepts a string of three digits\n", - "space_every_three_integer = pynini.closure(NEMO_NON_BREAKING_SPACE + every_three_digits) # inserts space before every three digits." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "RSB2gGH-5vwi" - }, - "source": [ - "However, we cannot let the component insert spaces when there are *only* three digits (e.g. `100`.) As such, we need to make sure the insertion only begins starting from the beginning of a string (e.g. when there is a string between one and three digits.)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "wfWp3ghH6mDQ" - }, - "outputs": [], - "source": [ - "space_every_three_integer = pynini.closure(NEMO_DIGIT, 1, 3) + space_every_three_integer" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "NJrQYSfA6vyu" - }, - "source": [ - "For the case of the decimal spacing, we simply reverse the logic:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "vBP6ncTp6yXX" - }, - "outputs": [], - "source": [ - "space_every_three_decimal = pynini.closure(NEMO_NON_BREAKING_SPACE + every_three_digits)\n", - "space_every_three_decimal = space_every_three_decimal + pynini.closure(NEMO_DIGIT, 1, 3)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "WRXPN_gk69VV" - }, - "source": [ - "Placed into our Verbalizer, we would see the following:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "h49eztvs7BXH" - }, - "outputs": [], - "source": [ - "class DecimalFst(GraphFst):\n", - " \"\"\"\n", - " Finite state transducer for verbalizing decimal, e.g.\n", - " decimal { negative: \"true\" integer_part: \"12\" fractional_part: \"5006\" quantity: \"billion\" } -> -12.5006 billion\n", - " \"\"\"\n", - "\n", - " def __init__(self):\n", - " super().__init__(name=\"decimal\", kind=\"verbalize\")\n", - "\n", - " # Need parser to group digits by threes\n", - " exactly_three_digits = NEMO_DIGIT ** 3\n", - " at_most_three_digits = pynini.closure(NEMO_DIGIT, 1, 3)\n", - "\n", - " space_every_three_integer = (\n", - " at_most_three_digits + (pynutil.insert(NEMO_NON_BREAKING_SPACE) + exactly_three_digits).closure()\n", - " )\n", - " space_every_three_decimal = (\n", - " pynini.accep(\",\")\n", - " + (exactly_three_digits + pynutil.insert(NEMO_NON_BREAKING_SPACE)).closure()\n", - " + at_most_three_digits\n", - " )\n", - " group_by_threes = space_every_three_integer | space_every_three_decimal\n", - " self.group_by_threes = group_by_threes\n", - "\n", - " optional_sign = pynini.closure(pynini.cross(\"negative: \\\"true\\\"\", \"-\") + delete_space, 0, 1)\n", - " integer = (\n", - " pynutil.delete(\"integer_part:\")\n", - " + delete_space\n", - " + pynutil.delete(\"\\\"\")\n", - " + pynini.closure(NEMO_NOT_QUOTE, 1)\n", - " + pynutil.delete(\"\\\"\")\n", - " )\n", - " integer = integer @ group_by_threes\n", - " optional_integer = pynini.closure(integer + delete_space, 0, 1)\n", - " fractional = (\n", - " pynutil.insert(\",\")\n", - " + pynutil.delete(\"fractional_part:\")\n", - " + delete_space\n", - " + pynutil.delete(\"\\\"\")\n", - " + pynini.closure(NEMO_NOT_QUOTE, 1)\n", - " + pynutil.delete(\"\\\"\")\n", - " )\n", - " fractional = fractional @ group_by_threes\n", - " optional_fractional = pynini.closure(fractional + delete_space, 0, 1)\n", - " quantity = (\n", - " pynutil.delete(\"quantity:\")\n", - " + delete_space\n", - " + pynutil.delete(\"\\\"\")\n", - " + pynini.closure(NEMO_NOT_QUOTE, 1)\n", - " + pynutil.delete(\"\\\"\")\n", - " )\n", - " optional_quantity = pynini.closure(pynutil.insert(\" \") + quantity + delete_space, 0, 1)\n", - " graph = (optional_integer + optional_fractional + optional_quantity).optimize()\n", - " self.numbers = graph # Saving just the part of the graph used for numbers\n", - " graph = optional_sign + graph\n", - " delete_tokens = self.delete_tokens(graph)\n", - " self.fst = delete_tokens.optimize()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Trying out some examples:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fst = DecimalFst().fst\n", - "\n", - "example1 = 'decimal { integer_part: \"3\" fractional_part: \"10453\" quantity: \"billion\" }'\n", - "example2 = 'decimal { integer_part: \"22323\" fractional_part: \"104553\" }'\n", - "\n", - "apply_fst(example1, fst)\n", - "apply_fst(example2, fst)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "CZbshZCW8clI" - }, - "source": [ - "# Money WFST " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "xuiv8HMz7yjm" - }, - "source": [ - "Now that we've handled some of the foundational classes, it's time to see how they build up to permit more concrete ones. Let's see how the previous WFSTs assist in building a WFST for normalizing currency: the `MoneyFst`. " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "wTU2c7MtUpqF" - }, - "source": [ - "## Grammar" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "qqyRm8Ru8TDf" - }, - "source": [ - "While the exact phrasing will vary, a valid string for currency will possess the following qualities:\n", - "- A major and/or minor denomination of currency\n", - "- A numeric quantity of the denomination \n", - "\n", - "As our `CardinalFst` and `OrdinalFst` already allow us to normalize the quantity, the only issue for `MoneyFst` is to graph the amounts and build a vocabulary to recognize the denominations.\n", - "\n", - "For French, we will use the following examples to build upon:\n", - "- \"une euros\" -> `1 €`\n", - "- \"deux euros\" -> `2 €` \n", - "- \"deux euros cinq\" -> `2,5 €` \n", - "- \"cinq centimes\" -> `0,5 €`\n", - "- \"deux billions de euros\" -> `2 billions de euros`" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "FMqUir9n9_cA" - }, - "source": [ - "These suggest the following requirements of our grammar:\n", - "- There must be a mapping between \"euro\" and \"centime\" and `€` in our vocabulary\n", - "- This mapping must allow both singular and plural forms\n", - "- The currency denomination is phrased between major and minor denominations (\"une euro cinq\" and not \"une cinq euro\")\n", - "- Large quantities of currency are left 'as is' instead of normalized\n", - "\n", - "We may deal with the vocabulary in the typical fashion:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "XN9nbNhB-vEV" - }, - "outputs": [], - "source": [ - "major_currency = pynini.string_map([(\"euro\", \"€\")])\n", - "minor_currency = pynini.string_map([(\"centime\", \"€\")])\n", - "\n", - "graph_plural = pynutil.delete(\"s\").ques\n", - "\n", - "major_currency += graph_plural\n", - "minor_currency += graph_plural" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "3aHrm1qPAc-f" - }, - "source": [ - "Moving to the numbers, note that we need to append a leading zero to the value of fractional currency amounts (\"five cents\" -> `$0.05`). We bring back the subgraph from `CardinalFst` that maps tokens to numbers without tokenization to assist with this:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "jwi-yQW1AjvG" - }, - "outputs": [], - "source": [ - "from nemo_text_processing.inverse_text_normalization.fr.taggers import cardinal\n", - "\n", - "cardinal_graph = cardinal.CardinalFst()\n", - "graph_cardinal = cardinal_graph.graph_no_exception # graphs cardinals w/o tokenization\n", - "\n", - "add_leading_zero_to_double_digit = (NEMO_DIGIT + NEMO_DIGIT) | (pynutil.insert(\"0\") + NEMO_DIGIT)\n", - "graph_fractional_values = graph_cardinal @ add_leading_zero_to_double_digit" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, let us consider how to manage arge quantities of currency. In our example (\"deux billions de euros\" -> `2 billions de euros`) we see that its behavior mirrors that of our `get_quantity` portion of `DecimalFst`. As such, it would be useful if there was a subcomponent of that graph that we could use in here. Like in the case of `CardinalFst`, let us go back and create a subgraph for later use. Since all our quantities are positive, this would be best accomplished right before incorporating the `negative` property, creating a `self.final_graph_wo_negative`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "class DecimalFst(GraphFst):\n", - " def __init__(self, cardinal: GraphFst):\n", - " super().__init__(name=\"decimal\", kind=\"classify\")\n", - " quantities_cardinal = cardinal.graph_hundreds_component_at_least_one_none_zero_digit\n", - " cardinal = cardinal.graph_no_exception\n", - " delete_virgule = pynutil.delete(\"virgule\")\n", - "\n", - " graph_integer = pynutil.insert(\"integer_part: \\\"\") + cardinal + pynutil.insert(\"\\\" \") + delete_space\n", - " graph_integer_or_none = graph_integer | pynutil.insert(\"integer_part: \\\"0\\\" \", weight=.001) # In cases we don't always have an integer preceding\n", - "\n", - " graph_string_of_cardinals = delete_space + cardinal\n", - " graph_string_of_cardinals = pynini.closure(graph_string_of_cardinals, 1)\n", - " graph_fractional = pynutil.insert(\"fractional_part: \\\"\") + graph_string_of_cardinals + pynutil.insert(\"\\\"\")\n", - "\n", - " graph_decimal_no_sign = graph_integer_or_none + delete_virgule + graph_fractional \n", - "\n", - " ### NEW GRAPH HERE\n", - " self.final_graph_wo_negative = graph_decimal_no_sign | get_quantity(\n", - " final_graph_wo_sign, cardinal.graph_hundreds_component_at_least_one_none_zero_digit\n", - " )\n", - " \n", - " graph_negative = pynini.cross(\"moins\", \"negative: \\\"-\\\" \") + delete_space\n", - " graph_negative = pynini.closure(graph_negative, 0, 1)\n", - " graph_decimal = graph_negative + graph_decimal_no_sign\n", - "\n", - " # Union default decimal with version that accepts quantities\n", - " graph_decimal |= graph_negative + get_quantity(\n", - " graph_decimal_no_sign, quantities_cardinal\n", - " )\n", - " final_graph = self.add_tokens(graph_decimal)\n", - " self.fst = final_graph.optimize()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Allowing us to change our grammar to:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nemo_text_processing.inverse_text_normalization.fr.taggers import cardinal, decimal\n", - "\n", - "cardinal_graph = cardinal.CardinalFst()\n", - "decimal_graph = decimal.DecimalFst(cardinal_graph)\n", - "\n", - "graph_cardinal = cardinal_graph.graph_no_exception # graphs cardinals w/o tokenization\n", - "graph_decimal = decimal_graph.final_graph_wo_negative # graphs positive decimals w/o tokenization\n", - "\n", - "add_leading_zero_to_double_digit = (NEMO_DIGIT + NEMO_DIGIT) | (pynutil.insert(\"0\") + NEMO_DIGIT)\n", - "graph_fractional_values = graph_cardinal @ add_leading_zero_to_double_digit" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "L1RHoW-TLzIz" - }, - "source": [ - "Note that by doing this, we're also incorporating the formatting from the `decimal` class up to this point. Since these overlap with the `money` class (see next section), we have saved ourselves some work. \n", - "\n", - "Since we already made `graph_quantity` part of our `DecimalFst`, we can avoid dealing with large quantities now. However, this does mean we still need a way to leave currencies 'as is' without normalization. We can do this by using the `project` method, which will create a WFST that excepts either all valid inputs or all valid outputs of another WFST (depending on argument)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "7l_TLtJkMluU" - }, - "outputs": [], - "source": [ - "major_currency_no_normalize = major_currency.project(\"input\")\n", - "apply_fst(\"euro\", major_currency_no_normalize)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "raBdHc_WXEpG" - }, - "source": [ - "We then append this WFST with a WFST that recognizes prepositions commonly used before large values of currency (\"d'\", \"des\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "CEuxiVgDXRBf" - }, - "outputs": [], - "source": [ - "graph_preposition = pynini.union(\"des \", \"d'\") # Used for large amounts (billions de euros)\n", - "major_currency_no_normalize = pynini.closure(graph_preposition, 0, 1) + major_currency.project(\"input\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "FlXmf8Fq_Rm1" - }, - "source": [ - "## Classifier" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "T5BBuQRzLuXS" - }, - "source": [ - "For the Money semiotic class, we have available the following properties for tokenization:\n", - "- `integer_part`\n", - "- `fractional_part` \n", - "- `currency`\n", - "\n", - "Laying the initial groundwork seems simple enough. We first instantiate our `MoneyFst` classifier with our initial grammars:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "EZaCeHcFWVP3" - }, - "outputs": [], - "source": [ - "class MoneyFst(GraphFst):\n", - " def __init__(self, cardinal: GraphFst, decimal: GraphFst):\n", - " super().__init__(name=\"money\", kind=\"classify\")\n", - " major_currency = pynini.string_map([(\"euro\", \"€\")])\n", - " minor_currency = pynini.string_map([(\"centime\", \"€\")])\n", - "\n", - " graph_plural = pynutil.delete(\"s\").ques\n", - "\n", - " major_currency += graph_plural\n", - " minor_currency += graph_plural\n", - "\n", - " major_currency_no_normalize = major_currency.project(\"input\")\n", - " graph_preposition = pynini.union(\"des \", \"d'\") # Used for large amounts (billions de euros)\n", - " major_currency_no_normalize = graph_preposition + major_currency.project(\"input\")\n", - "\n", - " graph_cardinal = cardinal.graph_no_exception\n", - " graph_decimal = decimal.final_graph_wo_negative\n", - "\n", - " add_leading_zero_to_double_digit = (NEMO_DIGIT + NEMO_DIGIT) | (pynutil.insert(\"0\") + NEMO_DIGIT)\n", - " graph_fractional_values = graph_cardinal @ add_leading_zero_to_double_digit" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "_bpkXroLWaBo" - }, - "source": [ - "Let us now manage the `currency` property. We have the following scenarios to consider:\n", - "- Major denomination only\n", - "- Minor denomination only\n", - "- Major denomination and implicit minor denomination (\"cinq euro trois\")\n", - "- Major denomination and explicit minor denomination (\"cinq euros et trois centimes\")\n", - "- Large quantities of euros (\"cinq billion des euros\")\n", - "\n", - "Note how across cases the use of `graph_cardinal` and `graph_decimal` will be applied differently. Further, we may have varying orders in which tags are assigned proper values. For instance, if we have only minor denomination we would assign `fractional_part` before `currency`. Meanwhile, major denomination and implicit minor denomination would be the order of `integer_part`, `currency`, `fractional_part`. While we could try and figure out a way to preserve order, recall that the use of permutations in NeMo ITN makes that unnecessary: we can assume the desired order of tags reach our Verbalizer without make overt efforts in our Classifier! \n", - "\n", - "For example, let's say we need to process \"five dollars\" as `$5.00`. Processed linearly, we could get a token sequence along the lines of: `{ integer_part: \"5\" currency: \"$\" }`. If we passed this token array straight to a Verbalizer, we would need to configure a graph that effectively reverses the order so we could parse the `currency` field prior to the `integer_part` field, perhaps something along the lines of: \n", - "\n", - "`pynutil.insert(\"$\") + delete_space + pynutil.delete('integer_part: \\\"') +.... + pynutil.delete('currency: \"$\"')`\n", - "\n", - "But since NeMo creates permutations of our Classifier outputs, this is unnecessary. We can simply assume whatever would be the most convenient order for us (e.g. `{ currency: \"$\" integer_part: \"5\" }`) and build our Verbalizer around that:\n", - "\n", - "`pynutil.delete('currency: \\\"') + NEMO_SIGMA + pynutil.delete('\\\" integer_part: \\\"') + NEMO_DIGIT +...`\n", - "\n", - "Along with helping to keep our script simpler (we can focus simply on tokenization and not worry about what input order our Verbalizers will accept), this also allows us to overcome structural constraints of WFSTs, namely that they are [limited in reordering text strings](https://en.wikipedia.org/wiki/Pushdown_automaton)." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "fMZ13D2Dh9ZF" - }, - "source": [ - "Keeping this in mind, let's begin mapping the proper tags. Since they're relatively simple, we can start with only major and minor denominations:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "EtwWLp7VbbjM" - }, - "outputs": [], - "source": [ - "graph_integer_component = pynutil.insert(\"integer_part: \\\"\") + graph_cardinal + pynutil.insert(\"\\\"\")\n", - "graph_fractional_component = pynutil.insert(\"fractional_part: \\\"\") + graph_fractional_values + pynutil.insert(\"\\\"\")\n", - "\n", - "graph_major_currency = pynutil.insert(\" currency: \\\"\") + major_currency + pynutil.insert(\"\\\"\")\n", - "graph_minor_currency = pynutil.insert(\" currency: \\\"\") + minor_currency + pynutil.insert(\"\\\"\")\n", - "\n", - "graph_only_major_money = graph_integer_component + delete_space + graph_major_currency\n", - "graph_only_minor_money = graph_fractional_component + delete_space + graph_minor_currency " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "XTmxrK4DmS39" - }, - "source": [ - "Now we may append the case of an implicit `fractional_part` to `graph_only_major_money`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "Zvzn3pQinkT0" - }, - "outputs": [], - "source": [ - "implicit_fractional_part = delete_space + pynutil.insert(\"fractional_part: \\\"\") + graph_fractional_values + pynutil.insert(\"\\\"\") \n", - "implicit_fractional_part = pynini.closure(implicit_fractional_part, 0, 1) " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "tKFZkCVmn1OX" - }, - "source": [ - "And the explicit fractional portion:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "d_h0pTlMn3jz" - }, - "outputs": [], - "source": [ - "delete_et = pynutil.delete(\"et \") # Sometimes prefaces the minor currency\n", - "delete_et = pynini.closure(delete_et, 0 , 1)\n", - "\n", - "delete_minor = pynutil.delete(minor_currency.project(\"input\")) # to remove the minor currency\n", - "\n", - "explicit_fractional_part = pynutil.insert(\"fractional_part: \\\"\") + graph_fractional_values + pynutil.insert(\"\\\"\") \n", - "explicit_fractional_part = delete_space + delete_et + explicit_fractional_part + delete_space + delete_minor\n", - "explicit_fractional_part = pynini.closure(explicit_fractional_part, 0, 1)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "rvnpAudgo-o3" - }, - "source": [ - "We join them together:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "qYzlIRWTpD8e" - }, - "outputs": [], - "source": [ - "graph_major_money = graph_only_major_money + (implicit_fractional_part | explicit_fractional_part)\n", - "graph_standard_money = graph_major_money | graph_only_minor_money" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "TzeaKXVzpYs8" - }, - "source": [ - "Finishing with the case the large quantities of money, we need to use `graph_decimal` so we can exploit its ability to map quantities. Note that since we are using a pre-existing WFST, we can ignore inserting the tags ourselves, since this is already done by the Decimal WFST. As long as we remember to process this aspect with our Verbalizer, we can spare ourselves the extra step." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "LnqX9mGFpmJm" - }, - "outputs": [], - "source": [ - "graph_large_money = pynutil.insert(\" currency: \\\"\") + major_currency_no_normalize + pynutil.insert(\"\\\"\")\n", - "graph_large_money = graph_decimal + delete_space + graph_large_money" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "24TUZnJKqgPA" - }, - "source": [ - "Alltogether, this would give the following Classifier:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "B7-muCO2qizg" - }, - "outputs": [], - "source": [ - "class MoneyFst(GraphFst):\n", - " def __init__(self, cardinal: GraphFst, decimal: GraphFst):\n", - " super().__init__(name=\"money\", kind=\"classify\")\n", - " major_currency = pynini.string_map([(\"euro\", \"€\")])\n", - " minor_currency = pynini.string_map([(\"centime\", \"€\")])\n", - "\n", - " graph_plural = pynutil.delete(\"s\").ques\n", - "\n", - " major_currency += graph_plural\n", - " minor_currency += graph_plural\n", - "\n", - " major_currency_no_normalize = major_currency.project(\"input\")\n", - " graph_preposition = pynini.union(\"des \", \"d'\") # Used for large amounts (billions de euros)\n", - " major_currency_no_normalize = graph_preposition + major_currency.project(\"input\")\n", - "\n", - " graph_cardinal = cardinal.graph_no_exception\n", - " graph_decimal = decimal.final_graph_wo_negative\n", - "\n", - " add_leading_zero_to_double_digit = (NEMO_DIGIT + NEMO_DIGIT) | (pynutil.insert(\"0\") + NEMO_DIGIT)\n", - " graph_fractional_values = graph_cardinal @ add_leading_zero_to_double_digit\n", - "\n", - " graph_integer_component = pynutil.insert(\"integer_part: \\\"\") + graph_cardinal + pynutil.insert(\"\\\"\")\n", - " graph_fractional_component = pynutil.insert(\"fractional_part: \\\"\") + graph_fractional_values + pynutil.insert(\"\\\"\")\n", - "\n", - " graph_major_currency = pynutil.insert(\" currency: \\\"\") + major_currency + pynutil.insert(\"\\\"\")\n", - " graph_minor_currency = pynutil.insert(\" currency: \\\"\") + minor_currency + pynutil.insert(\"\\\"\")\n", - "\n", - " graph_only_major_money = graph_integer_component + delete_space + graph_major_currency\n", - " graph_only_minor_money = graph_fractional_component + delete_space + graph_minor_currency \n", - "\n", - " implicit_fractional_part = delete_space + pynutil.insert(\"fractional_part: \\\"\") + graph_fractional_values + pynutil.insert(\"\\\"\") \n", - " implicit_fractional_part = pynini.closure(implicit_fractional_part, 0, 1) \n", - "\n", - "\n", - " delete_et = pynutil.delete(\"et \") # Sometimes prefaces the minor currency\n", - " delete_et = pynini.closure(delete_et, 0 , 1)\n", - "\n", - " delete_minor = pynutil.delete(minor_currency.project(\"input\")) # to remove the minor currency\n", - "\n", - " explicit_fractional_part = pynutil.insert(\"fractional_part: \\\"\") + graph_fractional_values + pynutil.insert(\"\\\"\") \n", - " explicit_fractional_part = delete_space + delete_et + explicit_fractional_part + delete_space + delete_minor\n", - " explicit_fractional_part = pynini.closure(explicit_fractional_part, 0, 1)\n", - "\n", - " graph_major_money = graph_only_major_money + (implicit_fractional_part | explicit_fractional_part)\n", - "\n", - " graph_large_money = pynutil.insert(\" currency: \\\"\") + major_currency_no_normalize + pynutil.insert(\"\\\"\")\n", - " graph_large_money = graph_decimal + delete_space + graph_large_money\n", - "\n", - " final_graph = graph_large_money | graph_major_money | graph_only_minor_money\n", - "\n", - " final_graph = self.add_tokens(final_graph)\n", - " self.fst = final_graph.optimize()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's see the results:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nemo_text_processing.inverse_text_normalization.fr.taggers import decimal, cardinal\n", - "\n", - "cardFst = cardinal.CardinalFst()\n", - "decFst = decimal.DecimalFst(cardFst)\n", - "\n", - "moneyFst = MoneyFst(cardFst, decFst).fst\n", - "\n", - "example = \"douze virgule cinq billions d'euros\"\n", - "\n", - "apply_fst(example, moneyFst)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "gxdcyuLmAZZa" - }, - "source": [ - "## Verbalizer" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "ZZFDWNwY6sOG" - }, - "source": [ - "By this point, the creation of the Verbalizer should be rather straight-forward - delete the expected tokens and perform any specific formatting that was not caught by the Classifier. \n", - "\n", - "In fact, it is so straight-forward that much of the work does not even need to be explicitly managed by the Verbalizer. As mentioned previously, two of the properties we inserted in our Classifier where already referenced in our `DecimalFst` - `integer_part` and `fractional_part`. We even went so far to directly call a component of `DecimalFst` in our Classifier. As such, outside of the `currency` property - there is little in our Money token that is different from a standard Decimal token. Indeed, even the normalized forms are similar (`200,5` vs. `200,5 €`.) " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "T7sgH0t79tmU" - }, - "source": [ - "Given these similarities, it seems that we can save ourselves some work and simply use the Decimal Verbalizer to manage much of the normalization. Let's look at the basic format of our `MoneyFst` verbalizer, writing it so it accepts a `DecimalFst` as input:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "BEu8nITP9mSG" - }, - "outputs": [], - "source": [ - "class MoneyFst(GraphFst):\n", - " def __init__(self, decimal: GraphFst):\n", - " super().__init__(name=\"money\", kind=\"verbalize\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "JYVLou5N-Dk8" - }, - "source": [ - "We manage the issue of deleting the `currency` property:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "LO35tJ7G-H6N" - }, - "outputs": [], - "source": [ - "class MoneyFst(GraphFst):\n", - " def __init__(self, decimal: GraphFst):\n", - " super().__init__(name=\"money\", kind=\"verbalize\")\n", - " unit = (\n", - " pynutil.delete(\"currency:\")\n", - " + delete_extra_space\n", - " + pynutil.delete(\"\\\"\")\n", - " + pynini.closure(NEMO_NOT_QUOTE, 1)\n", - " + pynutil.delete(\"\\\"\")\n", - " )" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "bDS8XSII-Dpd" - }, - "source": [ - "Now consider, we need to normalize an integer component, a fractional component, and a decimal to separate them. Since NeMo will automatically permutate all tags, we can assume whatever order we want. As such, we can assume we get the exact order that is accepted by our `DecimalFst`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "VtGfpjVA-r3u" - }, - "outputs": [], - "source": [ - " def __init__(self, decimal: GraphFst):\n", - " super().__init__(name=\"money\", kind=\"verbalize\")\n", - " unit = (\n", - " pynutil.delete(\"currency:\")\n", - " + delete_extra_space\n", - " + pynutil.delete(\"\\\"\")\n", - " + pynini.closure(NEMO_NOT_QUOTE, 1)\n", - " + pynutil.delete(\"\\\"\")\n", - " )\n", - " graph = decimal.numbers + delete_space + unit\n", - " delete_tokens = self.delete_tokens(graph)\n", - " self.fst = delete_tokens.optimize()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "ZefxZLIU-uRU" - }, - "source": [ - "It is as simple and compact as appending the `unit` component to the preexisting `decimal.numbers`. \n", - "\n", - "This feature is worth keeping in mind as you build up to more concrete classes: the combination of guaranteed tag permutations and prebuilt Verbalizers make the addition of semiotic classes progressively simpler despite the building complexity of your entire grammar." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "WydC7Cn28l5Y" - }, - "source": [ - "# Time WFST " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "VelunbumCJJe" - }, - "source": [ - "Our next composite graph will be for the Time WFST. Here, you may see more variation between your language and our example than with our previous classes. This is for a number of reasons, among them being that while there may be some standard cross linguistic patterns regarding time (e.g. `quantity_of_hours + quantity_of_minutes`), the use of various equivalent phrases can make an exhaustive grammar incredibly specific (e.g. consider managing \"twelve fifteen\", \"twelve and a quarter\", \"quarter past twelve\", \"quarter after twelve\", and \"forty five until one\" all together). You may find yourself drawing upon WFSTs that accommodate Cardinals, Fractions, and some basic subtraction.\n", - "\n", - "As such, we are going to focus on those aspects of the Time WFST that are necessary for a functional normalization of time related phrases, saving a more exhaustive grammar for your own specific languages and use cases." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "8wqb28wzATOR" - }, - "source": [ - "## Grammar" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "AVntDM3AEz0v" - }, - "source": [ - "For our Time WFST, we will focus on the following aspects:\n", - "- Use of 24 or 12 hour base\n", - "- Use of fraction terminology (e.g. \"quarter\" = `15`)\n", - "- Accommodation of key-words (\"noon\", \"midnight\")\n", - "- Counting backwards from the hour (\"ten to five\", \"five to three\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "seU9hTbgFgu7" - }, - "source": [ - "We'll start with the basic system.\n", - "\n", - "For French, time operates on a twenty-four hour system, with the zeroth hour being midnight. Time is given in the following format:\n", - "\n", - "`cardinal + heure(s) + (cardinal)` \n", - "\n", - "This is normalized as:\n", - "\n", - "`cardinal h (cardinal)`\n", - "\n", - "For instance, for `3:03`, we would have:\n", - "- input: \"trois heures trois\"\n", - "- output: `3 h 03`\n", - "\n", - "As such, our grammar needs to utilize a Cardinal WFST and have a means to accept \"heures\" from the input. Taking care of the latter case is simple enough:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "HTSVxf4fI_ND" - }, - "outputs": [], - "source": [ - "graph_heures = pynini.accep(\"heure\") + pynini.accep(\"s\").ques\n", - "graph_heures = pynutil.delete(graph_heures)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "6LW7pXaXJSZa" - }, - "source": [ - "For the cardinals, we could pass an instance of `CardinalFST` to our graph. But do we really need that level of coverage? We only really need to cover the numbers 0 - 60, which we could simply write a new WFST for. Further, it may be beneficial to allow our graph to separate possible ambiguity. While we will not cover it in our tutorial, you may in the future find it necessary to build a WFST for Measurements, of which quantities of time may play a part. Would it not be helpful for you WFST to know that \"thirty hours\" could only ever be a measurement instead of a possible time of day?\n", - "\n", - "Given the little amount of effort necessary and the quick benefit, we choose to make our hours and minutes explicit in the Time WFST." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "R4aa06ZPLKIR" - }, - "outputs": [], - "source": [ - "hours = pynini.string_map([\n", - " (\"zéro\",\"0\"),\n", - " (\"une\",\"1\"),\n", - " (\"deux\",\"2\"),\n", - " (\"trois\",\"3\"),\n", - " (\"quatre\",\"4\"),\n", - " (\"cinq\",\"5\"),\n", - " (\"six\",\"6\"),\n", - " (\"sept\",\"7\"),\n", - " (\"huit\",\"8\"),\n", - " (\"neuf\",\"9\"),\n", - " (\"dix\",\"10\"),\n", - " (\"onze\",\"11\"),\n", - " (\"douze\",\"12\"),\n", - " (\"treize\",\"13\"),\n", - " (\"quatorze\",\"14\"),\n", - " (\"quinze\",\"15\"),\n", - " (\"seize\",\"16\"),\n", - " (\"dix-sept\",\"17\"),\n", - " (\"dix-huit\",\"18\"),\n", - " (\"dix-neuf\",\"19\"),\n", - " (\"vingt\",\"20\"),\n", - " (\"vingt-et-une\",\"21\"),\n", - " (\"vingt et une\",\"21\"),\n", - " (\"vingt-deux\",\"22\"),\n", - " (\"vingt-trois\",\"23\"),\n", - " (\"vingt-quatre\",\"24\"),\n", - "])\n", - "minutes = pynini.string_map([\n", - " (\"une\", \"01\"),\n", - " (\"deux\", \"02\"),\n", - " (\"trois\", \"03\"),\n", - " (\"quatre\", \"04\"),\n", - " (\"cinq\", \"05\"),\n", - " (\"six\", \"06\"),\n", - " (\"sept\", \"07\"),\n", - " (\"huit\", \"08\"),\n", - " (\"neuf\", \"09\"),\n", - " (\"dix\", \"10\"),\n", - " (\"onze\", \"11\"),\n", - " (\"douze\", \"12\"),\n", - " (\"treize\", \"13\"),\n", - " (\"quatorze\", \"14\"),\n", - " (\"quinze\", \"15\"),\n", - " (\"seize\", \"16\"),\n", - " (\"dix-sept\", \"17\"),\n", - " (\"dix-huit\", \"18\"),\n", - " (\"dix-neuf\", \"19\"),\n", - " (\"vingt\", \"20\"),\n", - " (\"vingt-et-une\", \"21\"),\n", - " (\"vingt et une\", \"21\"),\n", - " (\"vingt-deux\", \"22\"),\n", - " (\"vingt-trois\", \"23\"),\n", - " (\"vingt-quatre\", \"27\"),\n", - " (\"vingt-cinq\", \"25\"),\n", - " (\"vingt-six\", \"26\"),\n", - " (\"vingt-sept\", \"27\"),\n", - " (\"vingt-huit\", \"28\"),\n", - " (\"vingt-neuf\", \"29\"),\n", - " (\"trente\", \"30\"),\n", - " (\"trente-et-une\", \"31\"),\n", - " (\"trente et une\", \"31\"),\n", - " (\"trente-deux\", \"32\"),\n", - " (\"trente-trois\", \"33\"),\n", - " (\"trente-quatre\", \"34\"),\n", - " (\"trente-cinq\", \"35\"),\n", - " (\"trente-six\", \"36\"),\n", - " (\"trente-sept\", \"37\"),\n", - " (\"trente-huit\", \"38\"),\n", - " (\"trente-neuf\", \"39\"),\n", - " (\"quarante\", \"40\"),\n", - " (\"quarante-et-une\", \"41\"),\n", - " (\"quarante et une\", \"41\"),\n", - " (\"quarante-deux\", \"42\"),\n", - " (\"quarante-trois\", \"43\"),\n", - " (\"quarante-quatre\", \"44\"),\n", - " (\"quarante-cinq\", \"45\"),\n", - " (\"quarante-six\", \"46\"),\n", - " (\"quarante-sept\", \"47\"),\n", - " (\"quarante-huit\", \"48\"),\n", - " (\"quarante-neuf\", \"49\"),\n", - " (\"cinquante\", \"50\"),\n", - " (\"cinquante-et-une\", \"51\"),\n", - " (\"cinquante et une\", \"51\"),\n", - " (\"cinquante-deux\", \"52\"),\n", - " (\"cinquante-trois\", \"53\"),\n", - " (\"cinquante-quatre\", \"54\"),\n", - " (\"cinquante-cinq\", \"55\"),\n", - " (\"cinquante-six\", \"56\"),\n", - " (\"cinquante-sept\", \"57\"),\n", - " (\"cinquante-huit\", \"58\"),\n", - " (\"cinquante-neuf\", \"59\"),\n", - "])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "4SmNsNKLM9cC" - }, - "source": [ - "Now that we've managed the basic graph, we can address some of the more niche rules of French timekeeping.\n", - "\n", - "To start, French employs some colloquialisms that will be familiar to English speakers: minutes that are multiples of fifteen are referred to as fractions of a clock. In particular:\n", - "- `5 h 15` -> \"cinq heures **et quart**\"\n", - "- `5 h 30` -> \"cinq heures **et demie**\"\n", - "- `5 h 45` -> \"cinq eures **et trois quarts**\"\n", - "\n", - "We thus need a means of rendering these as their numerical equivalents:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "xHe3nfrpSlrE" - }, - "outputs": [], - "source": [ - "# Mapping 'et demi' and 'et qart'\n", - "graph_et = pynutil.delete(\"et\") + delete_space\n", - "\n", - "graph_demi = pynini.accep(\"demi\")\n", - "graph_demi += pynini.accep(\"e\").ques # people vary on feminine or masculine form\n", - "graph_demi = pynini.cross(graph_demi, \"30\")\n", - "\n", - "graph_quart = pynini.accep('quart')\n", - "graph_quart = pynini.cross(graph_quart, '15')\n", - "graph_trois_quart = pynini.cross(\"trois quarts\", \"45\")\n", - "\n", - "graph_fractions = graph_demi | graph_quart | graph_trois_quart\n", - "graph_fractions = graph_et + graph_fractions" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "HD2wobIQS3fX" - }, - "source": [ - "Also like English, French will use key words to designate a specific timeslot. Noon and midnight are \"midi\" and \"minuit\" respectively." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "ahbkiZFuTN2t" - }, - "outputs": [], - "source": [ - "# Midi and minuit\n", - "graph_midi = pynini.cross(\"midi\", \"12\")\n", - "graph_minuit = pynini.cross(\"minuit\", \"0\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "6OyMoqfZTX1U" - }, - "source": [ - "Now it's time to throw a wrench into things: counting backwards from the hour. How are we to get what is essentially a graph to do the subtraction necessarily for \"ten to twelve\" to become `11:50`?\n", - "\n", - "Easy: we build the subtraction into the graph itself. That is, we map the hours and minutes produced by our graph onto another graph that produces their amount shifted back a value.\n", - "\n", - "Let's take our \"ten to twelve\" example. Normally \"ten\" would map to `10` and \"twelve\" to `12`. But with these new graphs, the detection of the pattern `minute + to + hour` would signal that `10` should now become `50` and `12` become `11`." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "uMWifbm1VQjP" - }, - "source": [ - "Let us do this for our French example. Luckily enough, the indication that a French string is regular: counting backwards from the hour is by use of the pattern `cardinal + heures + moins + minutes`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "c4bV3T1pViCH" - }, - "outputs": [], - "source": [ - "hours_to = pynini.string_map([\n", - " (\"1\",\"0\"),\n", - " (\"2\",\"1\"),\n", - " (\"3\",\"2\"),\n", - " (\"4\",\"3\"),\n", - " (\"5\",\"4\"),\n", - " (\"6\",\"5\"),\n", - " (\"7\",\"6\"),\n", - " (\"8\",\"7\"),\n", - " (\"9\",\"8\"),\n", - " (\"10\",\"9\"),\n", - " (\"11\",\"10\"),\n", - " (\"12\",\"11\"),\n", - " (\"13\",\"12\"),\n", - " (\"14\",\"13\"),\n", - " (\"15\",\"14\"),\n", - " (\"16\",\"15\"),\n", - " (\"17\",\"16\"),\n", - " (\"18\",\"17\"),\n", - " (\"19\",\"18\"),\n", - " (\"20\",\"19\"),\n", - " (\"21\",\"20\"),\n", - " (\"22\",\"21\"),\n", - " (\"23\",\"22\"),\n", - " (\"24\",\"23\"),\n", - " (\"0\",\"23\"),\n", - "])\n", - "minutes_to = pynini.string_map([\n", - " (\"59\", \"01\"),\n", - " (\"58\", \"02\"),\n", - " (\"57\", \"03\"),\n", - " (\"56\", \"04\"),\n", - " (\"55\", \"05\"),\n", - " (\"54\", \"06\"),\n", - " (\"53\", \"07\"),\n", - " (\"52\", \"08\"),\n", - " (\"51\", \"09\"),\n", - " (\"50\", \"10\"),\n", - " (\"49\", \"11\"),\n", - " (\"48\", \"12\"),\n", - " (\"47\", \"13\"),\n", - " (\"46\", \"14\"),\n", - " (\"45\", \"15\"),\n", - " (\"44\", \"16\"),\n", - " (\"43\", \"17\"),\n", - " (\"42\", \"18\"),\n", - " (\"41\", \"19\"),\n", - " (\"40\", \"20\"),\n", - " (\"39\", \"21\"),\n", - " (\"38\", \"22\"),\n", - " (\"37\", \"23\"),\n", - " (\"36\", \"24\"),\n", - " (\"35\", \"25\"),\n", - " (\"34\", \"26\"),\n", - " (\"33\", \"27\"),\n", - " (\"32\", \"28\"),\n", - " (\"31\", \"29\"),\n", - " (\"30\", \"30\"),\n", - " (\"29\", \"31\"),\n", - " (\"28\", \"32\"),\n", - " (\"27\", \"33\"),\n", - " (\"26\", \"34\"),\n", - " (\"25\", \"35\"),\n", - " (\"24\", \"36\"),\n", - " (\"23\", \"37\"),\n", - " (\"22\", \"38\"),\n", - " (\"21\", \"39\"),\n", - " (\"20\", \"40\"),\n", - " (\"19\", \"41\"),\n", - " (\"18\", \"42\"),\n", - " (\"17\", \"43\"),\n", - " (\"16\", \"44\"),\n", - " (\"15\", \"45\"),\n", - " (\"14\", \"46\"),\n", - " (\"13\", \"47\"),\n", - " (\"12\", \"48\"),\n", - " (\"11\", \"49\"),\n", - " (\"10\", \"50\"),\n", - " (\"09\", \"51\"),\n", - " (\"08\", \"52\"),\n", - " (\"07\", \"53\"),\n", - " (\"06\", \"54\"),\n", - " (\"05\", \"55\"),\n", - " (\"04\", \"56\"),\n", - " (\"03\", \"57\"),\n", - " (\"02\", \"58\"),\n", - " (\"01\", \"59\"),\n", - "])\n", - "graph_moins = pynutil.delete(\"moins\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "XOKETkIYZy5M" - }, - "source": [ - "Why graph the digits instead of the tokens themselves? Along with avoiding some minor repetition and making editing more apparent, it allows this subgraph to be ported to other languages - if so desired.\n", - "\n", - "Further, it helps us illustrate a helpful idea within this tutorial: as long as a pattern is regular and/or finite, it is no major issue to accommodate it in our graph, regardless of mathematic or logic system it employs." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "DJbFiD2fAUc5" - }, - "source": [ - "## Classifier" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "cK0SGXntaDkI" - }, - "source": [ - "Once again we place the grammar within the proper child class of `GraphFst`. We also insert the proper tags for the `Time` class, which are:\n", - "- `hours`\n", - "- `minutes`\n", - "- `suffix` (explained within this section)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "9Eq5r-_VbBIg" - }, - "outputs": [], - "source": [ - "graph_hours_component = pynini.union(hours, graph_midi, graph_minuit)\n", - "graph_hours_component = pynutil.insert(\"hours: \\\"\") + graph_hours_component + pynutil.insert(\"\\\"\")\n", - "\n", - "graph_minutes_component = (\n", - " pynutil.insert(\" minutes: \\\"\") + pynini.union(minutes, graph_fractions) + pynutil.insert(\"\\\"\")\n", - ") \n", - "graph_minutes_component = delete_space + graph_minutes_component\n", - "\n", - "graph_time_standard = (graph_hours_component + delete_space + graph_heures \n", - " + pynini.closure(graph_minutes_component, 0, 1))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "2avfS3IacSiC" - }, - "source": [ - "We now setup the alternate graph that allows backwards counting. Note, this is triggered by the occurrence of \"moins\" between the hour and minute component." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "TmpwisOVcn0T" - }, - "outputs": [], - "source": [ - "graph_hours_to_component = hours | graph_midi | graph_minuit\n", - "graph_hours_to_component @= hours_to\n", - "graph_hours_to_component = pynutil.insert(\"hours: \\\"\") + graph_hours_to_component + pynutil.insert(\"\\\"\")\n", - "graph_hours_to_component = graph_hours_to_component + delete_space + graph_heures\n", - "\n", - "graph_minutes_to_component = (minutes | graph_demi | # No 'et' in fractions\n", - " (pynutil.delete(\"le \") + graph_quart) | graph_trois_quart)\n", - "graph_minutes_to_component @= minutes_to\n", - "graph_minutes_to_component = pynutil.insert(\" minutes: \\\"\") + graph_minutes_to_component + pynutil.insert(\"\\\"\")\n", - "\n", - "graph_time_to = graph_hours_to_component + delete_space + graph_moins + delete_space + graph_minutes_to_component" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "FkO4tRRfdQT4" - }, - "source": [ - "We now join it with our main component, allowing us to graph all times:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "0O0vUVizdU8c" - }, - "outputs": [], - "source": [ - "graph_time = graph_time_standard | graph_time_to" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "jbX4JV-LdY3Y" - }, - "source": [ - "Once again we throw a wrench into things with the `suffix` feature. As in the case of Ordinals and Decimals, key-words can play into our Time WFST. For French, this occurs with the words \"du matin\", \"de l'après-midi\", and \"du soir\". (Respectively: \"in the morning\", \"in the afternoon\", and \"in the evening\".) Much like in English, these phrases alter how we write down the time. But instead of indicating `a.m.` or `p.m.`, these indicate *what hour system is used*. For example:\n", - "- \"deux heures du matin\" -> `2 h` = `2:00 a.m.`\n", - "- \"deux heures de l'après-midi\" -> `14 h` = `2:00 p.m.`\n", - "\n", - "Only a twelve hour system is used when these suffixes accompany the time. As such, our Classifier will need to either adjust the times like in the case of counting backwards or must pass the information to the Verbalizer so it can adjust. \n", - "\n", - "Since our Classifier is long enough as is, we will simply store this information in the `suffix` property and allow the Verbalizer to manage." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "OqVa78zRgJw9" - }, - "outputs": [], - "source": [ - "graph_suffix_am = pynini.cross(\"du matin\", \"am\")\n", - "graph_suffix_pm = pynini.string_map([(\"de l'après-midi\", \"pm\"),(\"du soir\", \"pm\")])\n", - "\n", - "graph_suffix = pynini.cross(graph_suffix_am, \"am\") | pynini.cross(graph_suffix_pm, \"pm\")\n", - "\n", - "graph_suffix_component = pynutil.insert(\" suffix: \\\"\") + graph_suffix + pynutil.insert(\"\\\"\")\n", - "graph_suffix_component = delete_space + graph_suffix_component\n", - "graph_suffix_component = pynini.closure(graph_suffix_component, 0, 1)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "-LaJMIjUf1XR" - }, - "source": [ - "And we append to our graph:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "76myCFiggX3E" - }, - "outputs": [], - "source": [ - "class TimeFst(GraphFst):\n", - " def __init__(self):\n", - " super().__init__(name=\"time\", kind=\"classify\")\n", - " \"\"\"grammar omitted for length\n", - " ....\n", - " ....\n", - " ....\n", - " \"\"\"\n", - " graph_hours_component = pynini.union(hours, graph_midi, graph_minuit)\n", - " graph_hours_component = pynutil.insert(\"hours: \\\"\") + graph_hours_component + pynutil.insert(\"\\\"\")\n", - "\n", - " graph_minutes_component = (\n", - " pynutil.insert(\" minutes: \\\"\") + pynini.union(minutes, graph_fractions) + pynutil.insert(\"\\\"\")\n", - " ) \n", - " graph_minutes_component = delete_space + graph_minutes_component\n", - "\n", - " graph_time_standard = (graph_hours_component + delete_space + graph_heures \n", - " + pynini.closure(graph_minutes_component, 0, 1))\n", - "\n", - " graph_hours_to_component = hours | graph_midi | graph_minuit\n", - " graph_hours_to_component @= hours_to\n", - " graph_hours_to_component = pynutil.insert(\"hours: \\\"\") + graph_hours_to_component + pynutil.insert(\"\\\"\")\n", - " graph_hours_to_component = graph_hours_to_component + delete_space + graph_heures\n", - "\n", - " graph_minutes_to_component = (minutes | graph_demi | # No 'et' in fractions\n", - " (pynutil.delete(\"le \") + graph_quart) | graph_trois_quart)\n", - " graph_minutes_to_component @= minutes_to\n", - " graph_minutes_to_component = pynutil.insert(\" minutes: \\\"\") + graph_minutes_to_component + pynutil.insert(\"\\\"\")\n", - "\n", - " graph_time_to = graph_hours_to_component + delete_space + graph_moins + delete_space + graph_minutes_to_component\n", - "\n", - " graph_time_no_suffix = graph_time_standard | graph_time_to\n", - "\n", - " graph_suffix_am = pynini.cross(\"du matin\", \"am\")\n", - " graph_suffix_pm = pynini.string_map([(\"de l'après-midi\", \"pm\"),(\"du soir\", \"pm\")])\n", - "\n", - " graph_suffix = pynini.cross(graph_suffix_am, \"am\") | pynini.cross(graph_suffix_pm, \"pm\")\n", - "\n", - " graph_suffix_component = pynutil.insert(\" suffix: \\\"\") + graph_suffix + pynutil.insert(\"\\\"\")\n", - " graph_suffix_component = delete_space + graph_suffix_component\n", - " graph_suffix_component = pynini.closure(graph_suffix_component, 0, 1)\n", - " \n", - " final_graph = graph_time_no_suffix + graph_suffix_component\n", - "\n", - " final_graph = self.add_tokens(final_graph)\n", - "\n", - " self.fst = final_graph.optimize()\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's see how we did:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "time = TimeFst().fst\n", - "example = \"quatre heures moins cinq\"\n", - "apply_fst(example, time)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "lPlJ1qyeAWOL" - }, - "source": [ - "## Verbalizer" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "CrO-xtJ87PEl" - }, - "source": [ - "The initial part of the Verbalizer should appear familiar. We delete the property tags `hours` and `minutes`, making sure they preserve the actual values for formatting." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "fCzZKR7ek0Mz" - }, - "outputs": [], - "source": [ - "hour = (\n", - " pynutil.delete(\"hours:\")\n", - " + delete_space\n", - " + pynutil.delete(\"\\\"\")\n", - " + pynini.closure(NEMO_DIGIT, 1, 2)\n", - " + pynutil.delete(\"\\\"\")\n", - ")\n", - "minute = (\n", - " pynutil.delete(\"minutes:\")\n", - " + delete_extra_space\n", - " + pynutil.delete(\"\\\"\")\n", - " + pynini.closure(NEMO_DIGIT, 1, 2)\n", - " + pynutil.delete(\"\\\"\")\n", - ")\n", - "graph = hour + delete_extra_space + pynutil.insert(\"h\") + minute.ques" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "WnVV9GUKk-b7" - }, - "source": [ - "We then deal with the case of `suffix`. We first note that if the suffix is for a morning time (before noon), then there is no further conversion that is needed. We may simply delete the property and its value." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "haOEiSbglc6s" - }, - "outputs": [], - "source": [ - "day_suffixes = pynutil.delete(\"suffix: \\\"am\\\"\")\n", - "\n", - "graph = hours + delete_extra_space + pynutil.insert(\"h\") + minute.ques + delete_space + day_suffixes.ques" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "wL0FNg6Xlhb-" - }, - "source": [ - "Meanwhile, the post-noon suffixes would require us shifting the hours value by twelve. Much like in the case of counting backwards from the hour, we can simply create a WFST to do this addition work for us." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "YLrabUNplwG7" - }, - "outputs": [], - "source": [ - "hour_to_night = pynini.string_map([\n", - " (\"1\", \"13\"),\n", - " (\"2\", \"14\"),\n", - " (\"3\", \"15\"),\n", - " (\"4\", \"16\"),\n", - " (\"5\", \"17\"),\n", - " (\"6\", \"18\"),\n", - " (\"7\", \"19\"),\n", - " (\"8\", \"20\"),\n", - " (\"9\", \"21\"),\n", - " (\"10\", \"22\"),\n", - " (\"11\", \"23\"), # Note that 12 and 24 would be phrased \"midi\" and \"minuit\" respectively\n", - "])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "X0-z-qJAmIiI" - }, - "source": [ - "We then create an alternate graph where this conversion is mapped onto the hours function - given a post-noon suffix - and create a union with our earlier graph:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "8CdEmo9NmN7u" - }, - "outputs": [], - "source": [ - "night_suffixes = pynutil.delete(\"suffix: \\\"pm\\\"\")\n", - "graph |= (\n", - " hour @ hour_to_night\n", - " + delete_extra_space\n", - " + pynutil.insert(\"h\")\n", - " + minute.ques\n", - " + delete_space\n", - " + night_suffixes\n", - " )" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "YnoIkZBqmaTo" - }, - "source": [ - "Giving us a final Verbalizer of:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "ZfXimvFBmdDD" - }, - "outputs": [], - "source": [ - "class TimeFst(GraphFst):\n", - " def __init__(self):\n", - " super().__init__(name=\"time\", kind=\"verbalize\")\n", - "\n", - " hour_to_night = pynini.string_map([\n", - " (\"1\", \"13\"),\n", - " (\"2\", \"14\"),\n", - " (\"3\", \"15\"),\n", - " (\"4\", \"16\"),\n", - " (\"5\", \"17\"),\n", - " (\"6\", \"18\"),\n", - " (\"7\", \"19\"),\n", - " (\"8\", \"20\"),\n", - " (\"9\", \"21\"),\n", - " (\"10\", \"22\"),\n", - " (\"11\", \"23\"),\n", - "])\n", - "\n", - " day_suffixes = pynutil.delete(\"suffix: \\\"am\\\"\")\n", - " night_suffixes = pynutil.delete(\"suffix: \\\"pm\\\"\")\n", - "\n", - " hour = (\n", - " pynutil.delete(\"hours:\")\n", - " + delete_space\n", - " + pynutil.delete(\"\\\"\")\n", - " + pynini.closure(NEMO_DIGIT, 1, 2)\n", - " + pynutil.delete(\"\\\"\")\n", - " )\n", - " minute = (\n", - " pynutil.delete(\"minutes:\")\n", - " + delete_extra_space\n", - " + pynutil.delete(\"\\\"\")\n", - " + pynini.closure(NEMO_DIGIT, 1, 2)\n", - " + pynutil.delete(\"\\\"\")\n", - " )\n", - "\n", - " graph = hour + delete_extra_space + pynutil.insert(\"h\") + minute.ques + delete_space + day_suffixes.ques\n", - "\n", - " graph |= (\n", - " hour @ hour_to_night\n", - " + delete_extra_space\n", - " + pynutil.insert(\"h\")\n", - " + minute.ques\n", - " + delete_space\n", - " + night_suffixes\n", - " )\n", - " delete_tokens = self.delete_tokens(graph)\n", - " self.fst = delete_tokens.optimize()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "e5tPcCaSYuhY" - }, - "source": [ - "If you've noticed, the Verbalizer process has become simpler as we've progressed through our WFSTs. Commonly, you will seldom need to even provide the amount of overhead we've seen in `TimeFst`, `MoneyFst`, and `OrdinalFst`, and the majority of this component is simply removing tokens as an intermediary step, as we'll see for our Name class." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "iHmRe3UIhyIH" - }, - "source": [ - "# WhiteList WFST " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "8kMn2qB9bVFy" - }, - "source": [ - "\n", - "While developing your grammars, you may encounter tokens that refuse standard categorization and yet still require normalization. For example, you may need to render \"Mister Brown\" as `Mr. Brown` or \"H M S Nelson\" as `H.M.S. Nelson`. As these cases are rather specific, they lack a regular pattern for a specific classifier. (What about \"mister\" as a token requires tokenization as opposed to \"Brown\".) Instead, we need to explicitly list their input-output mappings (i.e. a whitelist).\n", - "\n", - "For NeMo, this is performed through the `WhiteListFst`:\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "6B4oPXYcccWs" - }, - "source": [ - "## Grammar" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "RThTLUCRceOO" - }, - "source": [ - "`WhitelistFst` is essentially just a wrapper for a `string_map` or `string_file` mapping with the appropriate formatting for deployment. Per our example, we can make a graph with the following:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "eIOOb_wJdMMx" - }, - "outputs": [], - "source": [ - "graph = pynini.string_map([\n", - " (\"mister\", \"mr.\"),\n", - " (\"h m s\", \"h.m.s\"),\n", - " (\"doctor\", \"dr.\")\n", - "])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "O5kTXwmPZ9Tt" - }, - "source": [ - "As previously mentioned, here is where the use of `string_file` will make maintenance much easier. Discovering whitelist mappings is an iterative process and you will more than likely need to return to your list throughout development. For instance, it may be obvious that tokens such as \"madame\", \"miss\", \"esquire\", but would you think of providing abbreviations for \"the right honorable\" or \"tennessee valley authority\"? Keeping a tsv file available for quick insertions greatly assists here." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "RC5Cf-Z8dYVk" - }, - "source": [ - "## Classifier" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "144nvAHEdfBJ" - }, - "source": [ - "Unlike for our other WFSTs, There is no specific semiotic class for `WhiteListFst`. It instead falls under the default Name class to designate there is no need for further processing beyond obligatory tokenization. Indeed, we can simply insert the token ourselves instead of calling `add_tokens`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "oPkrmg2gdznd" - }, - "outputs": [], - "source": [ - "class WhiteListFst(GraphFst):\n", - " def __init__(self):\n", - " super().__init__(name=\"whitelist\", kind=\"classify\")\n", - "\n", - " whitelist = pynini.string_map([\n", - " (\"mister\", \"mr.\"),\n", - " (\"h m s\", \"h.m.s\"),\n", - " (\"doctor\", \"dr.\")])\n", - " graph = pynutil.insert(\"name: \\\"\") + convert_space(whitelist) + pynutil.insert(\"\\\"\")\n", - " self.fst = graph.optimize()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "B05kdSIdd2dv" - }, - "source": [ - "## Verbalizer" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Since the whitelisted token has already been rendered in the desired normalized form, all that is necessary is to strip the `name` token and render the string 'as is'. This can be done by through the following:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "gaq3voIYiUCA" - }, - "outputs": [], - "source": [ - "class WhiteListFst(GraphFst):\n", - " def __init__(self):\n", - " super().__init__(name=\"whitelist\", kind=\"verbalize\")\n", - " graph = (\n", - " pynutil.delete(\"name:\")\n", - " + delete_space\n", - " + pynutil.delete(\"\\\"\")\n", - " + pynini.closure(NEMO_CHAR - \" \", 1)\n", - " + pynutil.delete(\"\\\"\")\n", - " )\n", - " graph = graph @ pynini.cdrewrite(pynini.cross(u\"\\u00A0\", \" \"), \"\", \"\", NEMO_SIGMA) # Removes possible null token\n", - " self.fst = graph.optimize()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "cUE7Gg35bWKb" - }, - "source": [ - "While the graph is largely self-explanatory, take note that the default implementation assumes a character string without spacing. If you intend to include additional formatting in your normalization (e.g. `H. M. S.` instead of `H.M.S.`), you may need to adjust the graph to expand coverage." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "_o_a15Fg7niv" - }, - "source": [ - "# Word and Punctuation WFST " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Zi6lP7mTmnUV" - }, - "source": [ - "Continuing with the Name class, we will conclude with the Word and Punctuation WFSTs. These are among the simplest and most crucial classes of the entire ITN system, as they classify all tokens that are not caught by other semiotic classes. Since these other tokens make up the majority of all strings your normalization system will encounter, they are essential for general functionality.\n", - "\n", - "However, they escape discussion as their function is self-evident: since they function as default classes, tokens only reach Word WFST and Punctuation WFST if they have not been accepted by the other WFSTs. As such, we can simply accept the tokens as they are, providing them a `name` tag." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "9zCqczLqp5NW" - }, - "source": [ - "## Classifier" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "eUWum5U0p99c" - }, - "source": [ - "For instance, consider the entire `WordFst` Classifier in its entirety:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "CCZSTeDHofDl" - }, - "outputs": [], - "source": [ - "class WordFst(GraphFst):\n", - " def __init__(self):\n", - " super().__init__(name=\"word\", kind=\"classify\")\n", - " word = pynutil.insert(\"name: \\\"\") + pynini.closure(NEMO_NOT_SPACE, 1) + pynutil.insert(\"\\\"\")\n", - " self.fst = word.optimize()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "9ys2VpjjoiEC" - }, - "source": [ - "It just processes the entire token string with the `NEMO_NOT_SPACE` utility WFST (which accepts any string that is not a space). For your language, you may simply use one of the preexisting `WordFst`.\n", - "\n", - "Depending on language, the `PunctuationFst` may require some (minimal) adjustment. Note the following:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "Mnnd3PVMpF4t" - }, - "outputs": [], - "source": [ - "class PunctuationFst(GraphFst):\n", - " def __init__(self):\n", - " super().__init__(name=\"punctuation\", kind=\"classify\")\n", - "\n", - " s = \"!#$%&\\'()*+,-./:;<=>?@^_`{|}~\"\n", - " punct = pynini.union(*s)\n", - "\n", - " graph = pynutil.insert(\"name: \\\"\") + punct + pynutil.insert(\"\\\"\")\n", - "\n", - " self.fst = graph.optimize()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "_afW02LXpLtz" - }, - "source": [ - "If your language uses other punctuation than that in the `s` string (or reserves some of the punctuation as characters), you may simply edit `s` to accommodate. \n", - "\n", - "For instance, French has a unique quotation style that utilizes guillemets \"« »\". We may add their Unicode codepoints (to avoid encoding issues) to `s`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "mgfZIKzVplVm" - }, - "outputs": [], - "source": [ - "class PunctuationFst(GraphFst):\n", - " def __init__(self):\n", - " super().__init__(name=\"punctuation\", kind=\"classify\")\n", - "\n", - " s = \"!#$%&\\'()*+,-./:;<=>?@^_`{|}~\"\n", - " guillemets = \"\\u00AB\" + \"\\u00BB\" # quotation marks in French.\n", - " s += guillemets\n", - " punct = pynini.union(*s)\n", - "\n", - " graph = pynutil.insert(\"name: \\\"\") + punct + pynutil.insert(\"\\\"\")\n", - "\n", - " self.fst = graph.optimize()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "6Upb5-wcp_7H" - }, - "source": [ - "## Verbalizer" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "ufWT1T6GqCCT" - }, - "source": [ - "Note that both `PunctuationFst` and `WordFst` both encode with the `name` property. This leaves no differentiation between the two for a Verbalizer. This makes sense as there are no particular formatting rules for them, they simply need a placeholder tag to avoid alteration between the Classifier and Verbalizer step. Once passed to the verbalizer, they are rendered as normal by simply removing the tag (this is practically identical to the WhiteListFST):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "LqyhqQKZqcph" - }, - "outputs": [], - "source": [ - "class WordFst(GraphFst):\n", - " def __init__(self):\n", - " super().__init__(name=\"word\", kind=\"verbalize\")\n", - " chars = pynini.closure(NEMO_CHAR - \" \", 1)\n", - " char = pynutil.delete(\"name:\") + delete_space + pynutil.delete(\"\\\"\") + chars + pynutil.delete(\"\\\"\")\n", - " graph = char @ pynini.cdrewrite(pynini.cross(u\"\\u00A0\", \" \"), \"\", \"\", NEMO_SIGMA) # Cleans up possible null character\n", - "\n", - " self.fst = graph.optimize()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "lGbrUkcpapyi" - }, - "source": [ - "For many languages, the writing of your `WordFst` and `PunctuationFst` (both Classifiers and Verbalizers) will require no more than duplicating the preexisting grammars found in NeMo Text Processing." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "5y9jhkhQ7p4W" - }, - "source": [ - "# Other Classes " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "j1mgnISmiu-g" - }, - "source": [ - "While the preceding discussion should be suitable for development of the remaining classes, some helpful notes may be of use before continuing:\n", - "- Fraction WFST: This is the last of the 'fundamental' classes and should take priority after completion of the Decimal WFST. It operates very similarly to the Ordinal WFST in that you wish to recover the Cardinal roots for the numerator and denominator prior to tagging. Its properties are: `negative`, `integer_part`, `numerator`, and `denominator`.\n", - "- Measure WFST: Like the Money WFST, this will require management of several 'parent' WFSTS (Fraction, Cardinal, Decimal) to be suitably comprehensive. As well, you may find it more productive to find ways to compose new measurement units instead of simply listing all (e.g. micrometers, petameters, miles per hour, feet per second). Its properties are: `negative`, `units` and it allows subgraphs of the `cardinal`, `decimal`, and `fraction` classes. (This is, it allows tokenization within the tokenization.)\n", - "- Date WFST: Depending on writing conventions, this may vary in complexity. For instance, English speakers may write dates as `01/01/2021/` or `Jan. 1 2021`. Are there specific use cases where one is preferred or should you simply decide on a format? Further, you may wish to take advantage of the `preserve order` property to avoid possible unwanted verbalizations (some implementations will permit both `Jan. 1` and `1 Jan.` if not careful.) Its properties are: `month`, `day`, and `year`. \n", - "- Telephone WFST: These will be heavily dependent not only on writing conventions but even regional preference. For instance, the U.S. commonly uses a ten digit system broken into the following sequence: `###-###-####`. Meanwhile, mainland France breaks a ten digit sequence into groups of two: `##-##-##-##-##`. Take careful note of how your language's target region verbalizes these figures and leave room for some variation in development. The `telephone` class has only one property: `number_part`. \n", - "- Electronic WFST: For normalizing email addresses or urls, you will need to develop for the `electronic` class. The main concerns will be managing alphanumeric strings and parsing the reserved symbols used for protocols and domains. (How does your target language pronounce \"https://\"? www? '.' or '@'?\") Depending on whether you are normalizing a url or email, the following properties will be needed:\n", - " - email: `username`, `domain`\n", - " - url: `protocol` (Sparrowhawk allows further detail here but NeMo passes the entire url through the `protocol` property)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "-i25X8mK90n3" - }, - "source": [ - "# Tokenize and Classify " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "v4bcigU6b9ss" - }, - "source": [ - "We are now ready to build a general Classifier for our entire language. Upon completion of your grammars, the next step is to unite them together in a general Classifier WFST - located within a `tokenize_and_classify.py` file, preferably. This WFST will be responsible for determining the appropriate semiotic class for each token in your string and processing the necessary properties for normalization.\n", - "\n", - "For this section, we will focus on the following: grammar composition, assignment of weights, and importing/exporting as a FAR file. Since we will need to work with some instantiated graphs, let's preload them before proceeding. (Note the compilingtime.)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nemo_text_processing.inverse_text_normalization.fr.taggers.cardinal import CardinalFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.taggers.decimal import DecimalFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.taggers.money import MoneyFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.taggers.ordinal import OrdinalFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.taggers.punctuation import PunctuationFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.taggers.time import TimeFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.taggers.whitelist import WhiteListFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.taggers.word import WordFst\n", - "\n", - "cardinal = CardinalFst()\n", - "cardinal_graph = cardinal.fst\n", - "\n", - "ordinal = OrdinalFst(cardinal)\n", - "ordinal_graph = ordinal.fst\n", - "\n", - "decimal = DecimalFst(cardinal)\n", - "decimal_graph = decimal.fst\n", - "\n", - "whitelist_graph = WhiteListFst().fst\n", - "word_graph = WordFst().fst\n", - "time_graph = TimeFst().fst\n", - "money_graph = MoneyFst(cardinal, decimal).fst\n", - "punct_graph = PunctuationFst().fst" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "MIv58eSocOV1" - }, - "source": [ - "## Grammar" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "k_RPlnfVdG5E" - }, - "source": [ - "As for all previous grammars, the `tokenize_and_classify` grammar inherits from a `GraphFst` as an individual class: `ClassifyFst`. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "WHKG4c2WdW0G" - }, - "outputs": [], - "source": [ - "class ClassifyFst(GraphFst):\n", - " def __init__(self):\n", - " super().__init__(name=\"tokenize_and_classify\", kind=\"classify\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "j9_I6DJmdcOG" - }, - "source": [ - "This class is responsible for instantiating all subgraphs and passing necessary dependencies:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "4YtmcxLOdlas" - }, - "outputs": [], - "source": [ - "class ClassifyFst(GraphFst):\n", - " def __init__(self):\n", - " super().__init__(name=\"tokenize_and_classify\", kind=\"classify\")\n", - "\n", - " cardinal = CardinalFst()\n", - " cardinal_graph = cardinal.fst\n", - "\n", - " ordinal = OrdinalFst(cardinal)\n", - " ordinal_graph = ordinal.fst\n", - "\n", - " decimal = DecimalFst(cardinal)\n", - " decimal_graph = decimal.fst\n", - "\n", - " whitelist_graph = WhiteList().fst\n", - " word_graph = WordFst().fst\n", - " time_graph = TimeFst().fst\n", - " money_graph = MoneyFst(cardinal, decimal).fst\n", - " punct_graph = PunctuationFst().fst" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "y5vGvv3HeAY9" - }, - "source": [ - "We then join all the grammars together so `ClassifyFst` can apply them. Rather unceremoniously, this is accomplished by performing a union across all grammars (excluding `PunctuationFst`, to assist tokenization). We then follow this union by inserting the `tokens` class around the resulting formatting (required for processing):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "oocgPQ5geZJO" - }, - "outputs": [], - "source": [ - "class ClassifyFst(GraphFst):\n", - " def __init__(self):\n", - " super().__init__(name=\"tokenize_and_classify\", kind=\"classify\")\n", - "\n", - " cardinal = CardinalFst()\n", - " cardinal_graph = cardinal.fst\n", - "\n", - " ordinal = OrdinalFst(cardinal)\n", - " ordinal_graph = ordinal.fst\n", - "\n", - " decimal = DecimalFst(cardinal)\n", - " decimal_graph = decimal.fst\n", - "\n", - " whitelist_graph = WhiteListFst().fst\n", - " word_graph = WordFst().fst\n", - " time_graph = TimeFst().fst\n", - " money_graph = MoneyFst(cardinal, decimal).fst\n", - " punct_graph = PunctuationFst().fst\n", - "\n", - " classify = (\n", - " time_graph\n", - " | whitelist_graph\n", - " | decimal_graph\n", - " | cardinal_graph\n", - " | ordinal_graph\n", - " | money_graph\n", - " | word_graph\n", - " )\n", - " token = pynutil.insert(\"tokens { \") + classify + pynutil.insert(\" }\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "ASWDXWQjfLEU" - }, - "source": [ - "Our graph is now able to process an individual token. But what about a string? Here you will need to be mindful of the tokenization behavior for your language and decide on your desired treatment of punctuation (hence exclusion from the main graph). \n", - "\n", - "For our purposes, we will assume the convention of space and punctuation serving as token separators. We graph punctuation as individual tokens" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "r6WztK2jwhFt" - }, - "outputs": [], - "source": [ - "punct_graph = PunctuationFst().fst\n", - "punct = pynutil.insert(\"tokens { \") + punct_graph + pynutil.insert(\" }\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "9T2rT89jw3T1" - }, - "source": [ - "and join the `punct` graph with our `tokens` graph (inserting spaces between tokens for formatting)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "rGtVOK-txKOP" - }, - "outputs": [], - "source": [ - "token = \"PLACEHOLDER\"\n", - "token_plus_punct = (\n", - " pynini.closure(punct + pynutil.insert(\" \")) + token + pynini.closure(pynutil.insert(\" \") + punct)\n", - " ) # Note the use of closure incase there are multiple punctuations\n", - "graph = token_plus_punct + pynini.closure(delete_extra_space + token_plus_punct)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "_gixfQ69xWPe" - }, - "source": [ - "then address space between tokens: \n", - "\n", - "`graph = delete_space + graph + delete_space`" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "DWnmazWecyUG" - }, - "source": [ - "## Weighting " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "egHbwIbMx-hT" - }, - "source": [ - "Were we to leave our `ClassifyFst` like this, we would undoubtedly encounter a mountain of errors. What will stop our graph from treating punctuation that is part of a previous grammar as a token separator (e.g. \"vingt-et-un\")? How do we ensure that a currency string isn't treated as solely a decimal string with a `name` token following?\n", - "\n", - "As in previous cases, the solution lies in our choice of weights for the grammar." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "y3U7_M8CyxZ1" - }, - "source": [ - "Let us return to the main graph:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "9VXe1dfsy3Be" - }, - "outputs": [], - "source": [ - "classify = (\n", - " time_graph\n", - " | whitelist_graph\n", - " | decimal_graph\n", - " | cardinal_graph\n", - " | ordinal_graph\n", - " | money_graph\n", - " | word_graph\n", - " )\n", - "punct = pynutil.insert(\"tokens { \") + punct_graph + pynutil.insert(\" }\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "aY4vOFqxy5ua" - }, - "source": [ - "Beyond the path weights that we explicitly added, these graphs are currently weightless. Since we want the graphs themselves to be the general determiners of a path, let us use some default weights an order of magnitude beyond our path weights (we use `pynutil.add_weight`):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "bthyt_Le2rsA" - }, - "outputs": [], - "source": [ - "classify = (\n", - " pynutil.add_weight(time_graph, 1)\n", - " | pynutil.add_weight(whitelist_graph, 1)\n", - " | pynutil.add_weight(decimal_graph, 1)\n", - " | pynutil.add_weight(cardinal_graph, 1)\n", - " | pynutil.add_weight(ordinal_graph, 1)\n", - " | pynutil.add_weight(money_graph, 1)\n", - " | pynutil.add_weight(word_graph, 1)\n", - " )\n", - "punct = pynutil.insert(\"tokens { \") + pynutil.add_weight(punct_graph, 1) + pynutil.insert(\" }\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "xMNIJbzj3MMP" - }, - "source": [ - "Let's see what logical adjustments should be made. First off, we know that we want each class token to span the largest string possible. (e.g. We don't want \"quatre-vingt\" to be rendered as two `cardinal` classes with a hyphen in between.) As such, we want to penalize our graph for using more than one tokens. We can do so by establishing the following constraint: the sum of two or more tokens cannot be less than the weight of a single token. Or, for any pair of tokens `w_1` and `w_2`, their sum must always be greater than any other individual token (including themselves):\n", - "\n", - "`w_1 + w_2 > k >= w`\n", - "\n", - "To keep things simple, let us make the upper limit `2`. This means we should increase all the weights to keep our constraint:\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "classify = (\n", - " pynutil.add_weight(time_graph, 1.1)\n", - " | pynutil.add_weight(whitelist_graph, 1.1)\n", - " | pynutil.add_weight(decimal_graph, 1.1)\n", - " | pynutil.add_weight(cardinal_graph, 1.1)\n", - " | pynutil.add_weight(ordinal_graph, 1.1)\n", - " | pynutil.add_weight(money_graph, 1.1)\n", - " | pynutil.add_weight(word_graph, 1.1)\n", - " )\n", - "punct = pynutil.insert(\"tokens { \") + pynutil.add_weight(punct_graph, 1.1) + pynutil.insert(\" }\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Do we want this constraint to include all tokens? Imagine if we had a string of multiple semiotic tokens in a row. Since this string's combined weight would be larger than any single class token, a grammar that served as a universal acceptor (i.e. `word_graph`) would be preferred over these individual classes. This would be obviously incorrect. As such, we want to make sure that `word_graph` would only be traversed when there is truly no other option:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "qc_CU2ro63eg" - }, - "outputs": [], - "source": [ - "classify = (\n", - " pynutil.add_weight(time_graph, 1.1)\n", - " | pynutil.add_weight(whitelist_graph, 1.1)\n", - " | pynutil.add_weight(decimal_graph, 1.1)\n", - " | pynutil.add_weight(cardinal_graph, 1.1)\n", - " | pynutil.add_weight(ordinal_graph, 1.1)\n", - " | pynutil.add_weight(money_graph, 1.1)\n", - " | pynutil.add_weight(word_graph, 100)\n", - " )\n", - "punct = pynutil.insert(\"tokens { \") + pynutil.add_weight(punct_graph, 1.1) + pynutil.insert(\" }\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, even with a string of fifty different class tokens, `word_graph` would still not be considered as a path to traverse." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "fW8C3vD-7Dbl" - }, - "source": [ - "Next, let us consider our foundational graph: `cardinal_graph`. As Cardinals occur in practically all our WFSTs, it's possible for `cardinal_graph` to apply in almost all cases. Yet, we've specifically invoked `CardinalFST` when it was required in any of the other classes, so it will never be needed in any of those cases. This means that we want all those graphs to have *priority* over `cardinal_graph`. As such, we will increase its weight so it takes second lowest precedence (while still paying attention to the combined weight constraint). " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "97UwGaEn8pj7" - }, - "outputs": [], - "source": [ - "classify = (\n", - " pynutil.add_weight(time_graph, 1.1)\n", - " | pynutil.add_weight(whitelist_graph, 1.1)\n", - " | pynutil.add_weight(decimal_graph, 1.1)\n", - " | pynutil.add_weight(cardinal_graph, 1.2)\n", - " | pynutil.add_weight(ordinal_graph, 1.1)\n", - " | pynutil.add_weight(money_graph, 1.1)\n", - " | pynutil.add_weight(word_graph, 100)\n", - " )\n", - "punct = pynutil.insert(\"tokens { \") + pynutil.add_weight(punct_graph, 1.1) + pynutil.insert(\" }\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "0d9Lw4Ot88_B" - }, - "source": [ - "This form of thinking can be applied to all the 'foundational' graphs you may develop: the dependent graphs should take higher precedence than the graphs they borrow from. For instance, since `money_graph` utilizes `decimal_graph`, we know it should take precedence. However, since `decimal_graph` borrows from `cardinal_graph`, its weight must still be less than `1.2`. As such: " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "-wF8cgLK9tpU" - }, - "outputs": [], - "source": [ - "classify = (\n", - " pynutil.add_weight(time_graph, 1)\n", - " | pynutil.add_weight(whitelist_graph, 1)\n", - " | pynutil.add_weight(decimal_graph, 1.1)\n", - " | pynutil.add_weight(cardinal_graph, 1.2)\n", - " | pynutil.add_weight(ordinal_graph, 1)\n", - " | pynutil.add_weight(money_graph, 1.09)\n", - " | pynutil.add_weight(word_graph, 100)\n", - " )\n", - "punct = pynutil.insert(\"tokens { \") + pynutil.add_weight(punct_graph, 1) + pynutil.insert(\" }\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "huMzDoZ2-FD2" - }, - "source": [ - "For those classes that don't seem affected, we can set their weights as the same as those below their 'foundation' graphs, simply to prevent prioritization when not required\n", - "\n", - "Meanwhile, `whitelist_graph` should take precedence over all others, as it may contain unique normalizations that may get accidentally caught by the other graphs." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "gWG6ttyd-bbD" - }, - "outputs": [], - "source": [ - "classify = (\n", - " pynutil.add_weight(time_graph, 1.1)\n", - " | pynutil.add_weight(whitelist_graph, 1.07)\n", - " | pynutil.add_weight(decimal_graph, 1.1)\n", - " | pynutil.add_weight(cardinal_graph, 1.2)\n", - " | pynutil.add_weight(ordinal_graph, 1.1)\n", - " | pynutil.add_weight(money_graph, 1.08)\n", - " | pynutil.add_weight(word_graph, 100)\n", - " )\n", - "punct = pynutil.insert(\"tokens { \") + pynutil.add_weight(punct_graph, 1.1) + pynutil.insert(\" }\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "1TH08f8O-fWx" - }, - "source": [ - "Keep in mind that building weights in this manner is hardly a rule for grammar development and is instead intended as a means to initialize weights for empirical development. You will find that actual strings will cause unexpected behavior that require fine tuning. \n", - "\n", - "For instance, the Classifier for French in NeMo ITN benefits from having varying precedence for some weights, as seen in the following excerpt:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "gKdkyDK3_r46" - }, - "outputs": [], - "source": [ - "class ClassifyFst(GraphFst):\n", - " \"\"\"\n", - " Final class that composes all other classification grammars. This class can process an entire sentence, that is lower cased.\n", - " For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File. \n", - " More details to deployment at NeMo/tools/text_processing_deployment.\n", - "\n", - " Args:\n", - " cache_dir: path to a dir with .far grammar file. Set to None to avoid using cache.\n", - " overwrite_cache: set to True to overwrite .far files\n", - " \"\"\"\n", - "\n", - " def __init__(self, cache_dir: str = None, overwrite_cache: bool = False):\n", - " super().__init__(name=\"tokenize_and_classify\", kind=\"classify\")\n", - "\n", - " far_file = None\n", - " if cache_dir is not None and cache_dir != \"None\":\n", - " os.makedirs(cache_dir, exist_ok=True)\n", - " far_file = os.path.join(cache_dir, \"_fr_itn.far\")\n", - " if not overwrite_cache and far_file and os.path.exists(far_file):\n", - " self.fst = pynini.Far(far_file, mode=\"r\")[\"tokenize_and_classify\"]\n", - " logging.info(f\"ClassifyFst.fst was restored from {far_file}.\")\n", - " else:\n", - " logging.info(f\"Creating ClassifyFst grammars.\")\n", - "\n", - " cardinal = CardinalFst()\n", - " cardinal_graph = cardinal.fst\n", - "\n", - " fraction = FractionFst(cardinal)\n", - " fraction_graph = fraction.fst\n", - "\n", - " ordinal = OrdinalFst(cardinal)\n", - " ordinal_graph = ordinal.fst\n", - "\n", - " decimal = DecimalFst(cardinal)\n", - " decimal_graph = decimal.fst\n", - "\n", - " measure_graph = MeasureFst(cardinal=cardinal, decimal=decimal, fraction=fraction).fst\n", - " date_graph = DateFst(cardinal).fst\n", - " word_graph = WordFst().fst\n", - " time_graph = TimeFst().fst\n", - " money_graph = MoneyFst(cardinal, decimal).fst\n", - " whitelist_graph = WhiteListFst().fst\n", - " punct_graph = PunctuationFst().fst\n", - " electronic_graph = ElectronicFst().fst\n", - " telephone_graph = TelephoneFst().fst\n", - "\n", - " classify = (\n", - " pynutil.add_weight(whitelist_graph, 1.01)\n", - " | pynutil.add_weight(time_graph, 1.05)\n", - " | pynutil.add_weight(date_graph, 1.09)\n", - " | pynutil.add_weight(decimal_graph, 1.08)\n", - " | pynutil.add_weight(measure_graph, 1.1)\n", - " | pynutil.add_weight(cardinal_graph, 1.1)\n", - " | pynutil.add_weight(ordinal_graph, 1.1)\n", - " | pynutil.add_weight(fraction_graph, 1.09)\n", - " | pynutil.add_weight(money_graph, 1.07)\n", - " | pynutil.add_weight(telephone_graph, 1.1)\n", - " | pynutil.add_weight(electronic_graph, 1.1)\n", - " | pynutil.add_weight(word_graph, 100)\n", - " )\n", - "\n", - " punct = pynutil.insert(\"tokens { \") + pynutil.add_weight(punct_graph, weight=1.1) + pynutil.insert(\" }\")\n", - " token = pynutil.insert(\"tokens { \") + classify + pynutil.insert(\" }\")\n", - " token_plus_punct = (\n", - " pynini.closure(punct + pynutil.insert(\" \")) + token + pynini.closure(pynutil.insert(\" \") + punct)\n", - " )\n", - "\n", - " graph = token_plus_punct + pynini.closure(delete_extra_space + token_plus_punct)\n", - " graph = delete_space + graph + delete_space\n", - "\n", - " self.fst = graph.optimize()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "qc4B_0rNcQZu" - }, - "source": [ - "## FAR import/export" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "0nRRPvy-AYsA" - }, - "source": [ - "While working through these code excerpts, you may have noticed some latency with each instantiation of our WFSTs (notably wherever `CardinalFst` was involved). This is because the `pynini.optimize` that we call with each graph's instantiation is computationally expensive. For our ultimate purpose of deployment, it seems a waste of resources to recreate stable graphs for each use.\n", - "\n", - "To address this, NeMo ITN supports WFST caching through use of `pynini.Far`, storing and recovering Classify grammars as FAR (Fst ARchives).\n", - "\n", - "Let us update our `ClassifyFst` to permit passing a cache and allowing overwriting (for development):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "5XgWevUzD1AE" - }, - "outputs": [], - "source": [ - "class ClassifyFst(GraphFst):\n", - " def __init__(self, cache_dir: str = None, overwrite_cache: bool = False):\n", - " super().__init__(name=\"tokenize_and_classify\", kind=\"classify\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "l28GMR70ESz0" - }, - "source": [ - "For storing our graphs as FARs, we can use `graph_utils.generator_main`, which saves our WFSTs by type for easier management. For arguments it takes a string name and a dict mapping of WFST type to graph:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "AzTkcmAWFLYm" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "class ClassifyFst(GraphFst):\n", - " def __init__(self, cache_dir: str = None, overwrite_cache: bool = False):\n", - " super().__init__(name=\"tokenize_and_classify\", kind=\"classify\")\n", - " # Grammar here\n", - " # ....\n", - " if cache_dir is not None and cache_dir != \"None\":\n", - " os.makedirs(cache_dir, exist_ok=True)\n", - " far_file = os.path.join(cache_dir, \"_fr_itn.far\")\n", - " generator_main(far_file, {\"tokenize_and_classify\": self.fst})" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Wz8wjCQSD6eJ" - }, - "source": [ - "We pair this with the ability to load from cache (note the `\"tokenize_and_classify\"` key being passed):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "FRFYgMmuD_53" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "class ClassifyFst(GraphFst):\n", - " def __init__(self, cache_dir: str = None, overwrite_cache: bool = False):\n", - " super().__init__(name=\"tokenize_and_classify\", kind=\"classify\")\n", - " if not overwrite_cache and far_file and os.path.exists(far_file):\n", - " self.fst = pynini.Far(far_file, mode=\"r\")[\"tokenize_and_classify\"]\n", - " else:\n", - " # Grammar here\n", - " # ....\n", - " if cache_dir is not None and cache_dir != \"None\":\n", - " os.makedirs(cache_dir, exist_ok=True)\n", - " far_file = os.path.join(cache_dir, \"_fr_itn.far\")\n", - " generator_main(far_file, {\"tokenize_and_classify\": self.fst})\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "ib9nggZxF38s" - }, - "source": [ - "Producing our `ClassifyFst` as:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "d2BZyx6sGGg2" - }, - "outputs": [], - "source": [ - "class ClassifyFst(GraphFst):\n", - " def __init__(self, cache_dir: str = None, overwrite_cache: bool = False):\n", - " super().__init__(name=\"tokenize_and_classify\", kind=\"classify\")\n", - "\n", - " far_file = None\n", - " if cache_dir is not None and cache_dir != \"None\":\n", - " os.makedirs(cache_dir, exist_ok=True)\n", - " far_file = os.path.join(cache_dir, \"_fr_itn.far\")\n", - " if not overwrite_cache and far_file and os.path.exists(far_file):\n", - " self.fst = pynini.Far(far_file, mode=\"r\")[\"tokenize_and_classify\"]\n", - " else:\n", - " cardinal = CardinalFst()\n", - " cardinal_graph = cardinal.fst\n", - "\n", - " ordinal = OrdinalFst(cardinal)\n", - " ordinal_graph = ordinal.fst\n", - "\n", - " decimal = DecimalFst(cardinal)\n", - " decimal_graph = decimal.fst\n", - "\n", - " whitelist_graph = WhiteList().fst\n", - " word_graph = WordFst().fst\n", - " time_graph = TimeFst().fst\n", - " money_graph = MoneyFst(cardinal, decimal).fst\n", - " whitelist_graph = WhiteListFst().fst\n", - " punct_graph = PunctuationFst().fst\n", - "\n", - " classify = (\n", - " pynutil.add_weight(time_graph, 1.1)\n", - " | pynutil.add_weight(whitelist_graph, 1.01)\n", - " | pynutil.add_weight(decimal_graph, 1.09)\n", - " | pynutil.add_weight(cardinal_graph, 1.1)\n", - " | pynutil.add_weight(ordinal_graph, 1.09)\n", - " | pynutil.add_weight(money_graph, 1.08)\n", - " | pynutil.add_weight(word_graph, 100)\n", - " )\n", - "\n", - " punct = pynutil.insert(\"tokens { \") + pynutil.add_weight(punct_graph, weight=1.1) + pynutil.insert(\" }\")\n", - " token = pynutil.insert(\"tokens { \") + classify + pynutil.insert(\" }\")\n", - " token_plus_punct = (\n", - " pynini.closure(punct + pynutil.insert(\" \")) + token + pynini.closure(pynutil.insert(\" \") + punct)\n", - " )\n", - "\n", - " graph = token_plus_punct + pynini.closure(delete_extra_space + token_plus_punct)\n", - " graph = delete_space + graph + delete_space\n", - "\n", - " self.fst = graph.optimize()\n", - "\n", - " if far_file:\n", - " generator_main(far_file, {\"tokenize_and_classify\": self.fst})" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "nEhY6wKKtfhn" - }, - "source": [ - "You should find the caching to vastly speed up compilingtime." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "rTtCnC5w95CI" - }, - "source": [ - "# Verbalize and Verbalize Final " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "H9y5yuk1HaGj" - }, - "source": [ - "Our last step is to create a universal Verbalizer for all classes. This is very similar to development of `ClassifierFst`, except that the Verbalizer breaks its normalization task into two components:\n", - "- `VerbalizeFst`, which removes formatting for each token\n", - "- `VerbalizeFinalFst`, which extends `VerbalizeFst` across all tokens in a string\n", - "Why two components when `tokenize_and_classify` was one? Because Sparrowhawk performs all the functionality of `VerbalizeFinalFst`, so its inclusion would break deployment. However, without it, your NeMo grammar would be unable to function at base. So we separate the two to allow the best of both world." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "vUawTJVuH8iR" - }, - "source": [ - "## VerbalizeFst" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "xghiBV06IIWU" - }, - "source": [ - "Much like `ClassifyFst`, `VerbalizeFst` instantiates all its subgraphs and then joins them together under a union operation. However, it does not need to employ weighting. Why? Because `ClassifyFst` has assigned each token a specific class. As each class is unique, there is no possibility that a subgraph will be employed for the wrong token.\n", - "\n", - "As such, our `VerbalizeFst` is formed by a simple union operation across all previous Verbalizer graphs:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "uMVCqCvsIt2v" - }, - "outputs": [], - "source": [ - "from nemo_text_processing.inverse_text_normalization.fr.verbalizers.cardinal import CardinalFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.verbalizers.decimal import DecimalFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.verbalizers.money import MoneyFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.verbalizers.ordinal import OrdinalFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.verbalizers.time import TimeFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.verbalizers.whitelist import WhiteListFst\n", - "from nemo_text_processing.inverse_text_normalization.fr.verbalizers.word import WordFst\n", - "\n", - "class VerbalizeFst(GraphFst):\n", - " def __init__(self):\n", - " super().__init__(name=\"verbalize\", kind=\"verbalize\")\n", - " cardinal = CardinalFst()\n", - " cardinal_graph = cardinal.fst\n", - " ordinal_graph = OrdinalFst().fst\n", - " decimal = DecimalFst()\n", - " decimal_graph = decimal.fst\n", - " whitelist_graph = WhiteListFst().fst\n", - " money_graph = MoneyFst(decimal=decimal).fst\n", - " time_graph = TimeFst().fst\n", - " graph = (\n", - " time_graph\n", - " | whitelist_graph\n", - " | money_graph\n", - " | ordinal_graph\n", - " | decimal_graph\n", - " | cardinal_graph\n", - " )\n", - " self.fst = graph" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Wap-LU6EI2Iu" - }, - "source": [ - "## Verbalize Final" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "TYaEt_0tI47t" - }, - "source": [ - "With `VerbalizeFst` complete, we now extend our graph to cover any series of tokens. All this requires is deletion of the `tokens` formatting (note the absence of such in our previous graph) and use of closure for any series of one or more tokens.\n", - "\n", - "This provides the following graph:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "L-9lJNE6JPCW" - }, - "outputs": [], - "source": [ - "\n", - "class VerbalizeFinalFst(GraphFst):\n", - " def __init__(self):\n", - " super().__init__(name=\"verbalize_final\", kind=\"verbalize\")\n", - " verbalize = VerbalizeFst().fst\n", - " word = WordFst().fst\n", - " types = verbalize | word\n", - " graph = (\n", - " pynutil.delete(\"tokens\")\n", - " + delete_space\n", - " + pynutil.delete(\"{\")\n", - " + delete_space\n", - " + types\n", - " + delete_space\n", - " + pynutil.delete(\"}\")\n", - " )\n", - " graph = delete_space + pynini.closure(graph + delete_extra_space) + graph + delete_space\n", - " self.fst = graph" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "WwMKFw-QJVgm" - }, - "source": [ - "Unlike `ClassifyFst`, NeMo ITN does not cache `VerbalizeFst` or `VerbalizeFinalFst`. (While you are welcome to provide such functionality in your own development, keep in mind that the limited complexity of our Verbalizers makes compilingtimes less significant.)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "7U21AZearZMK" - }, - "source": [ - "# Deployment " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "VrSccoh9K6JK" - }, - "source": [ - "Now that we have done all the groundwork, we can finally move to deployment. This final section will just cover the minor code alterations required to call your language through NeMo ITN and deploy through Sparrowhawk. For further information on using NeMo ITN, please see [this tutorial](https://colab.research.google.com/github/NVIDIA/NeMo/blob/stable/tutorials/text_processing/Inverse_(Text)_Normalization.ipynb). " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "0Le2aJvFIAKd" - }, - "source": [ - "## InverseNormalize" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "r2R3TUCDLi5-" - }, - "source": [ - "NeMo calls upon the `InverseNormalizer` class for all ITN tasks. Given a string and language, it will instantiate both the `ClassifierFst` and `VerbalizeFst` respective for the given language. (Note: we do not use `VerbalizeFinal` as its functions are managed by Sparrowhawk.) To make your language deployable in the general NeMo ITN system, you must designate the availability of these classes for instantiation. (For more information, see the [source code](https://github.com/NVIDIA/NeMo/blob/main/nemo_text_processing/inverse_text_normalization/inverse_normalize.py).)\n", - "\n", - "To do so requires only two changes. The first is providing a string to identify your language as an option for `parse_args` ([ISO codes are advised](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)):" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "tfv4Ee3ML-Fg" - }, - "source": [ - "```Python\n", - "def parse_args():\n", - " parser = ArgumentParser()\n", - " ...\n", - " parser.add_argument(\"--language\", choices=[..., 'MY_LANGUAGE'], type=str)\n", - " ...\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "awVl5nAsMUTl" - }, - "source": [ - "The next is to call your `ClassifyFst` and `VerbalizeFst` from `__init__`:" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```bash\n", - "class InverseNormalizer(Normalizer):\n", - " def __init__(self, lang: str = 'en', cache_dir: str = None, overwrite_cache: bool = False):\n", - "\n", - " if lang == 'en':\n", - " from nemo_text_processing.inverse_text_normalization.en.taggers.tokenize_and_classify import ClassifyFst\n", - " from nemo_text_processing.inverse_text_normalization.en.verbalizers.verbalize_final import (\n", - " VerbalizeFinalFst,\n", - " )\n", - " # Other languages\n", - " # ....\n", - " elif lang == 'MY_LANGUAGE':\n", - "\n", - " from nemo_text_processing.inverse_text_normalization.MY_LANGUAGE.taggers.tokenize_and_classify import ClassifyFst\n", - "\n", - " from nemo_text_processing.inverse_text_normalization.MY_LANGUAGE.verbalizers.verbalize_final import (\n", - "\n", - " VerbalizeFst,\n", - "\n", - " )\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "TI1PuejLMxdI" - }, - "source": [ - "And you're done! NeMo will handle the rest. " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "xrksINQoICfj" - }, - "source": [ - "## Grammar export and Deployment to C++" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "rP9-dmMJSg3h" - }, - "source": [ - "Find information here:\n", - "https://docs.nvidia.com/deeplearning/nemo/user-guide/docs/en/stable/nlp/text_normalization/wfst/wfst_text_processing_deployment.html" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "TDoVUxCE-Dax" - }, - "source": [ - "# Final Notes" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Fw-9mU7ql8iY" - }, - "source": [ - "Congratulations, you have now constructed an entire ITN system from the ground up! While your experience will vary with each language, you will find several commonalities that will assist you in further development. \n", - "\n", - "If you are interested in working further with your language WFSTs, you may wish to construct a TN system. Broadly, this is accomplished by inverting your previous graphs (`pynini.invert` may assist here) and changing your outputs to avoid indeterminacy (i.e. decide on one canonical output for your grammar for each class). But outside of such grammar specific edits, you repeat many of the steps exhibited here, such as:\n", - "- Use of a two step classifier-verbalizer system\n", - "- Same semiotic classes for tagging\n", - "- Inheritance of `GraphFst`\n", - "\n", - "For Audio-based non-deterministic text normalization please extend your grammars with additional output options for ambiguous options. Every semiotic class has a input flag `deterministic` which is by default set to True. For non-deterministic text normalization add additional grammar for the case `deterministic=False` \n", - "\n", - "We also recommend to look at the source of some of the existing [languages](https://docs.nvidia.com/deeplearning/nemo/user-guide/docs/en/stable/nlp/text_normalization/wfst/wfst_text_normalization.html#language-support), in particular English: https://github.com/NVIDIA/NeMo/tree/main/nemo_text_processing/inverse_text_normalization/en." - ] - } - ], - "metadata": { - "colab": { - "collapsed_sections": [], - "name": "WFST Tutorial.ipynb", - "provenance": [], - "toc_visible": true - }, - "interpreter": { - "hash": "fbc643a332f9d7801191710b24a8a955d342df4f32791f7fb65121dc4784751f" - }, - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/tutorials/text_processing/images/audio_based_tn.png b/tutorials/text_processing/images/audio_based_tn.png deleted file mode 100644 index 7953c7ee0eb05bfcb5f0e11252bcb16e4e27cf1e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 88329 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 S(FyLY$$nS*RxM!^^nU>fBM+$n diff --git a/tutorials/text_processing/images/cent.PNG b/tutorials/text_processing/images/cent.PNG deleted file mode 100644 index 457006f9bde2dd71d2654359aa107e90a322004c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10352 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 Uy$Sl)Uq_*@ZK75E@afzC15*x0^Z)<= diff --git a/tutorials/text_processing/images/cent_to_100.PNG b/tutorials/text_processing/images/cent_to_100.PNG deleted file mode 100644 index ee595ef7d91fb5299e61b627614cd60a0d77bb63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11898 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 AY5)KL diff --git a/tutorials/text_processing/images/cent_vingt_bad.PNG b/tutorials/text_processing/images/cent_vingt_bad.PNG deleted file mode 100644 index f8c56586641647385ee9e5687322720fa2703d2e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13247 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 diff --git a/tutorials/text_processing/images/cent_vingt_good.PNG b/tutorials/text_processing/images/cent_vingt_good.PNG deleted file mode 100644 index 88c0323dbcbefcf7ff5afb297254aee0b97230d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31222 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 diff --git a/tutorials/text_processing/images/cent_vingt_to_120.PNG b/tutorials/text_processing/images/cent_vingt_to_120.PNG deleted file mode 100644 index 017c98261949e56473b0aac471c7b05687e9ef33..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10937 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 S0(c?AbM+UG%O1Ua_kRF$9h)uy diff --git a/tutorials/text_processing/images/deployment.png b/tutorials/text_processing/images/deployment.png deleted file mode 100644 index 3cbef287dc80d85bcfb2259fddccce78ce8e1892..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 154145 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ⅅ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

PY*A6OHqtWdNubq!&iSlt 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
MA+MN_BjxRWhe;ZbzJMcij<>I#k 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 YEBwa}7k=G6i(ErBWt39h^}CP%56c{m;{X5v diff --git a/tutorials/text_processing/images/task_overview.png b/tutorials/text_processing/images/task_overview.png deleted file mode 100644 index 98aefe0364fa333fdd279f08d6b1346d1e7cf6e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49787 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
_xMTy>DUnsgD|Vj59s`5^N{v{?WJ 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

97XMIbP~1kqc!tzPi#%F}-+B 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 AEC2ui diff --git a/tutorials/text_processing/images/thutmose_tagger_alignment_top.png b/tutorials/text_processing/images/thutmose_tagger_alignment_top.png deleted file mode 100644 index dcdad57c4e0854d922a320266beffc4d56fa5961..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13009 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 diff --git a/tutorials/text_processing/images/thutmose_tagger_architecture.png b/tutorials/text_processing/images/thutmose_tagger_architecture.png deleted file mode 100644 index 4729ce51b425c28127ed5edc181191e360215b8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47623 zcmd42XH?To*FGARA|PPr1_@DV3IZxkh=2%!^bSh3(5uu?69okk0RidKr8nuFh=@p& zP9On-^cIj_LX!W)`+48ztn=Zl^Wl6rtmQKKmC4NBdv>|@HKE#?%FK)$j1UNfS>>Uk zE(Aj734t6NIC&ggscITi1^*p$*Hyj`Dfz{@1isMN$ZN<$AZ3wEM^9! zEUnak$J$-;EFqA(Pb!M?`rhWNB&KnFg9!@7U6%Vr_VLVPstf`G_Sd+MZ%bTbm46&~ zps`rehqhpnzP^|8&RB9F0P);s%b)sFJN<)nJ86579s%>AzR=#>yQhjx^wt zW(@&W1Tt3K#Bh`q0=f2{D@D^DS73%=;rFn^R8d?PV7Lt$;xWkg`~J)jh*-h{hg9-P z=VOPOCad;i5ZjXmPLZTXrCO)fL^GYbSiH_cEQ9trAP`$f*vMVdkOReHCsl*Pd92ZZ zGV@DrNrBWecOoD{O6C?51oEal$M*r@RRG++0Pl+$EBD;0!8rDRuNIOvb=zE^+hZTg z^br)j1hITe9r0QLiGG)~JO>j-+N1^-Vm+ZF>)472n7S4nr0=DnqyI*tB(iCt~P9M6ESAR2SKfH4aqTSb(|HnR&A#Nl#Tp1iwI>yu zBIiSHC@eIpbn5?YLb+XBU}gkl^?A6P41}ru1nqke0)9~2Vs1rY?Mf{j;;xlVD&y-){Sc`M~$REWbHxdTJ+ zmZ9HmxXD5>VQ9?&9FK%|TbMHu(x>-*#P1gI8iNzXJBj)-qM znm70((jbF35=z*+36@0yA5?D^h`J2tVtJC~Q@+>QYF!2=F>opr7SWa<| zaT{R{hxVv4I}~xZb1!bfyPx%t)4d+NbWds&^#osoLRhs+q^uteaRZa)(fhEE9Z6ZI zc+Y=s6J*6_O%`}=Nhnv@WAdwlag{EK`<#%Mn_m!!;$Ja-h~c%d>QZY&agP7?{$R7j zW(1e=-fhv#=MXXnZObyr&QP+pK|=``7GgDxPp!4rBK*ovOLS^Kb~*7#4nC3e&rgd}>+wln5jOP{e8aC)Zqu8Fpe;LkZH?)6)oh~seTNMr(L#r(Zb|t#GiyK+4}tp?Avg2qN2Z3|E7=i zd2s(;Tc?S?)9|6>wo7tEarZTF-vpP%lC~+~WM-p89YMdSu?-{P^tcpdQaRh<0C}m} zU*t1r=7$(P1MWg%1yCDRP9dnTF8u{{?6i>dez^D42PicbXt&gkpQ1hkfxL~R`l>l6 zBj&w4>MiEbzuyU=v}j+ijdNbW`$F#ZQ}IB^3>=g-cAwAYzdh%UH|!`$m!@3Agh8Igriyv9GD|$hGcdV=d2b?oO5Q`}Ue)xdFSuJyh6uFrVE1p8a{dp0=dt>S!5x+AhfnI|Ouy zI^16BFLeNmmN394SnHi&j>cML$m<608wX1`j^4EGOCK&Y0&)5RnYh-_8!>c&c(P}s zPcwTT7lViP7p&DSaKWc<|mbM!vew=*(nbSE+DM%G+~Ct&B2>tDY>$R*CsFflX!_^S)@Z2DCWP zfLJek-px-RA^UAplcs9tiGn7Hz(;g_FFj5wGFcWpgMn4d^=W4PUB%d!r$GgZ@aPwl zJ`0H}{-TkqC+BG~(PJQ-jr7DD?LhEWXqcTXq|Cf)&p}#OlLNa_wAZ|b z9Lp3Kh8rZXSJFAh55F2cr_j1q{@{4VXQQ%NVu+-Q`=6LUGBMJxN|>?72^A+ zgiDbbx1Wmsmj4?!1Mx?^<57fF3BN}0%@7%yA$0P`!ZHa(dK%0ydYw} z1pR2T9RzD-qcugChKXfG8c^ z&pbGJ?Ze$kn7w9}lQgIB*A0)wfqI2u`rc82{7VnE+-!P+nJ*1p`6TI*Ds2};&l(Xx zZ#W-3(v*BXd`gMm^LB&B(lkPRIEGW!-y7^qSirUp#Xd^H30q`b)w%9PS0hWSD~=n_ z@md>yY~iul!SCSRlO(QM_I&}CaO?SxJe{o7hxSW)@P~>1;(T5AH@Y&!elojS*?fI@ zOfBw48Fwn!(cOun?GZfxmBV-Qo8f+IbvD54y*{hN%yq@@6%)Oy{Gsr17-PES84Cr@ z{(WpEI-s2;KfwKHiagP~KAIB-)f&~QnERDmf9eYKrl)zf(& zl@vIlS}(HEwl{)j--6AAw;jVlvr*+M19UEZv*%BuO=|v$cIfB+edASbvE9I>3n)Lv zGVXKyn%kxI6XjN67Ho58KuXFRMpTG`3F1wb$w=T`{pKS|C(Rbt8Y7(QKY1QK0dA!m z>*R9A^u(#uHooQOy(w@>Uy3!KENMwgD@EG#U~_J!Jrcq8>=R-v$U@p`zu`m&eIaXGvs&4PC-Z7GC|K; zlObA0L0nwi+Ld`!<+`P;4adev`h!j46S({3?`mbT4T!1Ai4MxRLbqM^-cHAm&Z<2rc0a$GmD zH!+7(I_VBD+6o_QW+zp%MeTH^#Aa-F!pq2(>LSoPjFFy5zC-Y~D$2DcH;-I~E#|HK4>vkglxpiMTk6lw>_e@e(MvBr# z4gYSSO0jtp*7DhlQ7+-D3tSt6fKt+L{oSFpMuHZ`(vQ9n zJr62S37rRop{W=pk0QU2Rgk|g4^EZKwpw(65)#$?N0av_Iu((ucF}1Vx_Rxu0OfCgAQH>x6|I~faA5bmcRe5xy7!qf*}c{E(~JC@R05rE zQkjtqTi>yH%U4dPJNHpmqkdfpBnHMNpp>nd$?q|davvFBMP5~!(VN~2k!D6_*_y8v zskm!>p!9X>L63#@;3=ie&wMW8Slq3Gp z-jmm{bN;L=(vOa~u2dnkFHC6m8S(a2(SwgxeFo7Y>sK9AMa@Ak{tmWX;^@-I$Kz>7 zv-~mEd4t6!l2-Dk)lV@7%Q9EvV>7+o-&wGP*SRm`gl1jOGT@1p^;r(S60Yl(EzNi;dvvxVW+D#cAts@Y!4C#u-c!fmZm+!x{Pxzocy4RG|7#|F zlFK)5Mb%0ub6uQ=-4XaeCS{aG_8oCyHG#39;b)74IQtP3_WxCX3zYaSr~BxeBc2 zDM+oTWxeg62f#UO)*X|qt5u2AWbJRe{`;b&ez^PUc)sK&e|>NZY`ubAQLDb0?i!|?R%*`sgw zigb7(mvr~ee#=@$E|)5|9;(IYfx6MSpi+D(PUVG0r2w2pEbm>tj>lx9PY@lm>IK7E z3YoalbUxF3xtCriL$rzQH|+*3g`58*`{#GJ=GIdE);8cB6ZM{I9Ah2YO;Lf}iLyRP z&@>rh5VFWx6J~mjw*eLMk+fr#@b^K&zYX1*aY88y5nXp;1Bu9xgmaD8y80)Xe;7N z7W|3Rc8TMi{;3O)?^MER}-r?;BHY+U!fF77i;VwkIQIyggu%hCSFZG?z@u zhWv(gl19 z-Qc+rS6RxHr!rn+t9%wz8gLNZL83J&v__ARaITcY*v*LJfY~o}CvMHgXhoFz?UhIm z*wrtpez5&|&CVV~zk6y2XRc-sgWLrJGFkiK0%}%mp`d-z0FtTma_bkobW&SK*IM+* zY;m|SexSz`%l=)%^o44&7DBlHb2isKRL}$Zj2S}F(g@UGzLcte1YVP?p6KPMZ*;v>{n$wWnf%Ap&MNijp#_&<+e_-3 znn_|Rxb&xTbOrBUV0x~n^t}A7Jg8{0=u%Ps)#=eHG_q4-tlbyEXO9$eR@M#szZimP z)oIg94$Cw;Efq1kC$H8C6!PJ#tPLIuFm%O>>@higoL4va%^!|Ce(%+3;7Z@$3HSt+ zo*v%W9TQ{QZocu>`RF%{b$mCse=v=*)*8mAg*+7GIGGxhQ+41PZf-zY)&5qLJxDDo z6vY5m>iR=XuF+vP0e_8;0bmPqXhS{vN8t$KxxUBfA8=nGa@lCGd2{ww&_^sH!clJP770+G{9k zB90n{Q4mj4@UxV|ksH~OX?*uiU@HRfel9L@-cx;TCrWDV6lKg(l_0Bx8{AzoG%CSSh&5^;I%x|!NNOq=|ZP=!A_ooFOg7_yPkeA26Zg`?28}o*^!Sx z`gUbmQ;?qEeHCIeIn5ECN@I3tWbV_(lAW$u%g`H2w>o5bJ8f{|L@D-WtO7jx$$d?) z&(7*C4{8;O`@n0|%M3RQUXPB;)IW0gUYYvWKbW?*oXh9(UA!WiF0`$8r}ne(TRqc& zonfzQAwcm0V=$lDGYDeEJB0Q>x}pE0da)fr+-=CQ@687+4iIgP=t7eqntntk@n?h1z2cgaU|Y zl7*K>%PR(K|4YjQ8DQ6^%2FBtTA%$JDsEEg&=Tk2IGEkrzjHTE4TtI-XW;YCLcz>E z_V*4Nc~Y8XYF`RJ3T^h?-Ly~#7BsKM98d)l0Yw+SJNx(ZXXHPTX~c{s*APfS`N@jGQE~B}GawAtK zONn=4_0%9wf>406@6rB7Ye-1xb5(&YDJbEiMTw^DfhBx)qgCBur0CWOtTnp(6#H$S zT3a>R)fEd$Jv3kIL!-Q7*G&=iFWi&A`*PNVGLzOajiQ(5`$pkhu?FRW{YSX8pqmH| z5Iv7Wv2FO&9KnEi0 zs-JiN1mFHZ)>f!f6H!T=+J|sbzS}h-GOD-7^|fFZCiV-c5G?rAI>PBR(e@fB`VNzM z_YR-(`ONRUp!f4AS&$e=6KNb&T=e%*sR#KDscTHP_%o0y0NpCML$i;q)!8XGR0BV+zc(3&K}7`Uyf=UZ*{1&9T{Lb#$0eb z$l)x3QAy#{tsADbeQ0(K;2OD_zVj_ESzWaVz~t-YP9k4d3yq%jECBUS@KlRU8{4*L zOz`soZQ*egg|v(eJNyl5qAHl><*jQ5^X}3uNR2dEGu*{L3$Cp#fjhEA0!>r$>o%WU zgP{dotc)%iH(OhTGReG-`OYliO*1-=d5JQ6<1_bfW+{bt9&L7yD%-wdAxyhyz4GFb z816-y8i)@=5##+$ehBDPzr$E&CN7RzNItvV9DKCs-n}$0QoGTY&@>${Z0z9}BF)NX zV%Bt(BR>kX8;VCCEijB55F)BTfyxd$-`t;hAYwQ~t=}`;?TFEmwM%8;a@6{x)IZI1{2u%6>)7|8Xw#4P>r0oH z9Dfm1kEg;$mIEZ=h$rZ7LB8X{@8_7NHKc;+ZmUmT=J{RyRB^&YG0k`Ry6Be|zwe{q zq-2HX>)sJLT4~O7l`VR?n&PdA|C?Q$w^eq_;Cvrv0G;BKP4-|qmQDlEm8S;llly-% zJII6(WRY?v+c7DeLjgfM5Vg|>>L_8*yNTfJuP9hwtU&uLzcOVy7fSTHyjr%WAC#M_jJ!mJbyWqzKie>l#7Z}v`NxG{NZxtng70vS{`nh>91Wt4n*%nc*_Q-;&E%LyMob zQGF4X7g}_{YO&?Q5<3b{4fB>)ahEdxUkK}&$Rq7TM(B{h6^sQc;E$)k530#RkG(vr zg{#AHt1$iG5)Nh_@Kt1h+C4N9KSUJU z2Zyj}Xx5M2<3NOyR94Yp^rlZS(f3{mMGl3-q&@QR6#e$~4*oQC_Zt1vQ*WF5byv2H z{<({TuVX9i&UqLHy)cQQRf(Us+Y27(TwhN9(*saQC6MLuv>^NX0{z#A@?*vtKOZFq z(d6%4$XlXy@FVSD2-;@IX!|qhhcw5=@T4_F(wImYxhLbSU;8E1|CJD9CAUw86EH)f zVX?8e&nCUg*jWmrD&*SZ@0bs;ig?PmqePpK>`)jjjMgaoAh1T#8Jjr!%JA?( zd3FE0iV)uZ=-VU}OWK`yX-u?mp4T7&}ljY%hrMXQz8J?quXt)^Y;FGO6 z|1*l4gtT7xl>*A3bK9CH6qd~=L=kJI#Q?3%%UaZ8mvsIF6*%RDdQqnP$F1p~z?Wu* zY~N{*rPIUwZ$5-=H0&V{)iPoPt1qjHyO^hh?G2tRp(Ker7=E~1XjDq>k3?E3FP}UA zOPR}uC#g~36*7)C%vMA{nD8(Y;=t?0bH%&OW)<4|rdfaW;ST^)=b)a$GNib$CN<8( zSA$VzPXV;h4!wTdX;O&6>#hzzP5jWYdkj~bgP-Aa_s(p#%uBeg21&bZU-wDKM6%^rt$nxxR2d zkk{#hOT8~==vh~N^4UiWWg2Zarx6SJc~p7?U=j%YBWiy58#I;Aavw3yGPP74CEan& zJWTYOefXky5_7&fpXgCA^^4E*3`wWwuKmz8_*o2COTEWZ=CIO?>0v!cWN=a3ZfFkP zlTOs&%VL!d7Kr31z}*Ev9$FL)k{y6VGRtf}>q}pNy~eGfDM+~f@uSxU9X_A~L3n7^ zDS{IDcv+#*J8l=#dpo;WmwVVLRYyPvc_irjJmsC@AC|F26F5n5s8R+LCS}~Aed?!A zfAof@W*HP3LREW2GO^a8{hSFPwc%uSx?r~ulXmnu1$!EI&adToza(FH1;DwnEW8rj z#(yB`L!h`Qb1J%Yb!jw54WKJE+^GO&XUxg17S-U{VJMU^*5tE&f+i2Wzj2LAj_zo;R{s zZ@TDEU~~hCm;{vy1tTgsNiAH+yD6X?LG&l(658)6m>t%r#S16WXgi}xUht29 zKp?*MxUx3-Aw%}lVnshoS?1$O!i)Jn9}SV0(_3g=%7(bEw^vn#h&)o&4&?yQtq)T{ z%JnO?vJa&SebiO@KFPD^8NC1sN{iNC%F)g6w10!-3jgQU)mknMv3`*_VG*8dIHgsn z%eB9VjQ}&rlJ))o$UH}xE(Y2|V%dky5cq%^)v)A6i|Tyi!oK8VkUG%rdPZ$KURJza zv|c6uMJbD0vvhEzXtK$*4)K`EDo_#0!0J@+dOxA;HhHdnfBz$NRXY_BHGcny)Y>pA zBsBgl>}d$iXpuw^wu>h?N@h3p+UeHa`xe>gH9vvA-^4cPc`k`$FM+zUJHqlZ?&w_# znszvAi@twURvCtjDS{qTRp|K@03!9z$m^Eh)^D=1-?r>dxaGwopEe0n`{i4Qg#-|- zPTNIO>*&`!@28Sj01_bMHi;f~XnS`7pzzHz5%8nbf5D5z zYRmpDN#UdLNo^4Q%h*mugiZYcUE=TNUrwttS4+EEWvhk}+rHtW7Y*Vd+JSFNGe$ut z(WiM$C=O7V0**f9pNN2&4wpEzOPGPiX|qz0;1_G5l(lw*(kI!qk>9N0vczQhm+FEW z3J(s2Nn=s3L2i8*=gQ`(;!XBeRlOqlP;WW0=B@p^Xq`*!G%~L;uhbe|9r8g= zy`m3XbGQe^Y_F^QG{rXuh(3<=9TK(qHMED_;8dKsKBjxf6FzijO^)16 zs2U)rL8BHF!SgG95wcq)KXEao(2g))^!;qX0MBC(-oSkhj;}EC#=X-Qm-?4Ji?Dq1 zUilNUb~N-}Bn4q|+TgKX(=*VK_u^@J?Fss(+a~eqgID?LZ*2^u+4EY{ar-7d+HSD{ z*=klc&_xNLvGF%q3vd+^+)p@jjN4eAHU%}U#p%3G)io7NNd8PMgz6TTvW2bB*FN&D^;{*1xjlBR^;ePfuKFc@8V|gfldAnX; zypnvX#zGLJcRA75PKv!X%v?Ggir^oxYzd@oae9Ba3@Ya9>?9&4@RHHW1QWl7yS@H( z(5=0|(svc4Z%v!uB48Q1PvZtodFejOw3cWQXA9~xqUqquX8#E1>(3Mwl%@_T3r+*8rl_2?2gE|^DCB~ zCywwA244P1%{K!@3gQRk^Pei8H44dQ9iXC*{gOO1f<6KpS83sAqd-dlbPzdp_9O;A zQxU79x4HoSK3egqvGR5u0gcMVROL7)WLp=0yCXs6Dm|^=oS#}Qi2(QF$>_b6iw6X; z)rSE9|DaFxA?d&amF#x}*O%~#h(=4LOP$>AIp`Xvfa~;_ZmDO>HnQGE_Z~b3B$gGk zLcs)scyT&Pwf2rE(?pF&uj(M%0coeig1^6 zI7e;ZE4>Ph?RiGwmP6+B_$^lUv9W{j`uBC zkvhmoIH{z`qsp>&E+5aCcN!vX&6+k}H-Z4#O#se;D@98TV_7`0Yq(PqzEVwcKls6h z;S9A<^mH5qSxLyJkpS(|f@a~pH#TCF5+?9vh%Nwb53bgpgD-Ikd6Glp0gV2P z+7paT%qtG?Bn1M%P4k>R=revIw=?S=_ab-)hUq7H$LxB{&07gxML1h0Tip7Om=1NV zK3yLrvaab}tv#7l{S*Y0wN7QiDn+<)5U(Kw|FXa@EazDS)q3b#ca+%aEm5n+g#HlMZ)o@6F*MVO*=euC)2!D_#AbA$}k`UDW7b8x@DYMMxWiwCb z#~R|7Adoy75VyU0&nMq{PS+w1vHcEsME6GMIORqss+PCq*8HgvE_Pk0N&zt1HmS6P z5c~w(=GW$27pO;{FgdNpB{|Pg+2i$itU=G6lv90Qhnabefn~5XA@X>zy~nwpvBToss+|6{emKr;ZY)WH#4em1$i0+#l zX{}eP(E&D_gBFQ3Zu0;JS<4%HCihTVrSE%g9A&y%&P>!{$)m?njyeQH%e0ATYq3d^9=j(C)RAfpT!5a%Tks4^);$HQN()sCO?20=nce ze&zvmL(RH4^-X7Zp~LpN9e*ly^#8CIe;xijI7IaB22}agy=I2={RDh(%LQV{j00Qh zA%iHDlm3?nS?nIdYIb*uaYu3zeGsk$#bgtVqCFrQ<=G|D`o{07_+y2s6cc zVb?$$fXGf*C;nrQzQxrEvj$R=44~3BlHZ{!VyF|;|Kk4xgRNQGuZlw0K3NGio6iRn zW3k(Q2uyR${GV8CTAi)qx-)-r=)+Q{FqT7va?eFk!0~0b!)^X~dJvm(ER~n;F5Tc{hISw&o9wN_|Mbsj5mz5PC(!3<84ybj5q=s~ zR7U1|&|#)>#^J{y*Jq`g`DTREya(T^5KIu`O#%3NOpSgiuFj)|B3u8U$-qE^!m@Le|&2PC~)e|<6!6=^>__Fua4mi@}FJ=9(I zf$7*Sf_Ds`kg>QC<$DCMS{CCf=jL8NkzM6TIRZe5!g2R2#8+aMwkDILittsfL^B5< zKy8+|;$+I!8^o^6?k86^WG|pgTW+FAN&R)LA^mRO{th@69X>O|kvBLQ3gE0xOZ zgle?zC8yx)y^R4$dn_ODeI*d&de40Y0|JZO3Iw8ieP{;|P9aKR^Ti;S(m$b9yq}$T z9CB~T4>=6mTg8u6s96L$K$ag+PXL@B=jBH=KiD$t8tJiU*b#y=Kwh4$rhIL-!S7em zc`%CVK`t{>39RW;poQ-yU)m*hb7qcW7}=&y1E}mh#u5R3J#nC@Tt=k3gpAZzg+%PARhdK_`Qd#T>S!0 zF#y=6o)Aoang7}>R;QAGV;XROg zPnK!00j@rTStB?1aTjQj_R86NKSDc%>@|$2)BlI-{-DZh#bj`SLX1c%2Ieje62o}^ z?JdPI@SX!W7SLOII8)(r{D00XRD*^KvSN19YGvM`)@sWTIAzN0 z^WGf(6Oe1opl9pvNjjbqT@8*r*aC_}fb&<4@r#U?P5!d_TOT#-6w5Upeg)AEyYV^; z>v2nl69Qq(1t&9>CoT~l-UqKRuk6uIf>*fyiu(%T-!mOnD;J9=LJd%2vsg#AB1e+?ul_f*2z{yQoS75HBSKWT&$ zrq09<9^E~{+)(Wd!p_~MhE#t{d`^waV1LA4$-MmJO5)IdH$a1NYccqD`m<0BnB44t zxYDOeEgF1(8YweFZGbPJPW9W=;s0numa0nX-#xIxe?Pt4r$&Rne;a?#Ur;|z16BQd z?K|KF{QK!&>N)lEY1sd&_y6+&n{z@~pyfdj)k%=_%1Q6>dwrHQVm(;S%a~a``FA4M zPL)s(n^6CzXa2jg|6crmJNM?CQs)F0`u{B6GIZttPZF^hG?MJD396I&vaa1z4H{7r z`NdM+Uq&<)PuP!?tAvecm{i(f3lz5EoVGY0P6VX;lGZR@1e6Pgtl`JvSfMBQ2sfXC zt;$>O%Ov(YX|4`!-hp4pH?TeiXYAa z<5Xy7!*B31LMVdJw>%2|TN%l*2(vCG485zQOik|DPuhO8`a0_tV%_ea*Q1Q9TC~i~ zEKxFrbQ(QAZC5*Vv5@H%EoBV1WlCat7X! z^;Q+TxgLi@A;x>vGZ*GbO@-#T-CS%?!>Tv;aFJbD4A#Qqen{k)E2qvC+I0U|>;CL~ z?ZavD3vS`1`Ie4mP3!l1uD~O7hl{LYgqw`T9r}kN+TFf%yo#5gpy0bMAhF7D+S0T^ zEn*FOusByv`7@hy*`Kr-;=}Y~Z%RE9zI``5I%Dpt2q6<%m2B=!k%#MUXs(EDj#k6U zxG&7~5PF*{h!5bp$0D3bQG*TdE4lL;k83xp7Ay|vUt_zK#8_v}h4#}E;?26jS>#mi z-iidUt_g184134-)Dt*EM`8u^Rmj!ZE;Xv2*zyIQx_vRV^yc7I=z6e5$PCh8DH`47 zhX?X$nBzAV0xozl^!`Yii@j(v*0|R*45v6N3z*Yhe7R|$`yf8&e%;%C{h!yH4I37w zQkR3gXn0-LBZ9)bp%^Q z9ke1XY+)t+Bya3rRlwzW)mh?FwsUoESFX+u2w(kv+VhWgN#vmrcP!_ViL$h`520V; zvvsf4i(Tl{G}L%AKI!t}a&EkEMkD9X>@$qouNhyd){aL7g`Z+))PJwa@?{|N+||{& zJf3&L8gG<@YN$tfATOi0zeidS$vKiQz z2*3@>p`OnaK)o7OVhG6&=BcE$h`l|mD+KaEPQzm2CZ5H5y=m@KYFFbBTEkk>uGRKBv%Lwc_b(&`UPcgkdMTW8TG?N!yUh>n`oC%lw%S zORm!IGVD2IC6v$>MmX5mbf$$h00R9L{DX(kVD#NTIh`+qVrR)pj&gmV}rl;FZ8+sqRX2ERkVJGl0t2>!7{AP1CXlsBTSc+TcbuUiO zSWmbcaXyJ3d4%oKD%S9HXjT3tbtsBo!_UlWdNq~+e4c;23L34A*Hvhx$wH+)zQj!B*Y0B-N}G1~XCg(6D<8If)3hI$ z{l+B1DdCtvu9{&GkOsI-1^0DR*R~667XD@I$knoTKvm2DXn%H=a%A&nr(n-^o-fAK zz`%2*xCR>)tk+EViEW~9mfK`^Z_?#(f>$5yCB`d?Sot0tRhv>YXYkNoIefp%h z#fuhNBQwufsNH4WOF-D7`obIHx5X5~Z#U=g4MO8(2)<|;&9&Jy$TPQMmYAm=w6Pu4{6y6b$yR`gAprMfc`OD*O{v}*Jl;<@$p zz5wX}t)TH>W>}-4fza!xt-(t};{^tS@O+hXDI2-!iJ1ok@7wmL#{IW?ti9(tW0Nc> zNx>n>T(KQV{OSp(D^1>&mQOn%COk0}y|NCNULAXDl;?)P}}JRm!^k>X#%HX`1fzuu0;Ha|^2vjV0ADeQYlr zwe{b3yK!GSzBiS82sV9d1SVFXd3Tm1xNW06pc^y=q>YQ37TDWQ(tIbq)kNCGf%T`P z!ca%96wJ+(YEOwO?(2Cy_$Ysg{c z-$T*Z*v^>1c{v{J;ZfRLZ-HQuOka0<#O>#DC>NxbdM=i+b)V4~rz<@+Pu?sBd+Rnl z`aWh7ir)J9VJfM7TI7-L+fw#kS>o!)>w3B=St5;pzBR1XcSVTVmD~1b`f7~;7Bhf( zg4w|VwQ57>VA7IKV$oyKys9V491rI;-jt)zEv3kvLa`Qc25kIJ_2o7f4{PBVac;IM zA`3P{aUx(X7Dk@k2@p9lU0-wxaN*4_Y{5|zvqj+r_3W|wQ?h(5)=d1i! zk5(^*1*(NS6M0)+?K&J`o2PbAGG4>YFmKlefZlzkOq#@%u7OWkL0a&vI5TeO!`#&`RA?x)PN+>vtR+WDhSqh_}ram=XH zvUz0ALGK-ImE@V}FcGi#%hpIzlAKMNY=Cg>)N#>OeFK9;oEpjOTJt6;#jrR;CGF6? zbg)o%t9bAzJpmgf_m4Pcg665e&vM4WDGoD=67U+b;!lWneto` zpyZ=4bL}z~5f2{Lts8$}Lze6jSG+_}$Sn3)vAvMyqam!8IbJd;wOe0Ild40L)EZndI=y3zX&cI+`FzeSd%BM&jz zHpnvdCWjG*XH#Eaar6n>lGczl&jjAAV1Blkew^9?rYX#U-*G^b$%4Hk>sa>YZ8cca zv_tOEW_5fWQfPhi5oGA1f!c5cPinGPYo+{rgl2%px=|{Av595`JubT&SeF%w`y&3; zh2lOoZqaO|e3p483NPKW?HpLx?eS&hLk%}`*^~VI(G`qgiRoCb7EJW+({HZr=}%^hDP^SZR8w+17PGoArodnDI<+!_Ze__n%+q+E11t$6<_ z3*bD!k)T_x`TJXib_q}Yg-D-(k56LUc*AC%$9xTW=K6})EEZG48-P^Y=K2a`Z-lab zRIFYl-t$EUC){y%7wB!v%_husiuiO3*&DwiCS{Gvcu-K ztxAzxX0yTptv`TX#$)yU!%y{?afg-WXN%3*6UCylBAnfcFcAa#gni>Hvt% z)4I+4+|=9s(Sjwl_X)?gmVhhiW;KhL_XorylCt9xjng-gXjyY>OIehmgCUbV#T*FF zelp?ugBTyZjJs>B6M)72JW?iyCPl)@*weGB;iHa^3!UP@85)eD3SK)tH2Z0_1uaR? z;-Is*W^#F*c0i58G{#o6KdszxjXu)xqpc@C*V)v%r(DSEWcF;#jONJLVV7Pnv#?^M z6B^N8=AZW@CH#l{gm(%>pLInsPX{4YL)qT8Ev*G?MtQAf#XPQylkw4))`o#O_Ik_L7o!aACf(C|O?4-RZcl_mxz&u!`)fxF1D?G%@W;!f*XP}p3 zq?LvDhVL{~RQrgw|4ZP@AgP*V3pUdj=cxGeJ4=J@tR=B5PCkA6*X6UhEc7OoFPR8O z%+n9)+a5_+UEh*SzGh;+I;P_g60wL~2?|*}!2BpoIm7(pK$Iywa=>Hc&&<}oUV@v? z(x)9jnK@bUO~sebo#chSFItm27Bw(mjXqnyJ^O@PA^7Tr6wIW*YX$q7t6T0-^I0gZ zZj*Bj1yc2yZH9Pm_~ql2ecn5}f!7dS3ohjf3{8iKG6iKCJyWAR;Pf(hm~SUs48&`G z07<|%me@uX<xyEStNL9}Q>sl}ib^nG;t;P;dsfxGIy7YNGj}0CC;bk3 zJAz$wexn_R-uQ8eMBwkd*b9QJVC0dp!zb~gkOtewJsNV&abTZ=ef2!#>=9dRrt2f} zkaaIO&nbfW!=@Lo5jsl{;>CYnRjS(Hu8!)Mb#bjh04;!_n z`ajgYXH-+|+V5+_f&z*PiUdSD2q;wu5D}1Gq^dL(ks3jIO%xOnq=N`Z?;U~Aizw1N zgpyE{-a>~!Aj!Vt^Q^Vkd&axg8E238!#?wqW0)Cc?s?bi`u~3yG|DPyX1PE@`Vbv6 zc_RvaK(k8RbXxs%HL1Prm#t>)x>Mb?at&=A@TVTNOAC&NaAya#IxAE-<(F1d;!kLPHRzZr5%45>SNESuN{HI$Z%G0qU(W0Y^^?D<*zIo> zPoDuJX;4LEQPLBrENtjbY@E8Ch54lkeZc;_J-p&PM~3|auWunuJ`Hc`WiSf+>+fM; zp*sPc^1z{n%O2w@AzHm|*3)Th$z##W2Q*QYBr)4|L(VYF!$ucV+30Jt7X8h!Lz%Ae zlXp54%D1>P?9J#mz`uQckG4LYx5qZ)VG1S;>J zVR2D5McCjX@kOT*Ckh|t9&zE?_1avbR}$holO4zaoqAB0wWxBizo>{5{va28P8Uyz zFvyki%dSVu+SrbdHW}YZTYOPw9OgHh=yz1OEZoBCw={Xg#_h(n&3BQ9{^wP{utv0o zwJndFO&!~RG?}W;Fup$}6mY{GY(DG@yt**!>=nce!cleHd+UKrMVLz#=DMCCGr<*m zXZ_~mvQ~ugFqm~5hfUmaa(*cdbfWAqZqSMIs>|!^@sEa$ow~rPEep<33q=V>ISX~f znJlB{6TP}a(@$2rENALmwtxv1o)xOqj?eWi%q&favigW&<0#$Tfzyp|rb1K}+~kIT zRn)k;6sfh(G+ykpO?>LfaQ?oxDH(Oq!*(Rc87 z(kobOvRAsHH$57M4|sF@R5gpsZEIwg>E1A}VGLHldge*h{FB$`;Loo^{m1J~&?onr3QUo=3_eek+qzM_Cml zaDZMPyu&_8K=McyOPBgxZi8+wr<3km?hZ@?>O#yQWyCfdpq|R}WawWwps8$O@lw~g zJ6j0RQX5@Kv|=bbh7K@}m_qylN2nYx|Y{&1GFA ztq$+$x1g>$;U!&X0|-q?h%5JkPino;e?=ITU|2MR zhA6a{>Zj&Tof9&-ZCROa;;BF9%5yKeJjW{mKo-)g`MCKp=Zy9}`;$nhtOhc1O`i_C zA*@6zF8rLQe|>VO!qa*uUg+g6*|#)4_V<2^0rr(>_Ai@c^~7U#3?J&Qw?F>4e5CWn zPqXtse=ET#TfI`(+y$jUumjs0NFs?cS>zQKYPwwhT;B~^#iOdaR(@*k=Qn^p!L{&y zZn4^v2%O94qc^hFFHw~fJJnTet;Z{;uONI*l9#tjE1}W6+tdr^SHXMvJ@vxp0MX!9 z%OoFzca8v9K%xr61c6tJxiE;bRKod42(B0Dzr=o>1rxhsnl)tkWJiR!tgox(Zx&VG zlUeke;MgH+ySD+3JEHC@wsUSz4COiAun@ne;)Ut z4-F_`Kl5{~)3TN7W?87s*Dh~OoiT)m3m01TP_`aWQBMu?k(3j(Sf1B;tHN)X5>eored`j!>4-lOa z&WZJ47vU#Z*WDE=v-?@9j$$LUEzt8-?JVk1dyApLFrt5uXxs{~!MOagke_Zk7LPgI zy6Z1qa!%lFk9Q4O16FejZPswM8naK|JmwR^HE|0b22t{(X1New#!Igt+F9gB=>cb_ z()(V=WbR!wsb>TmqpsKw67DA-h>Ov6e%LW=AVeRu@dYD@gwYfQ><`*#dLaHmE=C9C zE3yVW@CJJ%k%;@F;DF@;LQc6+pZh4=B$68H%dh5B4arf*;PI;a@^msY2 zP=A6o^o4Neu5E*8z%w3_$m?JbyDXPK(|BmrVjt9L#;{+Ew98*4dKjLu$DV$@XK*+qpk3R((qsJz+oP zS^6?b*0cZVrCT*IociuJyikZwnhLlG(q~68a(EVwDnP!2XJ(>7SOr)z4IpP!AQ%BwuuehXMef7%U_)aGorzC zp>*vDtlQsu>530wyYD1fv@|A{QTMMz*r=ENzA04ik`1rycCS3&)yyWVb};Aa{`~$0 z?CFLD(L@4IYUR7%oJm86Q4~S z?ydNJokh1e zw7ktHQb!5%f@pMAgEzm`2Ly<|@aegx2Rw_3ykYoVia)Q#Hjyp2TFt~PLA60=a=IBU zGt|~Y4t@?$KCV1s3BvmFalbw1DW`>^>Los!J*Sfefv$IDe$sGO88R`iO!#$>O03@ih=isqT$Fy*s9(2oTD4QYqSk#aCdmnjaL|aFUS)UTSQ%`R`ANXf zj;_hNcLZ?_>|`CiOg~I}i?-z#(~U}l`-f*O)nSiNv0QOlkviP_Lq_&HUYik1->)Vi z!|m&~4n!;$e9sE@Ry{hcq9{v7A9@sly0;a(lvrLN>mUx-R;dcrRy#gYV={Ezo^62tkIB`ZHgZ$caR z9-()If3)ubwXNi0Pl~EDXB{TN_3*UVuO)cJK77D)-Yr-UnNw)8EUhG-ED0jocb^Lw ztx80mL$5-u-p7enfpA-Tb$^#N5JnqjCN4Lyp5nAPIEW=-reXR53wr7OU^*2VyZtTX zx&Zn~?qiLgc>`HC7~8@su7$!N8_fYDmOBo~3C?eGUqPedkgG**2#HecIru|DuF-rz zAg(#`Ph)xd%wdTQ8|y7yHMI5knBUSK1%ufa=xhC+f#7}hS~C?Mu`da`&jr#=efBxc zrsp$yLLVv;J4QnHu2zG6HYG&Zj;9Q1vT;=IG=_@tDmcJL9TTH}S+`@55G0oW;#-;Z zfMEUJGm#|`2@}9}an5>_y|lTw~ekF4~Fz^`Swp4*t5xdzSxkDTG&l)){HV1g0-% zw?jVj(hW?XhQAHlc8V`vERiwKD*m#7eT$!6L$Z@D~dzKNGhwp5`BIc5B;v-x$9@(EE~0O<)O z)~qA zfF#d%d$@A2cGkG9D2szR-SETM<|qoso8?+P79#3OWrMaeMwx>nD$q(A?@!I;HXX!O z?=JNst?-`6vmfi8NQ=HYJDyJ*ecMojeHj%`mfea-fApvZ|E)xFS)yGbB(eXJ2O^k2i4>4xwSi(PXbeeKZ+fZJJ-#=2iVj!7-g+*Wj#ZH-nt94r@B3`l zOYdOjn=ori(q;d{xFE*OVc53|CgX5xnOig~0_Sw=3xB<9dxjYy8)B_Z5hG{{gqE@g zo4h02C||5drg{QWo@=BII|^b9Y~9w`Czh$gg6IQ^`t*2UAmr@m*&r8GPUEe6xddJI z4ib=J9wc4sVuf4AQs$g;q>9izK_7v5RE4~9tJ(#=W6>4KCthr3mB3?qbpG26eYSq- zE-TLA1ElT!cBqIJ`<9aBCB2^Ac|%PH8lG&By=<(T2cZi3MpX0LzZ3NXJGM}Xq17D0 z4$&$ErFFWJLH=v>CczZSK z-(^a{re7?L5Bp2in z!du7(PIpbqxj~!SMavA5;6(OC=($z&Z)2#h_obAnN_MkrDJB5Tlr2!`SauG z2USrZ1731b4`jecRgjbnxTYZPh9&;ZQZ#lRC$&yWB<{W|h)x4K7bTQ*x#9b>eCzEf zRYap=3quQHW#P7Ky3XaoBb^{#&_L|5V6p=9F$kf%gXB%@;%o-kV}Nu|DWuJR7eA-; z9uw}(|1MQcH)Z8Hnj!?@G8!HnTIC`=W{E@D^v^a8bvag!AIvT(m9GYE&6rTe11x^s z4H^;bSc@ot2jx12vS9O73JZ4cbG6*iV{WL$&AMxSCT$KnmHJ$OuTsPuA2|x%wlxYz zv{^@;6JW2Z2-0qwz-)}gnMk^%wHxAHvv#0Mx_>=&_T}mYynTh$_#p_^T40NBLh@n^ z$aqGwjZ(=27vmZgBxuvN3VL3OhC;bq}IdvJx;7a&1wc7`BaIo(>mck3wDw)uwe^Dm77xPnY{-0AE7A>a8zhAlj=R1$Qz6qq1 zfBh3E8c`fJ_%*D4>5q_PZ@cmRddM;1AC0Dj*0d7&Px_ZiY6g25a_>Hu3iONcx6^;Z z{$}s7^1COSAMGayTd&F(J36K$=8-!MLS7UKFLO5CDX&HL(01BK1`k?Sb>xk*kA*Mt z(5f<(=*I@Xor~nsDDk_abDE*(OM}bsLC~bJ!&9tp0Uj$WJ*H`EtIh@Vnk!n`h-JBBk68wd2RJ_&(E{K84L|+UU&X z4+)|fFBoA3*CDjK{Yos+kdpk=u<;E4a&4*`0}q&^M4#thfcPIJ=8BLS<9ZGRqcwRmnPQ?3e%D{XzGSftG*wO+Bmb;Z&+vyAm}wT=%d<2fcT4M>h38eE7a%%WyyAF~8ks zXH?CmuMHlozbcNA-OOGIv$*_jMNq8l9pCxbkg4Q!HX~8`FYUoa7CNWTfrsaeWSUH# zc;EQ%-hQ9*y1tdSz}CGdy{0!W#12!Q<3Bvc8`Y0R7DkkBrV+$azPac*^GZgjG#~W& zBSp3vH5zydwL)gv9UkjehTyl^fk8z-`A?k0{XBW8s97Q#&wugXy?8}-ww(6BqS-%B zO>-mW&v%Rdhws*lW07)hr4@HAJU{YEYrYT%pu+Y_5u?>lhlhe|Z~<9v!;x!*{_S1( zl%9NRSl))qHj9h15fr5o$xslt%=|=2XXKGdDRS~CG__B_t7}1nor_}g5V;WiZ~HrV z$Y52Fq1Rvw6NfyGOZMUugWB422Sy|-SgX#&lx@)D7xHv$h5fibAngBn&98;~ zN<2Cq$hY{+rG9mlzUC27gkGJ*FN`gQl5ui06;|k4`IsTNRJjeNOl8?spzGY9+m77L z$r<=Qk(~Py(<@!}x25fQ)Yl_pSqU_iADzCx8;zW6O6<I=HRv3@ojP+MWPlUg}>5Z05S+IU&NZx1J4vF}eUZ zqwzyHfL8$OqP)mCrRYw;p&AM2(uh(}<$?OH?BG1R_v>J20Fa`|YeoS0>~_&UPeb6Y z;t{@6U7ANm9~0ZX2ySx_q1e7{;P|k*0+=^U6Zo?ff)xaR2q+n!>=cd%Ppl(`X@U6> z!i!5r14@4gU<^K_MHOjcmUJ?b}k;u4NbfyyVM#XU5YizBq$BjI+y zSeL^>i35PDERs{(uUyj$0a>yecqK6xk}KPJ>URs&GGmul6i_X2WHr}9#=?0g5ru*r z(|Yog0@vb+4&VNw7RG|6B@@@5nK1@-?ziM(OZ5Q=@Tiq@z*EE96E161r^72-k(b%%_u97UT~b_qRNWK$VaXlg|9#?QqbeLc&Sw3^hpWX6VrVMSX~ZQ6^V1id4o(4$TA-ZWNEbYUV`T8pB42>G*x zJZE})<&N{k?8Q~{P#ma~v-+Vv@~~^l3plKRKb%53b>%1kr&aP!!J{17>ds;FF|h+F z#^Mg|M=p639G=o~9*-PNbPUCd-l@<>`qV0Pm12FCI%I?D3HEI-xE3r8mG(LR`uKwQk%JAwB%y zjXdk%t8YG_dEMV%uI~jM%uaAQ{FNNN1-MJuK53doCx>1Vvl6JUzuWRMd-0w=HRZjj zz7I~@htjSTG(PD6ICqoSB5uAd3w^eapwmnX?j<5Q4MU7ws$H>0fXDLrX&)(b$>Q?p z>x=#)DW`W72b>iW0F^o1#>}y;(ij+{3j!ne4?tC#3`{M~>#TbUby&?gHDTe~!mlp8 zB1mZ@ot#HWw-=A9WFBt?g!AiUM~|?kM4y@!?KA`Q>bktd7vOsP0^TCv-&NaX?mdtm{zB|Z+z)6SWcg@zbQkBT=|I-3&mH)sS_ok974kH> z7Nws+M)^@bZ3;w1Zp&D^c-6{|QnUf^Y9x@rW!GTaGE;%)vm_=L-=|O{p~s?LgXdfl z1Jl8Gs&3(^r=^kVr^0|4`7Wu;Sc;f4+i;rXTO-?r?e%Sd9ZmxifTUjaAbeqlh3N>N zYEqgOUg$*YO!FsT=*t91fH`~MqW@Ci_~Lx)5cPX6iRR4I4Bx;G@&V{K1JZX};C?R# zI(uqKR@;5{fJ2O_SeA6tuk-o?iL$E4*0p+~DV4^d@=6?@l)h?yYJ3*Bny z!c?1DXsACqB9Tg&cc`kplzFG;DHvw01_bO#R-Mt;+iBc>QBf%Dgs|cbjfV_({aV#b zvIYXlBCikmHf}0zdA+7Rq;vzoyIS*JlG4Wr)tY2m-t1pFq0LM&^#S-?QB?`MRnDEb zMw6k$X&+M?P~hP?mdfQ?G7QRl{6M58Mg5xj(3j~6bbQ3om}2Yt;jYNvQa-WyFd2c* zXcj8R$s-?3UMR%FKcUSGg7?OP-j3qO(m1NJQW!d=FCKUpy0^7Iqq zwu)P^oV32wgX_KUu9DLd_&W3I&N4)`5Tz_yt z4OUr@dn&tj&_zZn$qKyN2^GRbG~~V6tF61+a;Yk_iO;TzL#Nx)^Q%2dq-Mr-77b~W zk8Y5(la=o$5j9YNl{KSiElb{cH+R`_Ny*2$7j7dS`vKG?5!qiX0y@~wJyM8uz_U7q z54t*6o}-70USPDon^z5{(S6vqHK$O%2L_dK^<9QO9QC8ZpQjp!v0O zw*p=j7<4slis)BD1Zd0Jdz{7gC->9wxleWEyI?5Da_(I{TC*Z9gxY4nS z7adD`7RhzgobilO)lkF1^#+e6S_UMmqV9`ycchiP#S~b|KLaEYPI)SOe|N30j6Ly* zQkfsyOjX^;~vjEw=<5aH4be`F#B#}DR)-H(~_jmIrXi5zN3kx!%NDZ z)vuaO*6Zq(Qb#bki(3tO`(4?6+V7FhG)&s{{G9qCpBGcd4HXITKk;1S=d!H4E)ce;0 zn1XNfN%e{E6@40dDSP;RywPxBfb7*rHzt?TuFXv)a_(Epf&80I*Zw}%ZJv;2=hSX* z3S(%p(s}+nD8yh7;6c@KIS2@PUq2~#jj7Bk_*c+&5rM^w`A(nx)qwkG<2^9XQ_(QV zUqqvaD^93gUCLdYfh*D4JHVh(zPsO%^9kZc317etL8WVXp<-EA>`v63#^Zenow_={ zu_#W$!~()v4`k<_!Xx5@Z0e}m^3Z%${blGf8Vc&L+Cwf$u1phkJvtrH(%)ti+TS{S za$)j{MYDDax?-Uw^_?!2QAeBt?aV`trrF2q*rM`BqXjOJ7-D zxzT3aGUP&Urr{tqgj2pi>NY#$S2n$~xwuT9=xp947bV*l$?wYN8&&c40^ zwtk$z>u*4gtA;xR93-`7whBbHCMh<3V@X$Oxj6`t>6EapUdG*8!DY_Q&EgB*bRiJu=%K-jAA0=Q(3e3U$(Gshm)G=V&gKmax?;WsTI7^ zsRp>?O7us=5tF4DcC&i%>#ngEG7>TEi)!|w#4R0;?K*2m4fwXG*Nmtg*GVkv+u#y< zj?I#tvn$#casp4^@TUsgpo$ScVfI+cG~0{wdy6M22(r2GaD5pFHw&B$t^Hcp>%Jb) z4PsKpnxMpj5xw~qONgq9mX@5Q*X&#d>8CA?*WLl9+zvV2l9j&Ckl-gO-~ZC1Z})~N zJgKNFC|p7E(~Rdujs|$Vv1ZfH3(|tRWhl@`Z%0lKx7r7CMw@3c7WQ4KNhso5rXy!> ztxy%TASaz~RRb)Ii2b04>513wlm^wPBZ}fW0EyT6oHL(Eth>#gZU^!!DErq)q_ugH zVIQ}ki+Zx6Ol=8)aad*A@;0!h3MHVN34ka#KYw;@@I~cBzHR06ez^G7LeQW=K)Lp? zUXikA%UdS>#cNZGy28ms)q#`36r72t;T8CxO#_9zgAnE$rVxXotOyb#s17Z)Vf9X% z&@a+2@74;l+n_n?>^MC0;XfrL9@}>h@rPPp5xVHdwTFQ}UE{o{+D3sQ^~gaQ zs>W0zrWr0y_;Syiizc=5*qgT?MHh9I&;QUp!&{cNWfGFM|Yl}asM^K zvj8`OQQ2J2i*HvQ?nTVtChNLO_YY^_>pv#5O!6wu+4`5>uF&d^O|lmlyP6Z$6tD=@48XGz1v~y75-H9+&|+kz^Y*~mVXMxJga)gw0Xh1r zgAKo@9&^EFwx@8XUo(p)iCRZMI{VX{kf}(hTgQSd(~!JA6$)%u*V92V&7tN&7Pnu+O2bqXRmx}#p?@}}KoF7%Rhxyo4IAJSR zpMQ#mdrf#v*HQS9i;OwwgFSxoS`|_@h2`f5rKF__>&F#VHBS$gXt2X08(bRQcSxzB zEYNSja}rwWu(P6|HVc6_gAKl?%eXJ7#5WeK#xGvCJ?9mKng$E9J0RMMJOFs7KJFM; z{^AQ(vDwtfLg|AajPY8*9{iRhtJM<)W_kM1WRHOt+$=tE#C(;=7hQ2~FTI?5+d8E= zITt(QepeJ7!n!D^VO6+K5b;~&i$rjg+yfOeN#@S)5!-y^oF&Y!Q;a_3f+n@&hIQmn zMSJpW6J5fRxV(yJY1(VE6KD2~q93`AUCdW9E3k+pq3ni)v!DA~;90YMESKXk zK3iEd+CqFb$@ zXj7@)P?2a0;W$C{Fez>d-<-(_-c7bkF4OkKM_JsE#JIu+NqgD)EY}#~^_|?0+eyt( zA`WXdke6G;AUdFkdb=bDjYy^?M-yT0dY=eoxi^f(CVss~_KWdASd2DdfFYJ&3O_JV zqC$NUHu(U!fq%`}_v}pPRHD38TvN~JRF-R$b5E(yWE4W(7ruQOk3Xs4TZ=&fe7+tp zuh5XaS(;}LLfbHe_tVo-5wqCBkKOiZw@nr2S4L{kS{^t|{c$m=0KJaIndN#`FP)!pz*WyfB0!fEq>0IHi#W zBeYq+sf#+7e3?fXWK2wUDP*mC&1v>x0IW(6-fN8{Y8GRXRXpw9PXl;mNOh>!`2y|W zD^SOo76~G56fbv8Q5@$ztS+H315U&zU{g7g8RhRFD)~x86_n&Z>ECl)5VK zIt(1*LD3x4Q%begcuqWqPP$P#GiG@U8836FhM1(=Rr9*-v`9oy+m1|XNEWPGTzc>b z_kd0OI#xIhzHYB_o-nwLe}^d+AJpg6@j4@0?-(;x(6AtB26krw5Oy zX%as;>W#n8wAdSJWtgx%I4q2@!tZ44ta{Zg`Sb_ttbvv77pdNP0^Vp(;sPGuqbDSe zqUOymUq(MoZ#hmx;Hz*1vzH&|rg*y)9tT&D)ehY>w|&b$iJc*j~F5UEO; zuDVLU(pPramyfCD1|0&F29V>nODM-F(+*-c4#-l=u*j91^F6gJn)ww}v3#6qQA{9O zgSB@6#|&xMD}{njU}v5M>)Fa9UFn?WRxZj#ApBkc-dVJt>&$7Nq6UBI)$kdgMKB5K@LYlFGjrtSirO2D{r5u2mtZ2 z)}HNObG=XIs4pCBSJ)AKldqh+Lz;HwVXN$IrZfBW{WoiGNu#xL;^PYw3pg0O_^)m6 z!Q4;f)Gv>j<=o#C-|KBhNVz`^SX;NVfEa+NE1o^J6#=^0+;S(CA7@Of!lJx z<9l;Tt)56*4I!&5i$;UcN6$KR%BHKwE$dx74}fc)|F4uQ;yenU^!^mqy&9wz{-J$JvfT`M^)|4-)_EdD>*+-fE*&3Mz zbajY-aE#)UqaG~(@ahKbrL02TlCk|kmqw~|Srl7~$+`?QLOb~q|J!?_Or_DH7l&VZ z@n>)NsyX`|VSddFFfytpy;jPqxIQ(_dP2}~?zo!5&b2V6V%GU5rMzq*IxSYgtPI>RhxBnbyj*+`2#43 zY;;Y6t(Fp!QiVC}v=qV*dq4GX=LE9P$V52Z{;*-E?o@IAi8V);;e0B5;7Ldp^`+@; z#_e^~Nyd^Dq;Y~86+q!05djj`jz+CwR57Tshm^ zQ~o9F8#jGxUYNy*F7V1oC8RS~u>aKfdaw0s--7O&p7K;DUueEh-!&`6gt**~2Al6k`r7fYSJlH<^?Yv+x?Z(ZSU z@TN)hSNSno6(Fl6Q<;}{elSM2X9>V10n}C%3yMKSLU@k?EZ0E=ccG)2A)prWX zdruGPQN?`sD#Vyd1dtH*um{BZJ`Z@A6KQU=3{0k0kb$O~;09{|C^EwGnhdWQ)%D82 zV(&a{a`0QudPW+Q2ZH{x@<$U9>hW^xuxiam;_zgQze;PF;jm4*;{QpTx;!nej$ZKY89!21nL z_c|R)7ht?kn3u`Yp|-x#y3w2jkY0X;IZF&sy(a*xMt5(E62(lOWRWIGt~ItFQm+!3 z_kJVKOxT4@ccnl|YcmR+XApn3_mp1n07NM15?dN5Q}4W)?|UR4+z7odnc|7(g6cKZ zwW`6*D!bGo6{sR&EXK)L3c$f9C9|#Yzgmyc%AY7gpS77q{}}+;qL`32tXt#pHv}s&c-AMue zC6j9_+A5f`Efc8oTS^OW?Lq7vno+p74RL3rMYp8NjPT8CX8WI4*@ z&-C$l{FX)?#<(s7*!|cDe(10^4`M&-to0PKoZTez<2wF3Av&%GYR0QsFo;59hJ-41 zpf+fj!_&l-mzfl+J>`rG+ib4>tEb%(hEl^Xfz{qyhQwDGGkefKNoZ2+X+ge4fb&dm z@WO+VS?hrStpDBBL*)#UH_2!zFpctB>m!9FA~g+71DitXjBwiblGy3WHm%Kl=Z&aT z*=YROWp*K={yH}w%I7lfUiPg%C5Nvx3+(I)N-nn8d$Q;cAgV#0+O8ZoJYDqbd?PMO zE1No>@Tu)NMF(pSx@XKB9s~M&b&tXFq>navx__LRFT-K)RFqjC(=PyBKNxr1$m<0^ zyb~ik8PCZ6A)>vGLt?00!xIy8J?D5|UjLZTQJTbO@3QNKV`2hVBSH}qLS#xeLk#H{ z@$4?Q9#H@bLc9L@$Z)p#GD+O%bPlF{N#pSf9f!=CaM}CxuZ|osl~3eUI1~$QhCMOpzIU);AS7P-i3tH-Y0^FCY8IKsp65nsR<7`+O6z6Afl0!Yy5u3eg&JVL(IfIjSI z{IFn;*Wm3Ezf+Z=Gv6WOpwD*<2o&re=E6=IHqtHQ-=$X_zkZT3M^F<+Sc|ZCB|(TZ z>6-RB_q)(LK4S?o$_ zACjkCr42foLRnWq(U9t(#{fR}3UWEZdeP68GMO+R2wv#7#g-63mXLtNNOQUrdte&3 zL-RulTgX8h#WOLJ35d zJD)d2vs)Mn_Dhk}f*`h#hdcY#W0Z^1@M90L;)}da(c|qoP}mMXEsZ@JUy?TR5~~ zG+n&#F`G@ppH-N$@lpC7Nb{)SsSU_($e9}k^PxhsD3zcGuMdzgbo5b7a_sq{R0kQZ zq4YQyjOdk-xG!>$eG~;ng;v2H^Y?<@+*e*yuWT^~iahn=a~Dn)z}bZqr;7N081~t6 z@_i+kUY8LHo2KpfDOq`Y3BCR=8~N)zaMND`>y*^|J~MCKaoAD+^B3&OEF6hy&w)6 z!7>hPfQv$CvDaQUZujkJ#Z*(MgLPz=76xK+Sc2cpicKi_)4QeYM;w8Co5yhJ%87>3oc-+bAd<0!aC0QnRNtdV+z8aYLBx*Me5&Tk z%H>DoPuV7C5vFS9U;rU@`sr}W)&mGCxYF$$OVU_T2~|I6P(J$DF-&H8ve|bKRWwDo znG%9ZpKsJ``QC>oHx4+s>^MTBk{OVuH`sFpN%QuTDqG)*&Q&StTCTnmLjq9243YOq zf7)t-Y-9YQFurp+lrMTaE0x}hGgi$rSpQ?Zyds3pB@YDf`b5;qLGN>lJA2${ZnxTq z-3%-ji*jX{hvkB^r8uU;b-KZtcuHogZ-wWt*jJ^;Mual;g9wzZ#b9}xnn?^7&2iN) zfGJu5kxXs!Qu1AWCe8PCUy3N;3H^vuCFmOcU?IJM)y4_S zM`KhotdiR)&6S7Ss`U68nSGbD>LW(H{oUAdH8_we)mdk3>j{pDxOw;cRe%1g$y9?! zE{_9!0^z9@-1~f{aoL=8f(MwBL4KQmlgjgM+MqWmt>$`5jUs3w;l>K;u2O}W0wxx; zd^OdnbYItYEup|3)3anVTC2az03ZJ%l)taGu5Dw9P3!_IbdKWIrxrJj({ZU9>CrM`2!Q#IvF^Rr5-8FLG}p z?EpmVBF?;^QFG^cWhjVJ9bjQeBW}s}&7Lyc9zc1i72N!aH^`(s+}`Nl?zFzI@|dmRVBD#2SdG;Y<@z!5AcqD)zx`3i9hcW4?6kNoI-7nc^^{s!G4E|qc1}1=Hd^>6 zV&zQ4?7NCc;}4l825iG}S?tR1Unh8`A2w`vmomK^twrTErN&3895t=MCAHgHWRow) z?Q0dutjy~X^fSn!EdxnuILZByqlnAtB`G!>KBHFOewdBFfa%8+daZ&FmRaDNLG;vW zvN|$0`|&pGH3n;;RJ3o=p8!H=ori~dYHid^_Kh;Qu=EASH?-)6!Y>&oVD1Y~-Ww$N zdh2ls8o|sgo-AVKcX35ub=e(ZJC=z`9-Z4y60Za|k6bnZK;)cv=9$a(-SMFfmpz`( zXC|ywTkKT0^vn&5-&A$MQ1Oid<>k&ApqqADdHx0dE*0I}E#0v%5(&ATc7(|QP?J6N zcg8EH6Gj?t&$ZgUK51ZBq#e|v-dQ8t^2M}n*#ShLB^bb2DHl>M<7lGOC%ShM)G*fp zb#=`zB^n1R08sznG^W(vaGn39^iOqppW#2!p{5-(@=j;4>xs7P4jE7`4HMp^g|9Np zMxXvXt!W|#H9a!NV^p(~WL@~Z^+8BhgT0AxS3@tm=_n88Hxfj0*3FkOdTz%vs|P@E z`?H!3qovTZ;oYk*P2WUhCv#ST-Eo94)%BKLn^!vdr_qLUOtlGgdim(0+AO5yMEENH z%6p15e0Clje)T=<#Vc`IY1U3z8=wlqDfcyXclW*-kTg!4%E==G?GHe|TkpG5KmDD4 zhNbcPha*QkqV&Ck=K;A^8{wPP3JwbAp>G4gS#}4+i$FlWIC2RWmMuD;w8smk{*njn zjX(gV-40G4;D$s{c!M;q&$_%X$@Swj)OZ z1Q6Z@KFc8l{I55kL+t^sw(7KLC+yc=D3;9~D57>)o68n(SsvPuk07)Kj%Og>^9PBn z5hrRpF-U819o+Bqo{#ADge(Uz=0^gpape`1s^t(lYH*}+Jcg})fow=XQ;jP3feezI^y(^Fr`N(It*lDiW>zSj5cT^8cIdXC}Y9tNo1Dg1TO;(Z*XcJ{B& z9SQ#S$)#p@n1#7_{_rTPN=Z<%*BmK)}`8w)e=_n?k;Mr$a<)D@d2w1w$N!AXzNFM{=S#K969+$28)Md%e;`d z#@zxaVli^E7Kn>?OFL)S>bXz{_;K=kvb*cXsiK?kzT7JDZh&+Db8%+sFV7v+VsD?? zmL%JvEyZKY!R$fC-nzM+FH~nQc&E;m?)){r>`(^M5P2rHsKp*Z&!A`aKZSR^+{S50Tp+sowH|o_Ox>SGOIm zWbyuH9?x^PoD>1QH?7W>PI1zVrUGvR&VlW-^mKd$$(tGXbF*ZxYu}8x){$s(_!e?% z>E9YW`jo@|GHG0HyX5&BMO;dUN4H zFS_DU%Y%Wt&pvF~9;Rt6Wb?wM%K94y50u~i`*u8^-L&_(?EL4&GDbDyGKwuBoccf5 z5^|@eY;&mJ$a#Hzo{_sjv*e@jADkBvx%qpK34hEKa*MX!ex9td|0<{dSGP&f{d;_u zFPU|upT7bgM3sx;7oqd}_m3TRKci__Scu}Lpbz&5w%1c&cEnv!a%SuQ**`4wrX0iXl1QKgCvw<@H(Z^RN2;ak7Kam&&ywqpj@T_e`;}jXQrlJw9Y5~4*IP6;wTqzUg&2f}Qr zQ-G)HYB0Pq>Gmze%DKZ-#1oXMV5F=3uC6fsANqW&aa!H~sZ)c7_5a1G(W+`*j}XXF zno~zQuWAW@EH#yx$xHJODUH;2m-0+{xZdG&fFIpppTL%rMt^E8^x|A~xS_o7K<(DN z(WRK!;Eo?~uDu`!FOHMms zMIlU5-66@p|8}S3LfXFl!<}-P@AAKn)QEfk^rAgW4iCEoN(cNs8|#n#)40L%ug19l z|I`lnA75_%QH&tWzVt;G0Tdk_1oZ^5_6;UvOwJK)=d+B0UjV@NB&hHDYA)6??!(<5 z)nw9vna6B2r8HzEKz^E$3Bx-@`=_ZgFn4j0-3ibvf%oDD>%brYtFh7aJVSs zoUKF!P~my7BD)Nf$s5(QQK!{^4QR^J>)(#&85ERE9|^Po#oaP<`)*rtTd23>f9l_` zZRyK~kCs@x@IAT0A^N|%JM(`izd!DON|d5avPUu25M#&|vX6btkiEp1u``wr%387u zBW5(&ml|&pEI2KF^mev$szo z2e=Eay<#}2tNPKz=BHuOz^}QF>)pcl%x`z1GO>5pW50^569{l=Xuu@r8$(?|$NO@u z@pxWAGJZ69*~j^=sz*J z0(fD7L4#yhUB;Z>SS@dJXBdnU2mDc)JO|u&d=$*Y$V?cYTxp_aAW#dGF(;Jy%zN|u zCje0kG)&*~{b?t`v;P?DOVrCcj}+~$j;)qdJ5xmo@AR?c-M&s_G;okaW zsg)c!uAj&_Uh6gqct@Ib(1)_tR5h$p@?$B`KNFkJfEW1I9zj-RdO4%>BestQfVXgG zX0y~Hu!#yv(XEiP1Vw*8qo?}#Y1Q1qf#`-is))>>n}`fqw|{?g4yMO(%fm|9Zr=){ z{FET>@y0Gr{G^33IDgXQb^oUjcnu)*-rs-n7ua*XGJ7u*eb#-tk*bgG6m@pOIKhaX zXvc&pajV)NTaU;OJp{HK7yEIfe86>D1x*o9?0H)DV05m>@pF8{fu;{&Rw^@OjGF?z zEn7XCwdu8P#F-=PC7M%!tpzUfEQ>7zmRZjL6b=2Qn?){NntEuN)8F`_^@rQ^!rBVn zh>Xc=GJe*M)+Rna#mr9Hp%;N6!wLQX><>_$wDW5_241#aR^>7OTm6ALb{!i1)}4V{ zJt*NRzGO**D*+xlZdhk$XIkr#JM+#8wXY>+(vz2&_rX-BJ)!~$yq(8PGhm1A(hG24 zr0hGRVouPTr$-y+;jWXl0)q=L_o%YrW<-`<5UxAn=7uL3uU@lzpyzh%FV_-WJZi6OC6 zN&0KjiWj&HB_b9v3xhv~3t;JcFT^|l+lv*kEQ~B~J#;2#QneI%t)J&M10Z%zv(}6b zvkf}0N}GG*!9$e=S)&zBUZ;$lEq81yDmW`hDrpXAorvj#xF*!)EX5X{4$aU^p{uJ)ytTT?o0L3fs zOltgJZ%4$1_)zJN8xINR$+xp?{JyXo1pebJo8pgz_Y~2oVG5u(L9Lh7nd^6$gxjg< zLv2voF_^S9t$1cItbmdukI+NKX!(B2Q?TqyD!yucq_JEQUZ`>J<){h=y-XnyS@XDB zS41K=)sA)$7;QdvImSB^4-q0zT-F+g`!@VOxj|h!2Bwvoz4+yx#g2`oM!rd(v!s5I zsvRkMB^$+nNCvW!6xjSOA>6mQ+@h*$#1iT@cF7EOlp3*o;XM6#AvkLE>Ba2Pr@iXc z=VaX`Mwrrdu%pEAou%AF=SN>1NbS-Orcvdba{m3oF6(*xQ`)2f8HIqYA4_wkczY@d zfjosiP2ro_j6!aWws|dJzO^LPD zx_(WrF6AZrf3zDlROon~_hbR-`NP@Xj0;^8bQW12}PgFv46rWQNqvx0tZio2Mu%Mp?vy5@OH*t_0^>FS(U=cOnk14Lx*-_PeS9 zva?GI9utiT(b9pQFDFn}B`3WS2R4Qb>yip)OP`3duWN~99B+tA>cR?>yt)&;2mb37 zx^aZm^&6KVo5S3V@sAEG-UEp&Lr)9wWatI#CF(&c>SokUFJC)PKY!L@lm&wP-TxA` zzPc)~|28QE5!48Au?v6G*^dLSpJn8rm8>?P!9YNatw32K#9Wp`?o!ayfwXThbzP@Q zEi9W_xX}8&LUdbWBK$b#ry=<@RV&7ByxOhTi&b`C{Clq|-%PkW0|9Q`(|^x`vp8>E z2~>bf$HMvt%r}os(~Dj-eKhciE828(Qug^=&g9!_ioOw&WBK0CEK#hwY+(N@&Yp}E z3P`?XcB)3$si1rnK!qGRVb-%&B@P`+ykh+t;rFZ+*r7N+HtRWEs@V3IwUt+s#OUQ< z+2z=g0~6aOR+6yRPs3r{QAV}>Kh+w%IN&a%a$;Y$cLuLycgJSQJ@{G^QUa2 zeiJ%JeO}JAYpP86UFv{VSYSZ{Q%*GIBjIUtl)Es*5N8R z_%y2s(!rDTwNq3ukv-Rs)92E(yfX5{-(P}@wh7&0b%-Z$%KM{kng775_1?hj$(iQ7 zYQ{+fyXDfKh!1&EW`98K(iP$pi#f4QK={-6{hOC{WOoTMcsnon#7lt(W0zwEv$Ctb z8QD)x+H3AHwcU2Fw>F;dGK3C(?a&_^VwLGW$>h&D|BmPfiNI-h*JfzXE?f?-F(qr* zwI~)Na2x{n1K3G+{e_2nOT?TcsUT55B@XjCPN?5P_<>IJvSoE|+EVnaIF}m`F#3_s z%75*nIjgs_)KN$l#dmR;rS0uytc~A!IvqfJ;qFIdB@2+=)6`aiQ-X^ZBfU0#zd%A! z{AzEUp%YZ!l%??y$^s$f4j1e(t!zb0rTe6l$uO$yBv%4QfTwMJ>c4t-snzg+mPtbh+IWC1 zOB=xDleef0mey9qO7XQ~oUa0dYEQHCe10Xm@H8*-NAHQ->}fX1MCy6I2cqVDk_(hL zjtU-1)5`F4Bs+#>+xXSQ?3p`G>YE>G8+A(fW9@;YECI@G_|`gh<9A8xn1^p1yVS#6 zWTPl-%=;eYSvm2_ZaIEf)X*}zH||J+$A7Wpw%XjtTbea@b$~#cn@cC1xQeYBar2%rV4^axgy!YX*v?5%++A~s&&H9 zxw-#c43ANl${&$s_!r(2O&d2zpYsyCJ(vaZ!=_daLA6n>UmI1voY zY~z;}lkdhO4*lLKmLWWV?Tj(&Pf-xWehr?8uTsNkBkYW&6v}k8%~`p9)Utgm3F7!{ zcP@(g>CngCeD#X9oTt6dMTv*kVQZvxy>KA<)&|a zQ>Xg&9Pfjnfo#QdFi?_S4@%<4R!dpn2*WvToT!z5SbE+cTb|!)@4c+6l+Yiw^l(PG zuL2|{N2BuPzsl3nQ&FSBQZ`pb1D*YTYV0CX-g!>T&L3t^X#U6#Qi)|Z z{)`kyFE+F(ST(WKZXB+b%e9<+o*Q6^!I~AZgt`>4JI^*{4t;kl7Gqr$i955cgY>Fu zHzpUyOzti)u`;D?yics;03(<5A8S_@Za@~ytd<74AEc_9BH@At?`7;Im&B|qvmO>* z#Z&SdX^R;ivMkRdEuhnqS?$Bkv zk+z%ji2LC+DT=!e)x7hW6I2j1-W)Q@f@q-q8jF@(X50t--c8({Rl(Y;+y7dB(K~Qw zpqPT*tQ@Zz-f1s8uosA(G$ka4I}C;E2AzD?tkvu=(K_b__YzljbgUJ-OAnxYhNUuh z?rkUj@a~_^84?+O3q`aav`8~&hY1d8zt@ zoYi8D24tAs_}^|buFUeTN9GUM>-NFStfLKfn_fEne$dV2bg7;ngfO~`SlDiK%?3A#y=_e~dhnI$M$x6$>-!{ldIVe#q}=Osu7O{UnQ#|| zs3^Oef56e@R;75P(^^~FL40T!IOC(Xhf2C<>hqys#b0<~TI5@*xvdnPyf+tdIE)t= z-%ijtW-;cQbTH|WabsT(B00=SM;&`}{0K((8B-agNjUl z+H(ghuSWXbkt?(U<;FszR3bR;P03I8XJT<`$1KkTr>=2H8X*LxMj2E(GUUF;#G&q{I=T3e(A+hM`dteqsztW>vgw2YEFfpMrRoCaAhET8#qR+ z4K1sdcknxwrOt!b=JAj5_fMaWJf!JA>8>*0NP4JjCPS-J z@LfOJUEK#Tv5Vs&=%N)lNr0LX@ljfxW-g-g!$CrQ_?q`jz|V%sgkz3(fh!{5Zx0~W zH(?DPgr7%ctN7tuc^z!2j}ujy`Qxfkk7i`lwM%Fuy@I7jUKE< zj$mqd^Xh1UPu)3f*7GB?XujKIt=mL3dSj*q^4AFA$cxUa39~=#FO-liV7;(m=kPOl z&d06t;!hYXRTs}5UvG3`RwCGVfq1m6gB=h&sj>Xi_>YPMa$AH09Ff)zuJC;PMIoc$a8ko#;FMdqaWq~G|Y zfOb#tIbZw{s7KixK1I%McbvdEy{*RxCwucG@2FJ&Mn8gSOI_^b$+l_ogU|(}9fXzO zNB^LkFHA-Xn|~=Z+JmP)lL*{hsky(oXyHGA5JIhuzdt$L)w7Hd)K4D-zBighh(w)} zZBm_b_s$V1uySv3Y!9a|GcU6?=CAVKm;tb9(x##Sk>JOx6${M4`&c~jEQECm*uL+S zuh?O(5IGYT1CeaerlyK0jmC@bXV|vO?b;qp7R!1Umiq1w)F-?;8tQufqVk(1!w{P< zJ$(GwspZq(&o{peK29>GZUNHzbmEy5A)|LGk8Sr0&Va4dz1m)>7|+ID@_lsR-&voc z<9ParGxcv{)OX4O>e~|-aO?UkKW%aFo3vYqMTR=@V53z7>q5=jU^_um%XOUU^B{-k`p1A`Q)C`?r zod-r6E{xr$fE|qN2R^{5KmEz~=Op~`zcG03#p~w?{D4Ba{ekYv(oVM7tE0VUOp`9! z^{TV&jQ{L&K-2a%h-%tsZOXAH0C7*;Sy%EZbsIjFy<%s|^OGd+pN>oHcXTHKgp9^| zf1!gfjS>Zc{${?fP^y>#V5-#&*E-r;w+b^CrDj;?^HH@LM|EpoitmL-WozW9fIcHC zI9l2ITL=NbR7We^pv;N>aSigP(b)azp29UDHK`3{``>S!<5+k=IUd^j7Azs1_!xuE z*hyWb^l1}&)sD~KW#D+?ziE5l! zM{R1;!?o5vNh$+6=55e~ud%t$%194W-b8Il%w~~EF0E|S#_YkgjtJv3LGRn)|Q?hI2%l!4;IG^soLF6*36hoo?dT~>?*r`i|zbp zlBPNBZ(EV-ZB8<(leZeSSAj*FzIFdJi*r54l!a^{rU)eq+<-^<6vt3}+Gf+6O8iEZ zoeT;=V`mK~G|?s2mLNC1)Tlr?7>I0bLHwRK?1l$FA4Dg%@Y#E|^Z6gJG9ff*Kh=CQ za%&c*{b#3}{5{4@y+zQ$XE%wWT*`sffQe=M6V_=S_2GzKyGsSSue5IkJIimEB$WODr%n#eCizu*&Y7(PJ3G|_GTHisQBy~;w}4X% z{>&~eHyWLAdDzCp_8jvSUwfN0#VFqurQIH*VCld$ek}GFiw(dv0BbRw)bGae883i_?r)H z>Z{`mq%6m6PP3O;XUKd=hR)o@OcxvB2yCrjQmSb$59`rcs%+UC(rwtWyE=-yw4BVe*0%tuPO=Tf?+DxbOQ~wcctA3bY zHCj^jPB#E?r0~jFHIwvyC1)?8w}O|IAcKb4uP!>Px-pgtAf#I76BGmYX{A*at$h$K zINTaD*4EfTZ&r;o;#1VWn@Q$lCr71mHF+q1-I1gcpbwV@Y4FY;k7lK{&10RG%S)4F7&FrLiYTY?Cl{5d!9aaIWrJ)3L=9&E&S0T!<|FjPO0ll{C2LN# z3oS3RIt~K0s&(~BvlPk)+-t$+R|7GU&toZL^kXnkzVooe>#W>yeIs9z8U5Q5(eAG>M z;VO9MIB*iV19!429YzeOnaHHEgHpFE`I(a@xT&h(rdQip#05~S zbIZ3*Zl7rzXxHa7nLcFeguX0ngdV}qkI;t47RxV{5qCaW-P>L-=Uz^Eczyy9lq0lA zS`i|wdXQ|2&;)e!21Fl`bf{^F(b6kf9Pud?YE1qF!T!oS~cpG5g;R{q58u{G8Cb+9Q;y4@qnc=*@yEnhPR4&~Qtl zkvE{)j&C+%BbT{!4rKxhhL060u^YtJX@l|)5g^g$cZwjzG*op5{Ef)Gx4c~bAKRMv zTX{OH(@)L)egzf>qor19 z0`A|3IbnpEe(1`yMZ;Vq7tKhiO4W&zI*+v1lsd4#49ahy^p>+2K%MyzoQ+#qm}kb2 zLJL{4wr>9H=TPMEalz}N7pPz<-u$$K@Ll&1yaT#qDvYS1m#T=oH58g)mv2i_ueT?@ zjn(UcPkRixioOsPWIli~;8W`;kQ*A1*Od@r&Iu1l8FaBam&qaLF|C()*-|)r&k%23v6yiK!4H$^6 zV7>`Plp__&X(pPV=zWNt?>$?U6__CrV9+XH;F&Upe>Z22mCO&NsL`RJzJkQ;9}p_~ zHKYKpoks54Cmp9-eS<_e%d7ArEO*e&8}VCx@7_@S(O%moqEfY`L*RG?%oKVbadN2r zT(Blg#4s9+X+&X>!{+HVnr*_&;yGj%a(QEi0s-^ z*K*xxN9N;y|DQzx_=xtFRmk{LWywuLdocZfkKuf@EC2iT&-eMm|3AuzLjQy)`so9o z7kcmWDRI4m%l;pq;QxT93T^+jDZ%b<0dDBPe`QswKga&IMAr6ytW{&T^Jf?n#rS&P RZ~c9$t7)iF_V97|{{ethk+=W= diff --git a/tutorials/text_processing/images/thutmose_tagger_final_alignment.png b/tutorials/text_processing/images/thutmose_tagger_final_alignment.png deleted file mode 100644 index dec47fec51436a54ecd2f58039a9693104c774ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18211 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 DyY4c; diff --git a/tutorials/text_processing/images/thutmose_tagger_tag_vocabulary.png b/tutorials/text_processing/images/thutmose_tagger_tag_vocabulary.png deleted file mode 100644 index 5c97a0cb09cef58b6c1996f8199886644d87f5f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6134 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 X5`O)f{W1gBtH)D+sjXb8XchK Date: Fri, 10 Feb 2023 06:20:11 +0000 Subject: [PATCH 02/19] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/tts/g2p/g2p_inference.py | 3 +-- examples/tts/g2p/g2p_train_and_evaluate.py | 3 +-- .../tts/g2p/heteronym_classification_inference.py | 3 +-- .../heteronym_classification_train_and_evaluate.py | 3 +-- examples/tts/g2p/utils.py | 3 +-- .../tokenizers/text_to_speech/tokenizer_wrapper.py | 3 +-- .../tokenizers/text_to_speech/tts_tokenizers.py | 11 +++++------ nemo/collections/tts/g2p/models/__init__.py | 2 +- nemo/collections/tts/g2p/models/ctc_g2p.py | 4 ++-- .../tts/g2p/models/heteronym_classification.py | 4 ++-- nemo/collections/tts/g2p/models/t5_g2p.py | 4 ++-- nemo/collections/tts/g2p/modules.py | 4 ++-- .../tokenizers/text_to_speech/test_tts_tokenizers.py | 2 +- tests/collections/tts/test_torch_tts.py | 2 +- tools/ctc_segmentation/requirements.txt | 2 +- 15 files changed, 23 insertions(+), 30 deletions(-) diff --git a/examples/tts/g2p/g2p_inference.py b/examples/tts/g2p/g2p_inference.py index ec32c29c4a95..a178e474c7f8 100644 --- a/examples/tts/g2p/g2p_inference.py +++ b/examples/tts/g2p/g2p_inference.py @@ -18,14 +18,13 @@ import pytorch_lightning as pl import torch -from nemo.collections.tts.g2p.models import G2PModel from omegaconf import OmegaConf from utils import get_metrics +from nemo.collections.tts.g2p.models import G2PModel from nemo.core.config import hydra_runner from nemo.utils import logging - """ python g2p_inference.py \ pretrained_model=" \ diff --git a/examples/tts/g2p/g2p_train_and_evaluate.py b/examples/tts/g2p/g2p_train_and_evaluate.py index 1770bd28f87d..3627681ffe15 100644 --- a/examples/tts/g2p/g2p_train_and_evaluate.py +++ b/examples/tts/g2p/g2p_train_and_evaluate.py @@ -16,15 +16,14 @@ import pytorch_lightning as pl import torch -from nemo.collections.tts.g2p.models import G2PModel from utils import get_model from nemo.collections.common.callbacks import LogEpochTimeCallback +from nemo.collections.tts.g2p.models import G2PModel from nemo.core.config import hydra_runner from nemo.utils import logging, model_utils from nemo.utils.exp_manager import exp_manager - """ This script supports training of G2PModels (for T5G2PModel use t5_g2p.yaml, for CTCG2PModel use either g2p_conformer.yaml or g2p_t5_ctc.yaml) diff --git a/examples/tts/g2p/heteronym_classification_inference.py b/examples/tts/g2p/heteronym_classification_inference.py index a4621422a8f4..931f01439d3d 100644 --- a/examples/tts/g2p/heteronym_classification_inference.py +++ b/examples/tts/g2p/heteronym_classification_inference.py @@ -20,13 +20,12 @@ import pytorch_lightning as pl import torch -from nemo.collections.tts.g2p.models.heteronym_classification import HeteronymClassificationModel from omegaconf import OmegaConf +from nemo.collections.tts.g2p.models.heteronym_classification import HeteronymClassificationModel from nemo.core.config import hydra_runner from nemo.utils import logging - """ This script runs inference with HeteronymClassificationModel If the input manifest contains target "word_id", evaluation will be also performed. diff --git a/examples/tts/g2p/heteronym_classification_train_and_evaluate.py b/examples/tts/g2p/heteronym_classification_train_and_evaluate.py index 012e7a894958..171760f1cd3d 100644 --- a/examples/tts/g2p/heteronym_classification_train_and_evaluate.py +++ b/examples/tts/g2p/heteronym_classification_train_and_evaluate.py @@ -16,14 +16,13 @@ import pytorch_lightning as pl import torch -from nemo.collections.tts.g2p.models.heteronym_classification import HeteronymClassificationModel from nemo.collections.common.callbacks import LogEpochTimeCallback +from nemo.collections.tts.g2p.models.heteronym_classification import HeteronymClassificationModel from nemo.core.config import hydra_runner from nemo.utils import logging from nemo.utils.exp_manager import exp_manager - """ This script runs training and evaluation of HeteronymClassificationModel diff --git a/examples/tts/g2p/utils.py b/examples/tts/g2p/utils.py index c91be04f2ae3..143259356ce6 100644 --- a/examples/tts/g2p/utils.py +++ b/examples/tts/g2p/utils.py @@ -14,10 +14,9 @@ import json +from nemo.collections.asr.metrics.wer import word_error_rate from nemo.collections.tts.g2p.models import CTCG2PModel from nemo.collections.tts.g2p.models.t5_g2p import T5G2PModel - -from nemo.collections.asr.metrics.wer import word_error_rate from nemo.utils import logging diff --git a/nemo/collections/common/tokenizers/text_to_speech/tokenizer_wrapper.py b/nemo/collections/common/tokenizers/text_to_speech/tokenizer_wrapper.py index 539c9d85a829..e043367a9feb 100644 --- a/nemo/collections/common/tokenizers/text_to_speech/tokenizer_wrapper.py +++ b/nemo/collections/common/tokenizers/text_to_speech/tokenizer_wrapper.py @@ -12,10 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from nemo.collections.tts.g2p.modules import EnglishG2p - from nemo.collections.common.tokenizers import TokenizerSpec from nemo.collections.common.tokenizers.text_to_speech.tts_tokenizers import EnglishPhonemesTokenizer +from nemo.collections.tts.g2p.modules import EnglishG2p __all__ = ['TextToSpeechTokenizer'] diff --git a/nemo/collections/common/tokenizers/text_to_speech/tts_tokenizers.py b/nemo/collections/common/tokenizers/text_to_speech/tts_tokenizers.py index ac9cb45a2724..ef66913d22f0 100644 --- a/nemo/collections/common/tokenizers/text_to_speech/tts_tokenizers.py +++ b/nemo/collections/common/tokenizers/text_to_speech/tts_tokenizers.py @@ -19,18 +19,17 @@ from contextlib import contextmanager from typing import List, Optional +from nemo.collections.common.tokenizers.text_to_speech.ipa_lexicon import ( + get_grapheme_character_set, + get_ipa_punctuation_list, + validate_locale, +) from nemo.collections.tts.g2p.data.data_utils import ( any_locale_text_preprocessing, chinese_text_preprocessing, english_text_preprocessing, spanish_text_preprocessing, ) - -from nemo.collections.common.tokenizers.text_to_speech.ipa_lexicon import ( - get_grapheme_character_set, - get_ipa_punctuation_list, - validate_locale, -) from nemo.utils import logging from nemo.utils.decorators import experimental diff --git a/nemo/collections/tts/g2p/models/__init__.py b/nemo/collections/tts/g2p/models/__init__.py index 46c89fc49a6e..e1eeee3b2c33 100644 --- a/nemo/collections/tts/g2p/models/__init__.py +++ b/nemo/collections/tts/g2p/models/__init__.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from nemo.collections.tts.g2p.models.t5_g2p import T5G2PModel from nemo.collections.tts.g2p.models.ctc_g2p import CTCG2PModel +from nemo.collections.tts.g2p.models.t5_g2p import T5G2PModel __all__ = ["T5G2PModel", "CTCG2PModel"] diff --git a/nemo/collections/tts/g2p/models/ctc_g2p.py b/nemo/collections/tts/g2p/models/ctc_g2p.py index 2a7ad7965c02..66475513ba59 100644 --- a/nemo/collections/tts/g2p/models/ctc_g2p.py +++ b/nemo/collections/tts/g2p/models/ctc_g2p.py @@ -19,13 +19,13 @@ import torch from hydra.utils import instantiate -from nemo.collections.tts.g2p.data.ctc_g2p import CTCG2PBPEDataset -from nemo.collections.tts.g2p.models.g2p_model import G2PModel from omegaconf import DictConfig, ListConfig, OmegaConf, open_dict from pytorch_lightning import Trainer from torch import nn from transformers import AutoConfig, AutoModel, AutoTokenizer +from nemo.collections.tts.g2p.data.ctc_g2p import CTCG2PBPEDataset +from nemo.collections.tts.g2p.models.g2p_model import G2PModel from nemo.core.classes.common import PretrainedModelInfo from nemo.utils import logging diff --git a/nemo/collections/tts/g2p/models/heteronym_classification.py b/nemo/collections/tts/g2p/models/heteronym_classification.py index ddfac56ef93a..f9727c7ad887 100644 --- a/nemo/collections/tts/g2p/models/heteronym_classification.py +++ b/nemo/collections/tts/g2p/models/heteronym_classification.py @@ -19,8 +19,6 @@ import torch from hydra.utils import instantiate -from nemo.collections.tts.g2p.data.data_utils import get_heteronym_spans, get_wordid_to_phonemes, read_wordids -from nemo.collections.tts.g2p.data.heteronym_classification_data import HeteronymClassificationDataset from omegaconf import DictConfig from pytorch_lightning import Trainer @@ -28,6 +26,8 @@ from nemo.collections.nlp.metrics.classification_report import ClassificationReport from nemo.collections.nlp.modules.common import TokenClassifier from nemo.collections.nlp.parts.utils_funcs import tensor2list +from nemo.collections.tts.g2p.data.data_utils import get_heteronym_spans, get_wordid_to_phonemes, read_wordids +from nemo.collections.tts.g2p.data.heteronym_classification_data import HeteronymClassificationDataset from nemo.core.classes.common import PretrainedModelInfo from nemo.utils import logging diff --git a/nemo/collections/tts/g2p/models/t5_g2p.py b/nemo/collections/tts/g2p/models/t5_g2p.py index bc1ba7e88ab9..a3d54caf3644 100644 --- a/nemo/collections/tts/g2p/models/t5_g2p.py +++ b/nemo/collections/tts/g2p/models/t5_g2p.py @@ -17,13 +17,13 @@ import torch from hydra.utils import instantiate -from nemo.collections.tts.g2p.data.t5_g2p import T5G2PDataset -from nemo.collections.tts.g2p.models.g2p_model import G2PModel from omegaconf import DictConfig, OmegaConf from pytorch_lightning import Trainer from transformers import AutoTokenizer, T5ForConditionalGeneration from nemo.collections.asr.metrics.wer import word_error_rate +from nemo.collections.tts.g2p.data.t5_g2p import T5G2PDataset +from nemo.collections.tts.g2p.models.g2p_model import G2PModel from nemo.core.classes.common import PretrainedModelInfo, typecheck from nemo.core.neural_types import LabelsType, LossType, MaskType, NeuralType, TokenIndex from nemo.utils import logging diff --git a/nemo/collections/tts/g2p/modules.py b/nemo/collections/tts/g2p/modules.py index 6108098cfa74..e1feb2981cb4 100644 --- a/nemo/collections/tts/g2p/modules.py +++ b/nemo/collections/tts/g2p/modules.py @@ -23,6 +23,8 @@ import nltk import torch + +from nemo.collections.common.tokenizers.text_to_speech.ipa_lexicon import validate_locale from nemo.collections.tts.g2p.data.data_utils import ( GRAPHEME_CASE_MIXED, GRAPHEME_CASE_UPPER, @@ -32,8 +34,6 @@ normalize_unicode_text, set_grapheme_case, ) - -from nemo.collections.common.tokenizers.text_to_speech.ipa_lexicon import validate_locale from nemo.utils import logging from nemo.utils.decorators import experimental from nemo.utils.get_rank import is_global_rank_zero diff --git a/tests/collections/common/tokenizers/text_to_speech/test_tts_tokenizers.py b/tests/collections/common/tokenizers/text_to_speech/test_tts_tokenizers.py index 54fc0f061ca8..4a8fe62e351b 100644 --- a/tests/collections/common/tokenizers/text_to_speech/test_tts_tokenizers.py +++ b/tests/collections/common/tokenizers/text_to_speech/test_tts_tokenizers.py @@ -13,7 +13,6 @@ # limitations under the License. import pytest -from nemo.collections.tts.g2p.modules import IPAG2P from nemo.collections.common.tokenizers.text_to_speech.tts_tokenizers import ( EnglishCharsTokenizer, @@ -21,6 +20,7 @@ IPATokenizer, SpanishCharsTokenizer, ) +from nemo.collections.tts.g2p.modules import IPAG2P class TestTTSTokenizers: diff --git a/tests/collections/tts/test_torch_tts.py b/tests/collections/tts/test_torch_tts.py index 4a1c2a4e0990..5d39c955fa88 100644 --- a/tests/collections/tts/test_torch_tts.py +++ b/tests/collections/tts/test_torch_tts.py @@ -18,9 +18,9 @@ import pytest import torch -from nemo.collections.tts.g2p.modules import EnglishG2p from nemo.collections.common.tokenizers.text_to_speech.tts_tokenizers import EnglishPhonemesTokenizer +from nemo.collections.tts.g2p.modules import EnglishG2p from nemo.collections.tts.torch.data import TTSDataset from nemo.collections.tts.torch.helpers import get_base_dir diff --git a/tools/ctc_segmentation/requirements.txt b/tools/ctc_segmentation/requirements.txt index 2c6cdaa1e755..f010b225a66e 100644 --- a/tools/ctc_segmentation/requirements.txt +++ b/tools/ctc_segmentation/requirements.txt @@ -1,3 +1,3 @@ ctc_segmentation==1.7.1 -num2words nemo_text_processing==0.1.6rc0 +num2words From e7019b7f0a9bf5ceee52ad770bd74c1ea4b8016e Mon Sep 17 00:00:00 2001 From: ekmb Date: Fri, 10 Feb 2023 12:06:24 -0800 Subject: [PATCH 03/19] fix imports Signed-off-by: ekmb --- docs/source/tts/api.rst | 2 +- docs/source/tts/checkpoints.rst | 2 +- docs/source/tts/configs.rst | 6 +- docs/source/tts/datasets.rst | 2 +- examples/tts/conf/aligner.yaml | 4 +- .../conf/de/fastpitch_align_22050_mix.yaml | 2 +- .../conf/es/fastpitch_align_44100_ipa.yaml | 2 +- .../es/fastpitch_align_44100_ipa_multi.yaml | 2 +- examples/tts/conf/fastpitch_align_44100.yaml | 4 +- examples/tts/conf/fastpitch_align_ipa.yaml | 4 +- examples/tts/conf/fastpitch_align_v1.05.yaml | 4 +- examples/tts/conf/mixer-tts-x.yaml | 2 +- examples/tts/conf/mixer-tts.yaml | 4 +- examples/tts/conf/rad-tts_dec.yaml | 4 +- examples/tts/conf/rad-tts_feature_pred.yaml | 4 +- examples/tts/conf/tacotron2.yaml | 4 +- examples/tts/conf/vits.yaml | 4 +- examples/tts/conf/vits_44100.yaml | 4 +- .../tts/conf/zh/fastpitch_align_22050.yaml | 2 +- examples/tts/g2p/conf/g2p_conformer_ctc.yaml | 2 +- .../text_to_speech/tts_tokenizers.py | 2 +- nemo/collections/tts/g2p/modules.py | 2 +- nemo/collections/tts/torch/tts_dataset.yaml | 4 +- .../ds_conf/ds_for_fastpitch_align.yaml | 4 +- .../ljspeech/ds_conf/ds_for_mixer_tts.yaml | 4 +- .../ljspeech/ds_conf/ds_for_mixer_tts_x.yaml | 2 +- .../tts/ljspeech/get_data.py | 2 +- .../ds_conf/ds_for_fastpitch_align.yaml | 4 +- .../wordid_to_ipa-0.7b_nv22.10.tsv | 326 ++++++++++++++++++ .../asr/test_text_to_text_dataset.py | 2 +- .../tts/FastPitch_ChineseTTS_Training.ipynb | 2 +- .../tts/FastPitch_MixerTTS_Training.ipynb | 4 +- tutorials/tts/NeMo_TTS_Primer.ipynb | 2 +- .../tts/Pronunciation_customization.ipynb | 2 +- tutorials/tts/Tacotron2_Training.ipynb | 4 +- 35 files changed, 378 insertions(+), 52 deletions(-) create mode 100644 scripts/tts_dataset_files/wordid_to_ipa-0.7b_nv22.10.tsv diff --git a/docs/source/tts/api.rst b/docs/source/tts/api.rst index b68bb0458331..7e87687aa0a8 100644 --- a/docs/source/tts/api.rst +++ b/docs/source/tts/api.rst @@ -96,4 +96,4 @@ Dataset Processing Classes .. autoclass:: nemo.collections.tts.torch.data.VocoderDataset :show-inheritance: - :members: \ No newline at end of file + :members: diff --git a/docs/source/tts/checkpoints.rst b/docs/source/tts/checkpoints.rst index f78cf6a2805e..8d79c4a278f1 100644 --- a/docs/source/tts/checkpoints.rst +++ b/docs/source/tts/checkpoints.rst @@ -150,4 +150,4 @@ End2End models .. csv-table:: :file: data/ngc_models_e2e.csv :align: left - :header-rows: 1 \ No newline at end of file + :header-rows: 1 diff --git a/docs/source/tts/configs.rst b/docs/source/tts/configs.rst index c8f47c82c03f..cecaee29673a 100644 --- a/docs/source/tts/configs.rst +++ b/docs/source/tts/configs.rst @@ -96,7 +96,7 @@ Text normalization (TN) converts text from written form into its verbalized form _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: en input_case: cased - whitelist: "nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv" + whitelist: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" text_normalizer_call_kwargs: verbose: false @@ -118,7 +118,7 @@ Tokenization converts input text string to a list of integer tokens. It may pad apostrophe: true pad_with_space: true g2p: - _target_: nemo_text_processing.g2p.modules.EnglishG2p + _target_: nemo.collections.tts.g2p.modules.EnglishG2p phoneme_dict: ${phoneme_dict_path} heteronyms: ${heteronyms_path} phoneme_probability: 0.5 @@ -260,4 +260,4 @@ Fine-tuning via a Pytorch Lightning checkpoint trainer.devices=-1 \ trainer.accelerator='gpu' \ trainer.max_epochs=50 \ - +init_from_ptl_ckpt="" \ No newline at end of file + +init_from_ptl_ckpt="" diff --git a/docs/source/tts/datasets.rst b/docs/source/tts/datasets.rst index 083566bc7cb8..4b2e96baedb6 100644 --- a/docs/source/tts/datasets.rst +++ b/docs/source/tts/datasets.rst @@ -47,7 +47,7 @@ LJSpeech $ python scripts/dataset_processing/tts/ljspeech/get_data.py \ --data-root \ --whitelist-path \ - or default nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv + or default scripts/tts_dataset_files/en/whitelist_lj_speech.tsv $ python scripts/dataset_processing/tts/extract_sup_data.py \ --config-path ljspeech/ds_conf \ diff --git a/examples/tts/conf/aligner.yaml b/examples/tts/conf/aligner.yaml index 3ee77b70ead1..e0008e65fe4b 100644 --- a/examples/tts/conf/aligner.yaml +++ b/examples/tts/conf/aligner.yaml @@ -21,7 +21,7 @@ window: hann phoneme_dict_path: "scripts/tts_dataset_files/cmudict-0.7b_nv22.10" heteronyms_path: "scripts/tts_dataset_files/heteronyms-052722" -whitelist_path: "nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv" +whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" model: symbols_embedding_dim: 384 @@ -56,7 +56,7 @@ model: apostrophe: true pad_with_space: true g2p: - _target_: nemo_text_processing.g2p.modules.EnglishG2p + _target_: nemo.collections.tts.g2p.modules.EnglishG2p phoneme_dict: ${phoneme_dict_path} heteronyms: ${heteronyms_path} diff --git a/examples/tts/conf/de/fastpitch_align_22050_mix.yaml b/examples/tts/conf/de/fastpitch_align_22050_mix.yaml index 1ea036055e09..ddd25d07bb38 100644 --- a/examples/tts/conf/de/fastpitch_align_22050_mix.yaml +++ b/examples/tts/conf/de/fastpitch_align_22050_mix.yaml @@ -74,7 +74,7 @@ model: apostrophe: true pad_with_space: true g2p: - _target_: nemo_text_processing.g2p.modules.IPAG2P + _target_: nemo.collections.tts.g2p.modules.IPAG2P locale: 'de-DE' phoneme_dict: ${phoneme_dict_path} heteronyms: ${heteronyms_path} diff --git a/examples/tts/conf/es/fastpitch_align_44100_ipa.yaml b/examples/tts/conf/es/fastpitch_align_44100_ipa.yaml index 53b8f813e1dc..38159321a75a 100644 --- a/examples/tts/conf/es/fastpitch_align_44100_ipa.yaml +++ b/examples/tts/conf/es/fastpitch_align_44100_ipa.yaml @@ -60,7 +60,7 @@ model: apostrophe: true pad_with_space: true g2p: - _target_: nemo_text_processing.g2p.modules.IPAG2P + _target_: nemo.collections.tts.g2p.modules.IPAG2P locale: es-ES phoneme_dict: ${phoneme_dict_path} phoneme_probability: 0.5 diff --git a/examples/tts/conf/es/fastpitch_align_44100_ipa_multi.yaml b/examples/tts/conf/es/fastpitch_align_44100_ipa_multi.yaml index 034de2396cb1..d31634f7b3b8 100644 --- a/examples/tts/conf/es/fastpitch_align_44100_ipa_multi.yaml +++ b/examples/tts/conf/es/fastpitch_align_44100_ipa_multi.yaml @@ -57,7 +57,7 @@ model: apostrophe: true pad_with_space: true g2p: - _target_: nemo_text_processing.g2p.modules.IPAG2P + _target_: nemo.collections.tts.g2p.modules.IPAG2P locale: es-ES phoneme_dict: ${phoneme_dict_path} phoneme_probability: 0.5 diff --git a/examples/tts/conf/fastpitch_align_44100.yaml b/examples/tts/conf/fastpitch_align_44100.yaml index aa60093f2d3b..29ef03109ef6 100644 --- a/examples/tts/conf/fastpitch_align_44100.yaml +++ b/examples/tts/conf/fastpitch_align_44100.yaml @@ -29,7 +29,7 @@ window: hann phoneme_dict_path: "scripts/tts_dataset_files/cmudict-0.7b_nv22.10" heteronyms_path: "scripts/tts_dataset_files/heteronyms-052722" -whitelist_path: "nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv" +whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" model: learn_alignment: true @@ -74,7 +74,7 @@ model: apostrophe: true pad_with_space: true g2p: - _target_: nemo_text_processing.g2p.modules.EnglishG2p + _target_: nemo.collections.tts.g2p.modules.EnglishG2p phoneme_dict: ${phoneme_dict_path} heteronyms: ${heteronyms_path} phoneme_probability: 0.5 diff --git a/examples/tts/conf/fastpitch_align_ipa.yaml b/examples/tts/conf/fastpitch_align_ipa.yaml index 5765b3e91797..d1a55d909e1e 100644 --- a/examples/tts/conf/fastpitch_align_ipa.yaml +++ b/examples/tts/conf/fastpitch_align_ipa.yaml @@ -30,7 +30,7 @@ window: hann phoneme_dict_path: "scripts/tts_dataset_files/ipa_cmudict-0.7b_nv22.10.txt" heteronyms_path: "scripts/tts_dataset_files/heteronyms-052722" -whitelist_path: "nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv" +whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" model: learn_alignment: true @@ -73,7 +73,7 @@ model: apostrophe: true pad_with_space: true g2p: - _target_: nemo_text_processing.g2p.modules.IPAG2P + _target_: nemo.collections.tts.g2p.modules.IPAG2P phoneme_dict: ${phoneme_dict_path} heteronyms: ${heteronyms_path} phoneme_probability: 0.8 diff --git a/examples/tts/conf/fastpitch_align_v1.05.yaml b/examples/tts/conf/fastpitch_align_v1.05.yaml index 368dd4e502fd..aa31b52fd5ec 100644 --- a/examples/tts/conf/fastpitch_align_v1.05.yaml +++ b/examples/tts/conf/fastpitch_align_v1.05.yaml @@ -30,7 +30,7 @@ window: hann phoneme_dict_path: "scripts/tts_dataset_files/cmudict-0.7b_nv22.10" heteronyms_path: "scripts/tts_dataset_files/heteronyms-052722" -whitelist_path: "nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv" +whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" model: learn_alignment: true @@ -75,7 +75,7 @@ model: apostrophe: true pad_with_space: true g2p: - _target_: nemo_text_processing.g2p.modules.EnglishG2p + _target_: nemo.collections.tts.g2p.modules.EnglishG2p phoneme_dict: ${phoneme_dict_path} heteronyms: ${heteronyms_path} phoneme_probability: 0.5 diff --git a/examples/tts/conf/mixer-tts-x.yaml b/examples/tts/conf/mixer-tts-x.yaml index 9a0f552dc4bb..5f55dc02a095 100644 --- a/examples/tts/conf/mixer-tts-x.yaml +++ b/examples/tts/conf/mixer-tts-x.yaml @@ -30,7 +30,7 @@ window: hann lm_model: albert -whitelist_path: "nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv" +whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" model: bin_loss_start_ratio: 0.2 diff --git a/examples/tts/conf/mixer-tts.yaml b/examples/tts/conf/mixer-tts.yaml index 474e32a3e730..7668b9d5bf96 100644 --- a/examples/tts/conf/mixer-tts.yaml +++ b/examples/tts/conf/mixer-tts.yaml @@ -30,7 +30,7 @@ window: hann phoneme_dict_path: "scripts/tts_dataset_files/cmudict-0.7b_nv22.10" heteronyms_path: "scripts/tts_dataset_files/heteronyms-052722" -whitelist_path: "nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv" +whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" model: bin_loss_start_ratio: 0.2 @@ -76,7 +76,7 @@ model: apostrophe: true pad_with_space: true g2p: - _target_: nemo_text_processing.g2p.modules.EnglishG2p + _target_: nemo.collections.tts.g2p.modules.EnglishG2p phoneme_dict: ${phoneme_dict_path} heteronyms: ${heteronyms_path} diff --git a/examples/tts/conf/rad-tts_dec.yaml b/examples/tts/conf/rad-tts_dec.yaml index 7b5cd5bc3442..7e676e7985da 100644 --- a/examples/tts/conf/rad-tts_dec.yaml +++ b/examples/tts/conf/rad-tts_dec.yaml @@ -104,7 +104,7 @@ model: add_blank_at: null pad_with_space: True g2p: - _target_: "nemo_text_processing.g2p.modules.EnglishG2p" + _target_: "nemo.collections.tts.g2p.modules.EnglishG2p" phoneme_dict: ${phoneme_dict_path} heteronyms: ${heteronyms_path} phoneme_probability: 0.5 @@ -148,7 +148,7 @@ model: add_blank_at: null pad_with_space: True g2p: - _target_: "nemo_text_processing.g2p.modules.EnglishG2p" + _target_: "nemo.collections.tts.g2p.modules.EnglishG2p" phoneme_dict: ${phoneme_dict_path} heteronyms: ${heteronyms_path} phoneme_probability: 0.5 diff --git a/examples/tts/conf/rad-tts_feature_pred.yaml b/examples/tts/conf/rad-tts_feature_pred.yaml index cd7cce38f1a1..9aee2d4a6264 100644 --- a/examples/tts/conf/rad-tts_feature_pred.yaml +++ b/examples/tts/conf/rad-tts_feature_pred.yaml @@ -103,7 +103,7 @@ model: add_blank_at: null pad_with_space: True g2p: - _target_: "nemo_text_processing.g2p.modules.EnglishG2p" + _target_: "nemo.collections.tts.g2p.modules.EnglishG2p" phoneme_dict: ${phoneme_dict_path} heteronyms: ${heteronyms_path} phoneme_probability: 0.5 @@ -147,7 +147,7 @@ model: add_blank_at: null pad_with_space: True g2p: - _target_: "nemo_text_processing.g2p.modules.EnglishG2p" + _target_: "nemo.collections.tts.g2p.modules.EnglishG2p" phoneme_dict: ${phoneme_dict_path} heteronyms: ${heteronyms_path} phoneme_probability: 0.5 diff --git a/examples/tts/conf/tacotron2.yaml b/examples/tts/conf/tacotron2.yaml index 4137ef19b5ad..4d30f25e4ae3 100644 --- a/examples/tts/conf/tacotron2.yaml +++ b/examples/tts/conf/tacotron2.yaml @@ -11,7 +11,7 @@ sup_data_types: null phoneme_dict_path: "scripts/tts_dataset_files/cmudict-0.7b_nv22.10" heteronyms_path: "scripts/tts_dataset_files/heteronyms-052722" -whitelist_path: "nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv" +whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" model: pitch_fmin: 65.40639132514966 @@ -46,7 +46,7 @@ model: apostrophe: true pad_with_space: true g2p: - _target_: nemo_text_processing.g2p.modules.EnglishG2p + _target_: nemo.collections.tts.g2p.modules.EnglishG2p phoneme_dict: ${phoneme_dict_path} heteronyms: ${heteronyms_path} diff --git a/examples/tts/conf/vits.yaml b/examples/tts/conf/vits.yaml index 1002bdfe89f5..64b1cf6ff12d 100644 --- a/examples/tts/conf/vits.yaml +++ b/examples/tts/conf/vits.yaml @@ -13,7 +13,7 @@ sup_data_types: null phoneme_dict_path: "scripts/tts_dataset_files/ipa_cmudict-0.7b_nv22.10.txt" heteronyms_path: "scripts/tts_dataset_files/heteronyms-052722" -whitelist_path: "nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv" +whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" # Default values from librosa.pyin pitch_fmin: 65.40639132514966 @@ -66,7 +66,7 @@ model: apostrophe: true pad_with_space: false g2p: - _target_: nemo_text_processing.g2p.modules.IPAG2P + _target_: nemo.collections.tts.g2p.modules.IPAG2P phoneme_dict: ${phoneme_dict_path} heteronyms: ${heteronyms_path} phoneme_probability: 0.8 diff --git a/examples/tts/conf/vits_44100.yaml b/examples/tts/conf/vits_44100.yaml index c9955e70abce..d666ae31f048 100644 --- a/examples/tts/conf/vits_44100.yaml +++ b/examples/tts/conf/vits_44100.yaml @@ -23,7 +23,7 @@ window: hann phoneme_dict_path: "scripts/tts_dataset_files/ipa_cmudict-0.7b_nv22.10.txt" heteronyms_path: "scripts/tts_dataset_files/heteronyms-052722" -whitelist_path: "nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv" +whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" model: n_speakers: 13000 @@ -61,7 +61,7 @@ model: apostrophe: true pad_with_space: false g2p: - _target_: nemo_text_processing.g2p.modules.IPAG2P + _target_: nemo.collections.tts.g2p.modules.IPAG2P phoneme_dict: ${phoneme_dict_path} heteronyms: ${heteronyms_path} phoneme_probability: 0.8 diff --git a/examples/tts/conf/zh/fastpitch_align_22050.yaml b/examples/tts/conf/zh/fastpitch_align_22050.yaml index b09540a75aad..c5275ba44fb8 100644 --- a/examples/tts/conf/zh/fastpitch_align_22050.yaml +++ b/examples/tts/conf/zh/fastpitch_align_22050.yaml @@ -72,7 +72,7 @@ model: apostrophe: true pad_with_space: true g2p: - _target_: nemo_text_processing.g2p.modules.ChineseG2p + _target_: nemo.collections.tts.g2p.modules.ChineseG2p phoneme_dict: ${phoneme_dict_path} word_segmenter: jieba # Only jieba is supported now. diff --git a/examples/tts/g2p/conf/g2p_conformer_ctc.yaml b/examples/tts/g2p/conf/g2p_conformer_ctc.yaml index aa94405747fb..d1c4b0b9ae9f 100644 --- a/examples/tts/g2p/conf/g2p_conformer_ctc.yaml +++ b/examples/tts/g2p/conf/g2p_conformer_ctc.yaml @@ -1,4 +1,4 @@ -name: G2P_paper-Conformer-CTC +name: G2P-Conformer-CTC # Dataset info train_manifest: ??? diff --git a/nemo/collections/common/tokenizers/text_to_speech/tts_tokenizers.py b/nemo/collections/common/tokenizers/text_to_speech/tts_tokenizers.py index ac9cb45a2724..4e535589747e 100644 --- a/nemo/collections/common/tokenizers/text_to_speech/tts_tokenizers.py +++ b/nemo/collections/common/tokenizers/text_to_speech/tts_tokenizers.py @@ -605,7 +605,7 @@ def encode_from_g2p(self, g2p_text: List[str], raw_text: Optional[str] = None) - of graphemes, or a mixture of both. For example, `['ˈ', 's', 'i', ' ', '#O', '#O', '#V']`, which is the G2P_paper's output of the text "see OOV", where '#' is prepended to each grapheme in order to distinguish graphemes from phonemes if there are overlaps in between. The prefix '#' can be customized in - `nemo_text_processing.g2p.modules.IPAG2P.grapheme_prefix`. + `nemo.collections.tts.g2p.modules.IPAG2P.grapheme_prefix`. raw_text (str): the original text after calling `self.text_preprocessing_func`. It is optional. It is only used to deliver a warning message that some graphemes from the original text are skipped. diff --git a/nemo/collections/tts/g2p/modules.py b/nemo/collections/tts/g2p/modules.py index 6108098cfa74..636966395a3f 100644 --- a/nemo/collections/tts/g2p/modules.py +++ b/nemo/collections/tts/g2p/modules.py @@ -436,7 +436,7 @@ def _parse_phoneme_dict( continue # Note that latin character pattern should be consistent with - # nemo_text_processing.g2p.data.data_utils.LATIN_CHARS_ALL. It is advised to extend its character + # nemo.collections.tts.g2p.data.data_utils.LATIN_CHARS_ALL. It is advised to extend its character # coverage if adding the support of new languages. # TODO @xueyang: unify hardcoded range of characters with LATIN_CHARS_ALL to avoid duplicates. line = normalize_unicode_text(line) diff --git a/nemo/collections/tts/torch/tts_dataset.yaml b/nemo/collections/tts/torch/tts_dataset.yaml index fb7aef09b955..2ca29630910e 100644 --- a/nemo/collections/tts/torch/tts_dataset.yaml +++ b/nemo/collections/tts/torch/tts_dataset.yaml @@ -22,7 +22,7 @@ tts_dataset: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: en input_case: cased - whitelist: "nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv" + whitelist: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" text_normalizer_call_kwargs: verbose: False @@ -41,6 +41,6 @@ tts_dataset: add_blank_at: null pad_with_space: True g2p: - _target_: nemo_text_processing.g2p.modules.EnglishG2p + _target_: nemo.collections.tts.g2p.modules.EnglishG2p phoneme_dict: "scripts/tts_dataset_files/cmudict-0.7b_nv22.10" heteronyms: "scripts/tts_dataset_files/heteronyms-052722" diff --git a/scripts/dataset_processing/tts/ljspeech/ds_conf/ds_for_fastpitch_align.yaml b/scripts/dataset_processing/tts/ljspeech/ds_conf/ds_for_fastpitch_align.yaml index cbb124fdef1f..8e66cb3497aa 100644 --- a/scripts/dataset_processing/tts/ljspeech/ds_conf/ds_for_fastpitch_align.yaml +++ b/scripts/dataset_processing/tts/ljspeech/ds_conf/ds_for_fastpitch_align.yaml @@ -3,7 +3,7 @@ name: "ds_for_fastpitch_align" manifest_filepath: "train_manifest.json" sup_data_path: "sup_data" sup_data_types: [ "align_prior_matrix", "pitch" ] -whitelist_path: "nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv" +whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" phoneme_dict_path: "scripts/tts_dataset_files/cmudict-0.7b_nv22.10" heteronyms_path: "scripts/tts_dataset_files/heteronyms-052722" @@ -46,6 +46,6 @@ dataset: apostrophe: true pad_with_space: true g2p: - _target_: nemo_text_processing.g2p.modules.EnglishG2p + _target_: nemo.collections.tts.g2p.modules.EnglishG2p phoneme_dict: ${phoneme_dict_path} heteronyms: ${heteronyms_path} diff --git a/scripts/dataset_processing/tts/ljspeech/ds_conf/ds_for_mixer_tts.yaml b/scripts/dataset_processing/tts/ljspeech/ds_conf/ds_for_mixer_tts.yaml index 66222656d9b9..bfec225353d3 100644 --- a/scripts/dataset_processing/tts/ljspeech/ds_conf/ds_for_mixer_tts.yaml +++ b/scripts/dataset_processing/tts/ljspeech/ds_conf/ds_for_mixer_tts.yaml @@ -3,7 +3,7 @@ name: "ds_for_mixer_tts" manifest_filepath: "train_manifest.json" sup_data_path: "sup_data" sup_data_types: [ "align_prior_matrix", "pitch" ] -whitelist_path: "nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv" +whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" phoneme_dict_path: "scripts/tts_dataset_files/cmudict-0.7b_nv22.10" heteronyms_path: "scripts/tts_dataset_files/heteronyms-052722" @@ -46,6 +46,6 @@ dataset: apostrophe: true pad_with_space: true g2p: - _target_: nemo_text_processing.g2p.modules.EnglishG2p + _target_: nemo.collections.tts.g2p.modules.EnglishG2p phoneme_dict: ${phoneme_dict_path} heteronyms: ${heteronyms_path} diff --git a/scripts/dataset_processing/tts/ljspeech/ds_conf/ds_for_mixer_tts_x.yaml b/scripts/dataset_processing/tts/ljspeech/ds_conf/ds_for_mixer_tts_x.yaml index ec0aa391fa84..89cf74f6b7de 100644 --- a/scripts/dataset_processing/tts/ljspeech/ds_conf/ds_for_mixer_tts_x.yaml +++ b/scripts/dataset_processing/tts/ljspeech/ds_conf/ds_for_mixer_tts_x.yaml @@ -3,7 +3,7 @@ name: "ds_for_mixer_tts_x" manifest_filepath: "train_manifest.json" sup_data_path: "sup_data" sup_data_types: [ "align_prior_matrix", "pitch", "lm_tokens" ] -whitelist_path: "nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv" +whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" dataset: _target_: nemo.collections.tts.torch.data.MixerTTSXDataset diff --git a/scripts/dataset_processing/tts/ljspeech/get_data.py b/scripts/dataset_processing/tts/ljspeech/get_data.py index d165fcce25e9..0d933f8a29ca 100644 --- a/scripts/dataset_processing/tts/ljspeech/get_data.py +++ b/scripts/dataset_processing/tts/ljspeech/get_data.py @@ -56,7 +56,7 @@ def __extract_file(filepath, data_dir): def __process_data(data_root, whitelist_path): if whitelist_path is None: wget.download( - "https://raw.githubusercontent.com/NVIDIA/NeMo/main/nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv", + "https://raw.githubusercontent.com/NVIDIA/NeMo/main/scripts/tts_dataset_files/en/whitelist_lj_speech.tsv", out=str(data_root), ) whitelist_path = data_root / "lj_speech.tsv" diff --git a/scripts/dataset_processing/tts/sfbilingual/ds_conf/ds_for_fastpitch_align.yaml b/scripts/dataset_processing/tts/sfbilingual/ds_conf/ds_for_fastpitch_align.yaml index 12ba58a93dc7..f675e49954f0 100755 --- a/scripts/dataset_processing/tts/sfbilingual/ds_conf/ds_for_fastpitch_align.yaml +++ b/scripts/dataset_processing/tts/sfbilingual/ds_conf/ds_for_fastpitch_align.yaml @@ -3,7 +3,7 @@ name: "ds_for_fastpitch_align" manifest_filepath: "train_manifest.json" sup_data_path: "sup_data" sup_data_types: [ "align_prior_matrix", "pitch" ] -whitelist_path: "nemo_text_processing/text_normalization/zh/data/whitelist/default.tsv" +whitelist_path: null # default whitelist will be used from https://raw.githubusercontent.com/NVIDIA/NeMo-text-processing/main/nemo_text_processing/text_normalization/zh/data/whitelist/default.tsv phoneme_dict_path: "scripts/tts_dataset_files/zh/pinyin_dict_nv_22.10.txt" dataset: @@ -46,6 +46,6 @@ dataset: apostrophe: true pad_with_space: true g2p: - _target_: nemo_text_processing.g2p.modules.ChineseG2p + _target_: nemo.collections.tts.g2p.modules.ChineseG2p phoneme_dict: ${phoneme_dict_path} word_segmenter: jieba # Only jieba is supported now. diff --git a/scripts/tts_dataset_files/wordid_to_ipa-0.7b_nv22.10.tsv b/scripts/tts_dataset_files/wordid_to_ipa-0.7b_nv22.10.tsv new file mode 100644 index 000000000000..d46ae401895a --- /dev/null +++ b/scripts/tts_dataset_files/wordid_to_ipa-0.7b_nv22.10.tsv @@ -0,0 +1,326 @@ +ABSTRACT_ADJ-NOU ˈæbˌstɹækt +ABSTRACT_VRB æbˈstɹækt +ABUSE_NOU əbˈjus +ABUSE_VRB əbˈjuz +ABUSES_NOU əbˈjusɪz +ABUSES_VRB əbˈjuzɪz +ADDICT_NOU ˈæˌdɪkt +ADDICT_VRB əˈdɪkt +ADVOCATE_NOU ˈædvəkət +ADVOCATE_VRB ˈædvəˌkeɪt +AFFECT_NOU-PSY ˈæˌfɛkt +AFFECT əˈfɛkt +AFFILIATE_NOU əˈfɪliət +AFFILIATE_VRB əˈfɪliˌeɪt +AGED_ADJ ˈeɪdʒɪd +AGED ˈeɪdʒd +AGGREGATE_ADJ-NOU ˈæɡɹəɡət +AGGREGATE_VRB ˈæɡɹəɡeɪt +ALTERNATE_ADJ-NOU ˈɔltɚnət +ALTERNATE_VRB ˈɔltɚˌneɪt +ANALYSES_NOU æˈnælɪˌsiz +ANALYSES_VRB ænəˈlaɪˌzɪz +ANIMATE_ADJ-NOU ˈænəmət +ANIMATE_VRB ˈænəˌmeɪt +APPROPRIATE_ADJ əˈpɹoʊpɹiət +APPROPRIATE_VRB əˈpɹoʊpɹiˌeɪt +APPROXIMATE_ADJ-NOU əˈpɹɑksəmət +APPROXIMATE_VRB əˈpɹɑksəˌmeɪt +ARTICULATE_ADJ ɑɹˈtɪkjələt +ARTICULATE_VRB ɑɹˈtɪkjəˌleɪt +ASSOCIATE_ADJ-NOU əˈsoʊsiət +ASSOCIATE_VRB əˈsoʊsiˌeɪt +ATTRIBUTE_NOU ˈætɹəbˌjut +ATTRIBUTE_VRB əˈtɹɪbˌjut +AUGUST_ADJ ˌɑˈɡəst +AUGUST ˈɑɡəst +AUGUST_NAM ˈaʊˌɡʊst +AXES_NOU ˈækˌsiz +AXES_NOU-VRB ˈækˌsɪz +BASS ˈbeɪs +BASS_CORP ˈbæs +BLESSED_ADJ ˈblɛsəd +BLESSED_VRB ˈblɛst +BOLOGNA_GEO bəˈloʊˌŋɑ +BOLOGNA bəˈloʊni +BOW_NOU-KNOT ˈboʊ +BOW_NOU-SHIP ˈbaʊ +BUFFET_NOU bəˈfeɪ +BUFFET_VRB ˈbʌfət +CELTIC_ADJ-NOU-SPORTS ˈsɛltɪk +CELTIC ˈkɛltɪk +CLOSE_ADJ-NOU ˈkloʊs +CLOSE_VRB ˈkloʊz +COMBINE_NOU ˈkɑmbaɪn +COMBINE_VRB kəmˈbaɪn +COMPACT_ADJ-NOU ˈkɑmpækt +COMPACT_VRB kəmˈpækt +COMPOUND_NOU ˈkɑmpaʊnd +COMPOUND_VRB kəmˈpaʊnd +COMPRESS_NOU ˈkɑmpɹɛs +COMPRESS kəmˈpɹɛs +CONDUCT_NOU ˈkɑndəkt +CONDUCT_VRB kɑnˈdʌkt +CONFINES_NOU ˈkɑnˌfaɪnz +CONFINES_VRB kənˈfaɪnz +CONFLICT_NOU ˈkɑnflɪkt +CONFLICT_VRB kənˈflɪkt +CONGLOMERATE_ADJ-NOU kənˈɡlɑmɚət +CONGLOMERATE_VRB kənˈɡlɑmɝˌeɪt +CONJUGATE_ADJ-NOU ˈkɑndʒəˌɡeɪt +CONJUGATE_VRB ˈkɑndʒəˌɡeɪt +CONSCRIPT_NOU ˈkɑnsˌkɹɪpt +CONSCRIPT_VRB kənsˈkɹɪpt +CONSOLE_NOU ˈkɑnsoʊl +CONSOLE_VRB kənˈsoʊl +CONSORT_NOU ˈkɑnˌsɔɹt +CONSORT_VRB kənˈsɔɹt +CONSTRUCT_NOU ˈkɑnstɹəkt +CONSTRUCT_VRB kənˈstɹʌkt +CONSUMMATE_ADJ ˈkɑnsəmət +CONSUMMATE_VRB ˈkɑnsəˌmeɪt +CONTENT_ADJ-NOU-VRB kənˈtɛnt +CONTENT_NOU ˈkɑntɛnt +CONTEST_NOU ˈkɑntɛst +CONTEST_VRB kənˈtɛst +CONTRACT_NOU ˈkɑnˌtɹækt +CONTRACT_VRB kənˈtɹækt +CONTRAST_NOU ˈkɑntɹæst +CONTRAST_VRB kənˈtɹæst +CONVERSE_ADJ-NOU ˈkɑnvɚs +CONVERSE_VRB kənˈvɝs +CONVERT_NOU ˈkɑnvɚt +CONVERT_VRB kənˈvɝt +CONVICT_NOU ˈkɑnvɪkt +CONVICT_VRB kənˈvɪkt +COORDINATE_ADJ-NOU koʊˈɔɹdənət +COORDINATE_VRB koʊˈɔɹdəˌneɪt +CORRELATE_NOU ˈkɔɹələt +CORRELATE_NOU-VRB ˈkɔɹəˌleɪt +DECREASE_NOU ˈdiˌkɹis +DECREASE_VRB dɪˈkɹis +DEFECT_NOU ˈdifɛkt +DEFECT_VRB dɪˈfɛkt +DEGENERATE_ADJ-NOU dɪˈdʒɛnɚət +DEGENERATE_VRB dɪˈdʒɛnɚˌeɪt +DELEGATE_NOU ˈdɛləɡət +DELEGATE_VRB ˈdɛləˌɡeɪt +DELIBERATE_ADJ dɪˈlɪbɚət +DELIBERATE_VRB dɪˈlɪbɚˌeɪt +DESERT_NOU ˈdɛzɚt +DESERT_VRB dɪˈzɝt +DEVIATE_NOU ˈdiˌviət +DEVIATE_VRB ˈdiviˌeɪt +DIAGNOSES_NOU ˌdaɪəɡˈnoʊsiz +DIAGNOSES_VRB ˌdaɪəɡˈnoʊsəz +DIFFUSE_ADJ dɪfˈjus +DIFFUSE_VRB dɪfˈjuz +DISCARD_NOU ˈdɪsˌkɑɹd +DISCARD_VRB dəsˈkɑɹd +DISCHARGE_NOU ˈdɪsˌtʃɑɹdʒ +DISCHARGE_VRB dɪsˈtʃɑɹdʒ +DISCOUNT_NOU ˈdɪskaʊnt +DISCOUNT_VRB dɪˈskaʊnt +DOCUMENT_NOU ˈdɑkjumɛnt +DOCUMENT_VRB ˈdɑkjəmɛnt +DOVE ˈdʌv +DOVE_VRB ˈdoʊv +DUPLICATE_ADJ-NOU ˈdupləkət +DUPLICATE_VRB ˈdupləˌkeɪt +ELABORATE_ADJ ɪˈlæbɹət +ELABORATE_VRB ɪˈlæbɚˌeɪt +ENTRANCE_NOU ˈɛntɹəns +ENTRANCE_VRB əˈntɹæns +ESCORT_NOU ˈɛskɔɹt +ESCORT_VRB ɛˈskɔɹt +ESTIMATE_NOU ˈɛstəmət +ESTIMATE_VRB ˈɛstəˌmeɪt +EXCUSE_NOU ɪkˈskjus +EXCUSE_VRB ɪkˈskjuz +EXPATRIATE_NOU ɛkˈspeɪtɹiət +EXPATRIATE_VRB ɛkˈspeɪtɹiˌeɪt +EXPLOIT_NOU ˈɛksˌplɔɪt +EXPLOIT_VRB ˌɛksˈplɔɪt +EXPORT_NOU ˈɛkspɔɹt +EXPORT_VRB əkˈspɔɹt +EXPOSE_NOU ˌɛkˌspoʊˈzeɪ +EXPOSE_VRB ɪkˈspoʊz +EXTRACT_NOU ˈɛkˌstɹækt +EXTRACT_VRB ɪkˈstɹækt +FRAGMENT_NOU ˈfɹæɡmənt +FRAGMENT_VRB ˌfɹæɡˈmɛnt +FREQUENT_ADJ ˈfɹikwənt +FREQUENT_VRB ˌfɹiˈkwɛnt +GRADUATE_ADJ-NOU ˈɡɹædʒuwət +GRADUATE_VRB ˈɡɹædʒuˌeɪt +HOUSE_NOU ˈhaʊs +HOUSE_VRB ˈhaʊz +IMPACT_NOU ˈɪmpækt +IMPACT_VRB ˌɪmˈpækt +IMPLANT_NOU ˈɪmˌplænt +IMPLANT_VRB ˌɪmˈplænt +IMPLEMENT_NOU ˈɪmpləmənt +IMPLEMENT_VRB ˈɪmpləˌmɛnt +IMPORT_NOU ˈɪmˌpɔɹt +IMPORT_VRB ˌɪmˈpɔɹt +INCENSE_NOU ˈɪnˌsɛns +INCENSE_VRB ˌɪnˈsɛns +INCLINE_NOU ˈɪnklaɪn +INCLINE_VRB ˌɪnˈklaɪn +INCREASE_NOU ˈɪnˌkɹis +INCREASE_VRB ˌɪnˈkɹis +INCREMENT_NOU ˈɪnkɹəmənt +INCREMENT_VRB ˈɪnkɹəˌmɛnt +INITIATE_NOU əˈnɪˌʃiət +INITIATE_VRB ˌɪˈnɪʃiˌeɪt +INSERT_NOU ˈɪnˌsɝt +INSERT_VRB ˌɪnˈsɝt +INSTRUMENT_NOU ˈɪnstɹəmənt +INSTRUMENT_VRB ˈɪnstɹəˌmɛnt +INSULT_NOU ˈɪnˌsʌlt +INSULT_VRB ˌɪnˈsʌlt +INTERCHANGE_NOU ˈɪntɚˌtʃeɪndʒ +INTERCHANGE_VRB ˌɪntɚˈtʃeɪndʒ +INTIMATE_ADJ ˈɪntəmət +INTIMATE_VRB ˈɪntɪˌmeɪt +INTRIGUE_NOU ˈɪntɹiɡ +INTRIGUE_NOU-VRB ˌɪnˈtɹiɡ +INVALID_ADJ ˌɪnˈvæləd +INVALID_NOU ˈɪnvələd +INVERT_ADJ-NOU ˈɪnvɝt +INVERT_VRB ˌɪnˈvɝt +INVITE_NOU ˈɪnˌvaɪt +INVITE_VRB ˌɪnˈvaɪt +ISOLATE_NOU ˈaɪsələt +ISOLATE ˈaɪsəˌleɪt +JESUS ˈdʒizəs +JESUS_ES ˌheɪˈsus +JOB_BIBLE ˈdʒoʊb +JOB ˈdʒɑb +LAMINATE_NOU ˈlæmənət +LAMINATE_VRB ˈlæməˌneɪt +LEAD_NOU ˈlɛd +LEAD_NOU-VRB ˈlid +LEARNED_ADJ ˈlɝnɪd +LEARNED_VRB ˈlɝnd +LIVE_ADJ ˈlaɪv +LIVE_VRB ˈlɪv +LIVES_NOU ˈlaɪvz +LIVES_VRB ˈlɪvz +MATE_NOU ˈmɑˌteɪ +MATE ˈmeɪt +MINUTE_ADJ maɪˈnut +MINUTE ˈmɪnət +MISUSE_NOU mɪsˈjus +MISUSE_VRB mɪsˈjuz +MOBILE ˈmoʊbəl +MOBILE_GEO ˌmoʊˈbil +MOBILE_NOU-ART ˈmoʊˌbil +MODERATE_ADJ-NOU ˈmɑdɚət +MODERATE_VRB ˈmɑdɚˌeɪt +MOPED_NOU ˈmoʊpɛd +MOPED_VRB ˈmoʊpt +MOUTH_NOU ˈmaʊθ +MOUTH_VRB ˈmaʊð +NESTLE_NAM ˈnɛsˈli +NESTLE_VRB ˈnɛsəl +OBJECT_NOU ˈɑbdʒɛkt +OBJECT_VRB əbˈdʒɛkt +ORNAMENT_NOU ˈɔɹnəmənt +ORNAMENT_VRB ˌɔɹnəˈmɛnt +OVERTHROW_NOU ˈoʊvɚˌθɹoʊ +OVERTHROW_VRB ˌoʊvɚˈθɹoʊ +PASTY_ADJ ˈpeɪˌsti +PASTY_NOU ˈpæsˌti +PERFECT_ADJ ˈpɝˌfɪkt +PERFECT_VRB pɚˈfɛkt +PERFUME_NOU ˈpɝˌfjum +PERFUME_VRB pɚˈfjum +PERMIT_NOU ˈpɝˌmɪt +PERMIT_VRB pɚˈmɪt +PERVERT_NOU ˈpɝvɚt +PERVERT_VRB pɚˈvɝt +PIGMENT_NOU ˈpɪɡmɛnt +PIGMENT_VRB pəɡˈmɛnt +POLISH_GEO ˈpoʊlɪʃ +POLISH ˈpɑlɪʃ +POSTULATE_NOU ˈpɑstʃələt +POSTULATE_VRB ˈpɑstʃəˌleɪt +PRECIPITATE_ADJ-NOU pɹəˈsɪpətət +PRECIPITATE_VRB pɹɪˈsɪpɪˌteɪt +PREDICATE_NOU ˈpɹɛdɪkət +PREDICATE_VRB ˈpɹɛdəˌkeɪt +PRESENT_ADJ-NOU ˈpɹɛzənt +PRESENT_VRB pɹiˈzɛnt +PRODUCE_NOU ˈpɹoʊdus +PRODUCE_VRB pɹəˈdus +PROGRESS_NOU ˈpɹɑˌɡɹɛs +PROGRESS_VRB pɹəˈɡɹɛs +PROJECT_NOU ˈpɹɑdʒɛkt +PROJECT_VRB pɹɑˈdʒɛkt +PROTEST_NOU ˈpɹoʊˌtɛst +PROTEST_VRB pɹəˈtɛst +RAVEL_NAM ɹəˈvɛl +RAVEL_NOU ˈɹævəl +READING_EN ˈɹidɪŋ +READING_GEO ˈɹɛdɪŋ +READ_PAST ˈɹɛd +READ_PRESENT ˈɹid +REBEL_NOU ˈɹɛbəl +REBEL_VRB ɹɪˈbɛl +RECORD_NOU ˈɹɛkɚd +RECORD_VRB ɹəˈkɔɹd +RECOUNT_NOU ˈɹiˌkaʊnt +RECOUNT_VRB ˌɹiˈkaʊnt +REFUND_NOU ˈɹiˌfʌnd +REFUND_VRB ɹɪˈfʌnd +REFUSE_NOU ˈɹɛfˌjuz +REFUSE_VRB ɹəfˈjuz +REJECT_NOU ˈɹidʒɛkt +REJECT_VRB ɹɪˈdʒɛkt +RERELEASE ˈɹiɹəˌlis +RERELEASE_VRB ˌɹiɹəˈlis +RESUME_NOU ˈɹɛzəˌmeɪ +RESUME_VRB ɹiˈzum +RETARD_NOU ˈɹitɑɹd +RETARD_VRB ɹɪˈtɑɹd +RODEO_GEO ˌɹoʊˈdeɪˌoʊ +RODEO ˈɹoʊdiˌoʊ +ROW_1 ˈɹoʊ +ROW_2 ˈɹaʊ +SAKE_JP ˈsɑˌkeɪ +SAKE ˈseɪk +SEPARATE_ADJ ˈsɛpɚɪt +SEPARATE_VRB ˈsɛpɚˌeɪt +SOW_NOU ˈsaʊ +SOW ˈsoʊ +SUBJECT_ADJ-NOU ˈsʌbdʒɪkt +SUBJECT_VRB səbˈdʒɛkt +SUBORDINATE_ADJ-NOU səˈbɔɹdənət +SUBORDINATE_VRB səˈbɔɹdəˌneɪt +SUPPLEMENT_NOU ˈsʌpləmənt +SUPPLEMENT_VRB ˈsʌpləmənt +SUSPECT_ADJ-NOU ˈsʌsˌpɛkt +SUSPECT_VRB səˈspɛkt +SYNDICATE_NOU ˈsɪndɪkət +SYNDICATE_VRB ˈsɪndəˌkeɪt +TEAR_NOU ˈtɪɹ +TEAR_VRB ˈtɛɹ +TRANSFORM_NOU ˈtɹænsfɔɹm +TRANSFORM tɹænsˈfɔɹm +TRANSPLANT_NOU ˈtɹænsˌplænt +TRANSPLANT_VRB tɹænsˈplænt +TRANSPORT_NOU ˈtɹænspɔɹt +TRANSPORT_VRB tɹænˈspɔɹt +UPSET_NOU ˈʌpˌsɛt +UPSET_VRB əpˈsɛt +USES_NOU ˈjusəz +USES_VRB ˈjuzəz +USE_NOU ˈjus +USE_VRB ˈjuz +WIND_NOU ˈwɪnd +WIND_VRB ˈwaɪnd +WINDS_NOU ˈwɪndz +WINDS_VRB ˈwaɪndz +WOUND_NOU-VRB ˈwund +WOUND_VRB ˈwaʊnd diff --git a/tests/collections/asr/test_text_to_text_dataset.py b/tests/collections/asr/test_text_to_text_dataset.py index 3c8951f0141f..a2f387e22c82 100644 --- a/tests/collections/asr/test_text_to_text_dataset.py +++ b/tests/collections/asr/test_text_to_text_dataset.py @@ -96,7 +96,7 @@ def asr_tokenizer(test_data_dir): def tts_tokenizer(): @dataclass class G2PConfig: - _target_: str = "nemo_text_processing.g2p.modules.EnglishG2p" + _target_: str = "nemo.collections.tts.g2p.modules.EnglishG2p" phoneme_dict: str = str(BASE_DIR / "scripts/tts_dataset_files/cmudict-0.7b_nv22.10") heteronyms: str = str(BASE_DIR / "scripts/tts_dataset_files/heteronyms-052722") phoneme_probability: float = 0.5 diff --git a/tutorials/tts/FastPitch_ChineseTTS_Training.ipynb b/tutorials/tts/FastPitch_ChineseTTS_Training.ipynb index e0c58fdf6ebd..0542a006ed91 100644 --- a/tutorials/tts/FastPitch_ChineseTTS_Training.ipynb +++ b/tutorials/tts/FastPitch_ChineseTTS_Training.ipynb @@ -433,7 +433,7 @@ " apostrophe: true\n", " pad_with_space: true\n", " g2p:\n", - " _target_: nemo_text_processing.g2p.modules.ChineseG2p\n", + " _target_: nemo.collections.tts.g2p.modules.ChineseG2p\n", " phoneme_dict: ${phoneme_dict_path}\n", "```\n", "\n", diff --git a/tutorials/tts/FastPitch_MixerTTS_Training.ipynb b/tutorials/tts/FastPitch_MixerTTS_Training.ipynb index 1b2ebc66ea3b..52d555faa934 100644 --- a/tutorials/tts/FastPitch_MixerTTS_Training.ipynb +++ b/tutorials/tts/FastPitch_MixerTTS_Training.ipynb @@ -198,7 +198,7 @@ "metadata": {}, "outputs": [], "source": [ - "from nemo_text_processing.g2p.modules import EnglishG2p\n", + "from nemo.collections.tts.g2p.modules import EnglishG2p\n", "from nemo.collections.tts.torch.data import TTSDataset\n", "from nemo_text_processing.text_normalization.normalize import Normalizer\n", "from nemo.collections.common.tokenizers.text_to_speech.tts_tokenizers import EnglishPhonemesTokenizer, EnglishCharsTokenizer" @@ -230,7 +230,7 @@ "!mkdir -p tts_dataset_files && cd tts_dataset_files \\\n", "&& wget https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/scripts/tts_dataset_files/cmudict-0.7b_nv22.10 \\\n", "&& wget https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/scripts/tts_dataset_files/heteronyms-052722 \\\n", - "&& wget https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv \\\n", + "&& wget https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/scripts/tts_dataset_files/en/whitelist_lj_speech.tsv \\\n", "&& cd .." ] }, diff --git a/tutorials/tts/NeMo_TTS_Primer.ipynb b/tutorials/tts/NeMo_TTS_Primer.ipynb index a0e0b3f7097a..e323536fa56b 100644 --- a/tutorials/tts/NeMo_TTS_Primer.ipynb +++ b/tutorials/tts/NeMo_TTS_Primer.ipynb @@ -2072,4 +2072,4 @@ }, "nbformat": 4, "nbformat_minor": 1 -} \ No newline at end of file +} diff --git a/tutorials/tts/Pronunciation_customization.ipynb b/tutorials/tts/Pronunciation_customization.ipynb index f6ae09f7aa8a..14269ce33455 100644 --- a/tutorials/tts/Pronunciation_customization.ipynb +++ b/tutorials/tts/Pronunciation_customization.ipynb @@ -85,7 +85,7 @@ "source": [ "import os\n", "import nemo.collections.tts as nemo_tts\n", - "from nemo_text_processing.g2p.modules import IPAG2P\n", + "from nemo.collections.tts.g2p.modules import IPAG2P\n", "import soundfile as sf\n", "import IPython.display as ipd\n", "import torch\n", diff --git a/tutorials/tts/Tacotron2_Training.ipynb b/tutorials/tts/Tacotron2_Training.ipynb index 3642a3e9e4dc..28ca250a8b58 100644 --- a/tutorials/tts/Tacotron2_Training.ipynb +++ b/tutorials/tts/Tacotron2_Training.ipynb @@ -171,7 +171,7 @@ " \n", "!(mkdir -p nemo_text_processing/text_normalization/en/data/whitelist/ \\\n", " && cd nemo_text_processing/text_normalization/en/data/whitelist/ \\\n", - " && wget https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv \\\n", + " && wget https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/scripts/tts_dataset_files/en/whitelist_lj_speech.tsv \\\n", " && cd ..)" ] }, @@ -235,7 +235,7 @@ "\n", "phoneme_dict_path: \"scripts/tts_dataset_files/cmudict-0.7b_nv22.10\"\n", "heteronyms_path: \"scripts/tts_dataset_files/heteronyms-052722\"\n", - "whitelist_path: \"nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv\"\n", + "whitelist_path: \"scripts/tts_dataset_files/en/whitelist_lj_speech.tsv\"\n", "```\n", "\n", "The first part of the yaml defines dataset parameters used by Tacotron. Then in the head of 'model' section there are processing - related parameters. You can see\n", From dec4c0bc4b36f123d8aa889d5ff19eb4557ced90 Mon Sep 17 00:00:00 2001 From: ekmb Date: Fri, 10 Feb 2023 12:35:13 -0800 Subject: [PATCH 04/19] fix import Signed-off-by: ekmb --- nemo/collections/tts/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nemo/collections/tts/__init__.py b/nemo/collections/tts/__init__.py index 2370d83948ce..75b3b35a8ad4 100644 --- a/nemo/collections/tts/__init__.py +++ b/nemo/collections/tts/__init__.py @@ -14,3 +14,4 @@ import nemo.collections.tts.helpers import nemo.collections.tts.models +import nemo.collections.tts.g2p From 48683abd6383531dbd3e3f675dc3b7eebff521f6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 10 Feb 2023 20:36:50 +0000 Subject: [PATCH 05/19] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- nemo/collections/tts/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nemo/collections/tts/__init__.py b/nemo/collections/tts/__init__.py index 75b3b35a8ad4..d122a46d2a02 100644 --- a/nemo/collections/tts/__init__.py +++ b/nemo/collections/tts/__init__.py @@ -12,6 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. +import nemo.collections.tts.g2p import nemo.collections.tts.helpers import nemo.collections.tts.models -import nemo.collections.tts.g2p From 09e881fed74709b31d78cd28e77023defdc431e3 Mon Sep 17 00:00:00 2001 From: ekmb Date: Fri, 10 Feb 2023 13:13:54 -0800 Subject: [PATCH 06/19] add missing init Signed-off-by: ekmb --- nemo/collections/tts/g2p/__init__.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 nemo/collections/tts/g2p/__init__.py diff --git a/nemo/collections/tts/g2p/__init__.py b/nemo/collections/tts/g2p/__init__.py new file mode 100644 index 000000000000..a1cf281f0908 --- /dev/null +++ b/nemo/collections/tts/g2p/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. From 71b061668c85b64db5e02ce6d4c29d886dc71f8a Mon Sep 17 00:00:00 2001 From: ekmb Date: Fri, 10 Feb 2023 13:15:55 -0800 Subject: [PATCH 07/19] fix import Signed-off-by: ekmb --- nemo/collections/tts/g2p/data/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nemo/collections/tts/g2p/data/__init__.py b/nemo/collections/tts/g2p/data/__init__.py index 5857cda3f17b..1162dacc7031 100644 --- a/nemo/collections/tts/g2p/data/__init__.py +++ b/nemo/collections/tts/g2p/data/__init__.py @@ -13,5 +13,6 @@ # limitations under the License. from nemo.collections.tts.g2p.data.t5_g2p import T5G2PDataset +from nemo.collections.tts.g2p.data.ctc_g2p import CTCG2PBPEDataset __all__ = ["T5G2PDataset", "CTCG2PBPEDataset"] From 1fc2f822798a41cf686e80dfb649c4fa48bc9475 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 10 Feb 2023 21:17:02 +0000 Subject: [PATCH 08/19] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- nemo/collections/tts/g2p/data/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nemo/collections/tts/g2p/data/__init__.py b/nemo/collections/tts/g2p/data/__init__.py index 1162dacc7031..d74b1a7ebee6 100644 --- a/nemo/collections/tts/g2p/data/__init__.py +++ b/nemo/collections/tts/g2p/data/__init__.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from nemo.collections.tts.g2p.data.t5_g2p import T5G2PDataset from nemo.collections.tts.g2p.data.ctc_g2p import CTCG2PBPEDataset +from nemo.collections.tts.g2p.data.t5_g2p import T5G2PDataset __all__ = ["T5G2PDataset", "CTCG2PBPEDataset"] From 0399299108dcf5c5b3a2ec3ade0a02893fea3af1 Mon Sep 17 00:00:00 2001 From: ekmb Date: Fri, 10 Feb 2023 15:15:09 -0800 Subject: [PATCH 09/19] rename unit test Signed-off-by: ekmb --- .../tts/g2p/data/{test_data_utils.py => test_g2p_data_utils.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/collections/tts/g2p/data/{test_data_utils.py => test_g2p_data_utils.py} (100%) diff --git a/tests/collections/tts/g2p/data/test_data_utils.py b/tests/collections/tts/g2p/data/test_g2p_data_utils.py similarity index 100% rename from tests/collections/tts/g2p/data/test_data_utils.py rename to tests/collections/tts/g2p/data/test_g2p_data_utils.py From fcc8f08e24936c029222b24ca513aebc3e13fadd Mon Sep 17 00:00:00 2001 From: ekmb Date: Fri, 10 Feb 2023 22:10:22 -0800 Subject: [PATCH 10/19] fix import Signed-off-by: ekmb --- examples/tts/conf/rad-tts_dec.yaml | 2 +- examples/tts/conf/rad-tts_dec_ipa.yaml | 8 ++++---- examples/tts/conf/rad-tts_feature_pred.yaml | 2 +- examples/tts/conf/rad-tts_feature_pred_ipa.yaml | 8 ++++---- nemo/collections/tts/torch/g2ps.py | 1 + 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/examples/tts/conf/rad-tts_dec.yaml b/examples/tts/conf/rad-tts_dec.yaml index 7e676e7985da..6192d1538399 100644 --- a/examples/tts/conf/rad-tts_dec.yaml +++ b/examples/tts/conf/rad-tts_dec.yaml @@ -8,7 +8,7 @@ export_dir: ??? sup_data_path: ??? sup_data_types: ["log_mel", "align_prior_matrix", "pitch", "voiced_mask", "p_voiced", "energy"] -whitelist_path: "nemo_text_processing/text_normalization/en/data/whitelist/tts.tsv" +whitelist_path: null # https://github.com/NVIDIA/NeMo-text-processing/blob/main/nemo_text_processing/text_normalization/en/data/whitelist/tts.tsv default will be used # these frame-wise values depend on pitch_fmin and pitch_fmax, you can get values # by running `scripts/dataset_processing/tts/extract_sup_data.py` diff --git a/examples/tts/conf/rad-tts_dec_ipa.yaml b/examples/tts/conf/rad-tts_dec_ipa.yaml index b251537daad8..f135eb713b1c 100644 --- a/examples/tts/conf/rad-tts_dec_ipa.yaml +++ b/examples/tts/conf/rad-tts_dec_ipa.yaml @@ -31,7 +31,7 @@ window: "hann" phoneme_dict_path: "scripts/tts_dataset_files/ipa_cmudict-0.7b_nv22.10.txt" heteronyms_path: "scripts/tts_dataset_files/heteronyms-052722" -whitelist_path: "nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv" +whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" mapping_file_path: "" model: @@ -62,7 +62,7 @@ model: apostrophe: true pad_with_space: true g2p: - _target_: nemo_text_processing.g2p.modules.IPAG2P + _target_: nemo.collections.tts.g2p.modules.IPAG2P phoneme_dict: ${phoneme_dict_path} heteronyms: ${heteronyms_path} phoneme_probability: 0.5 @@ -106,7 +106,7 @@ model: add_blank_at: null pad_with_space: True g2p: - _target_: "nemo_text_processing.g2p.modules.EnglishG2p" + _target_: "nemo.collections.tts.g2p.modules.EnglishG2p" phoneme_dict: ${phoneme_dict_path} heteronyms: ${heteronyms_path} phoneme_probability: 0.5 @@ -150,7 +150,7 @@ model: add_blank_at: null pad_with_space: True g2p: - _target_: "nemo_text_processing.g2p.modules.EnglishG2p" + _target_: "nemo.collections.tts.g2p.modules.EnglishG2p" phoneme_dict: ${phoneme_dict_path} heteronyms: ${heteronyms_path} phoneme_probability: 0.5 diff --git a/examples/tts/conf/rad-tts_feature_pred.yaml b/examples/tts/conf/rad-tts_feature_pred.yaml index 9aee2d4a6264..58c7481f0674 100644 --- a/examples/tts/conf/rad-tts_feature_pred.yaml +++ b/examples/tts/conf/rad-tts_feature_pred.yaml @@ -8,7 +8,7 @@ export_dir: ??? sup_data_path: ??? sup_data_types: ["log_mel", "align_prior_matrix", "pitch", "voiced_mask", "p_voiced", "energy"] -whitelist_path: "nemo_text_processing/text_normalization/en/data/whitelist/tts.tsv" +whitelist_path: null # default https://github.com/NVIDIA/NeMo-text-processing/blob/main/nemo_text_processing/text_normalization/en/data/whitelist/tts.tsv will be used # these frame-wise values depend on pitch_fmin and pitch_fmax, you can get values # by running `scripts/dataset_processing/tts/extract_sup_data.py` diff --git a/examples/tts/conf/rad-tts_feature_pred_ipa.yaml b/examples/tts/conf/rad-tts_feature_pred_ipa.yaml index cf36d7ad9506..9e0615b1f81b 100644 --- a/examples/tts/conf/rad-tts_feature_pred_ipa.yaml +++ b/examples/tts/conf/rad-tts_feature_pred_ipa.yaml @@ -29,7 +29,7 @@ window: "hann" phoneme_dict_path: "scripts/tts_dataset_files/ipa_cmudict-0.7b_nv22.10.txt" heteronyms_path: "scripts/tts_dataset_files/heteronyms-052722" -whitelist_path: "nemo_text_processing/text_normalization/en/data/whitelist/lj_speech.tsv" +whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" mapping_file_path: "" model: @@ -60,7 +60,7 @@ model: apostrophe: true pad_with_space: true g2p: - _target_: nemo_text_processing.g2p.modules.IPAG2P + _target_: nemo.collections.tts.g2p.modules.IPAG2P phoneme_dict: ${phoneme_dict_path} heteronyms: ${heteronyms_path} phoneme_probability: 0.5 @@ -104,7 +104,7 @@ model: add_blank_at: null pad_with_space: True g2p: - _target_: "nemo_text_processing.g2p.modules.EnglishG2p" + _target_: "nemo.collections.tts.g2p.modules.EnglishG2p" phoneme_dict: ${phoneme_dict_path} heteronyms: ${heteronyms_path} phoneme_probability: 0.5 @@ -148,7 +148,7 @@ model: add_blank_at: null pad_with_space: True g2p: - _target_: "nemo_text_processing.g2p.modules.EnglishG2p" + _target_: "nemo.collections.tts.g2p.modules.EnglishG2p" phoneme_dict: ${phoneme_dict_path} heteronyms: ${heteronyms_path} phoneme_probability: 0.5 diff --git a/nemo/collections/tts/torch/g2ps.py b/nemo/collections/tts/torch/g2ps.py index e6347a0657a9..4bdbde5e6380 100644 --- a/nemo/collections/tts/torch/g2ps.py +++ b/nemo/collections/tts/torch/g2ps.py @@ -14,3 +14,4 @@ # TODO @xueyang: deprecate this file since no other places import modules from here anymore. However, # all checkpoints uploaded in ngc used this path. So it requires to update all ngc checkpoints g2p path as well. +from nemo.collections.tts.g2p.modules import IPAG2P, BaseG2p, EnglishG2p From 6bcdbf1bc1d5c2071438e14ecb5eee4189d45f60 Mon Sep 17 00:00:00 2001 From: ekmb Date: Fri, 10 Feb 2023 22:47:43 -0800 Subject: [PATCH 11/19] fix modules test Signed-off-by: ekmb --- tests/collections/tts/g2p/test_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/collections/tts/g2p/test_modules.py b/tests/collections/tts/g2p/test_modules.py index 196239c65f7f..dd2a1812fad2 100644 --- a/tests/collections/tts/g2p/test_modules.py +++ b/tests/collections/tts/g2p/test_modules.py @@ -325,7 +325,7 @@ def test_forward_call_en_us(self): expected_output_mixed = ( [char for char in "həˈɫoʊ ɛnˈvɪdiəz ˈɛɹˌpɔɹts ˈdʒoʊnzɪz ˈɛɹˌpɔɹts ˈwɝɫdz"] + [" "] - + [f"{self.GRAPHEME_PREFIX}{char}" for char in "Kitty"] + + [f"{self.GRAPHEME_PREFIX}{char}" for char in "kitty"] + ["!"] ) From bfead4bcad9848f38c6278a4080324baf71faaba Mon Sep 17 00:00:00 2001 From: ekmb Date: Fri, 10 Feb 2023 23:38:23 -0800 Subject: [PATCH 12/19] fix imports Signed-off-by: ekmb --- examples/tts/g2p/conf/g2p_conformer_ctc.yaml | 4 ++-- examples/tts/g2p/conf/heteronym_classification.yaml | 6 +++--- examples/tts/g2p/conf/t5_g2p.yaml | 6 +++--- examples/tts/g2p/g2p_inference.py | 2 +- examples/tts/g2p/g2p_train_and_evaluate.py | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/tts/g2p/conf/g2p_conformer_ctc.yaml b/examples/tts/g2p/conf/g2p_conformer_ctc.yaml index d1c4b0b9ae9f..10e3ac3ec3d1 100644 --- a/examples/tts/g2p/conf/g2p_conformer_ctc.yaml +++ b/examples/tts/g2p/conf/g2p_conformer_ctc.yaml @@ -69,7 +69,7 @@ model: train_ds: manifest_filepath: ${train_manifest} dataset: - _target_: "nemo.tts.g2p.data.ctc_g2p.CTCG2PBPEDataset" + _target_: "nemo.collections.tts.g2p.data.ctc_g2p.CTCG2PBPEDataset" phoneme_field: "text" # name of the field in manifest_filepath for ground truth phonemes grapheme_field: "text_graphemes" # name of the field in manifest_filepath for input grapheme text dataloader_params: @@ -81,7 +81,7 @@ model: validation_ds: manifest_filepath: ${validation_manifest} dataset: - _target_: "nemo.tts.g2p.data.ctc_g2p.CTCG2PBPEDataset" + _target_: "nemo.collections.tts.g2p.data.ctc_g2p.CTCG2PBPEDataset" phoneme_field: "text" # name of the field in manifest_filepath for ground truth phonemes grapheme_field: "text_graphemes" # name of the field in manifest_filepath for input grapheme text dataloader_params: diff --git a/examples/tts/g2p/conf/heteronym_classification.yaml b/examples/tts/g2p/conf/heteronym_classification.yaml index 89c2899230de..aae427a36597 100644 --- a/examples/tts/g2p/conf/heteronym_classification.yaml +++ b/examples/tts/g2p/conf/heteronym_classification.yaml @@ -35,7 +35,7 @@ model: train_ds: dataset: - _target_: "nemo.tts.g2p.data.heteronym_classification_data.HeteronymClassificationDataset" + _target_: "nemo.collections.tts.g2p.data.heteronym_classification_data.HeteronymClassificationDataset" manifest: ${train_manifest} grapheme_field: "text_graphemes" # name of the field in manifest for input grapheme text dataloader_params: @@ -46,7 +46,7 @@ model: validation_ds: dataset: - _target_: "nemo.tts.g2p.data.heteronym_classification_data.HeteronymClassificationDataset" + _target_: "nemo.collections.tts.g2p.data.heteronym_classification_data.HeteronymClassificationDataset" manifest: ${validation_manifest} grapheme_field: "text_graphemes" # name of the field in manifest for input grapheme text dataloader_params: @@ -57,7 +57,7 @@ model: test_ds: dataset: - _target_: "nemo.tts.g2p.data.heteronym_classification_data.HeteronymClassificationDataset" + _target_: "nemo.collections.tts.g2p.data.heteronym_classification_data.HeteronymClassificationDataset" manifest: ${test_manifest} grapheme_field: "text_graphemes" # name of the field in manifest for input grapheme text dataloader_params: diff --git a/examples/tts/g2p/conf/t5_g2p.yaml b/examples/tts/g2p/conf/t5_g2p.yaml index 24eca43ce12f..9b4929656666 100644 --- a/examples/tts/g2p/conf/t5_g2p.yaml +++ b/examples/tts/g2p/conf/t5_g2p.yaml @@ -17,7 +17,7 @@ model: train_ds: manifest_filepath: ${train_manifest} dataset: - _target_: "nemo.tts.g2p.data.t5_g2p.T5G2PDataset" + _target_: "nemo.collections.tts.g2p.data.t5_g2p.T5G2PDataset" phoneme_field: "text" # name of the field in manifest_filepath for ground truth phonemes grapheme_field: "text_graphemes" # name of the field in manifest_filepath for input grapheme text dataloader_params: @@ -29,7 +29,7 @@ model: validation_ds: manifest_filepath: ${validation_manifest} dataset: - _target_: "nemo.tts.g2p.data.t5_g2p.T5G2PDataset" + _target_: "nemo.collections.tts.g2p.data.t5_g2p.T5G2PDataset" phoneme_field: "text" # name of the field in manifest_filepath for ground truth phonemes grapheme_field: "text_graphemes" # name of the field in manifest_filepath for input grapheme text dataloader_params: @@ -41,7 +41,7 @@ model: test_ds: manifest_filepath: ${test_manifest} dataset: - _target_: "nemo.tts.g2p.data.t5_g2p.T5G2PDataset" + _target_: "nemo.collections.tts.g2p.data.t5_g2p.T5G2PDataset" phoneme_field: "text" # name of the field in manifest_filepath for ground truth phonemes grapheme_field: "text_graphemes" # name of the field in manifest_filepath for input grapheme text dataloader_params: diff --git a/examples/tts/g2p/g2p_inference.py b/examples/tts/g2p/g2p_inference.py index a178e474c7f8..4b8f48ef1319 100644 --- a/examples/tts/g2p/g2p_inference.py +++ b/examples/tts/g2p/g2p_inference.py @@ -21,7 +21,7 @@ from omegaconf import OmegaConf from utils import get_metrics -from nemo.collections.tts.g2p.models import G2PModel +from nemo.collections.tts.g2p.models.g2p_model import G2PModel from nemo.core.config import hydra_runner from nemo.utils import logging diff --git a/examples/tts/g2p/g2p_train_and_evaluate.py b/examples/tts/g2p/g2p_train_and_evaluate.py index 3627681ffe15..e2edef695180 100644 --- a/examples/tts/g2p/g2p_train_and_evaluate.py +++ b/examples/tts/g2p/g2p_train_and_evaluate.py @@ -19,7 +19,7 @@ from utils import get_model from nemo.collections.common.callbacks import LogEpochTimeCallback -from nemo.collections.tts.g2p.models import G2PModel +from nemo.collections.tts.g2p.models.g2p_model import G2PModel from nemo.core.config import hydra_runner from nemo.utils import logging, model_utils from nemo.utils.exp_manager import exp_manager From be645ed9be823919552e43821d2d4c827ae5c282 Mon Sep 17 00:00:00 2001 From: ekmb Date: Tue, 14 Feb 2023 17:35:26 -0800 Subject: [PATCH 13/19] remove whitelist from config Signed-off-by: ekmb --- docs/source/tts/configs.rst | 1 - docs/source/tts/datasets.rst | 4 +--- examples/tts/conf/aligner.yaml | 2 -- .../de/fastpitch_align_22050_grapheme.yaml | 3 --- .../conf/de/fastpitch_align_22050_mix.yaml | 2 -- .../de/fastpitch_align_44100_grapheme.yaml | 3 --- .../de/fastpitch_align_44100_phoneme.yaml | 3 --- examples/tts/conf/fastpitch_align_44100.yaml | 2 -- examples/tts/conf/fastpitch_align_ipa.yaml | 2 -- examples/tts/conf/fastpitch_align_v1.05.yaml | 2 -- examples/tts/conf/mixer-tts-x.yaml | 3 --- examples/tts/conf/mixer-tts.yaml | 2 -- examples/tts/conf/rad-tts_dec.yaml | 3 --- examples/tts/conf/rad-tts_dec_ipa.yaml | 2 -- examples/tts/conf/rad-tts_feature_pred.yaml | 3 --- .../tts/conf/rad-tts_feature_pred_ipa.yaml | 2 -- examples/tts/conf/tacotron2.yaml | 2 -- examples/tts/conf/vits.yaml | 2 -- examples/tts/conf/vits_44100.yaml | 2 -- .../tts/conf/zh/fastpitch_align_22050.yaml | 2 -- nemo/collections/tts/torch/tts_dataset.yaml | 1 - .../ds_conf/ds_for_fastpitch_align.yaml | 2 -- .../ds_conf/ds_for_fastpitch_align.yaml | 2 -- .../ljspeech/ds_conf/ds_for_mixer_tts.yaml | 2 -- .../ljspeech/ds_conf/ds_for_mixer_tts_x.yaml | 2 -- .../tts/ljspeech/get_data.py | 2 +- .../tts/ljspeech/lj_speech.tsv | 21 +++++++++++++++++++ .../ds_conf/ds_for_fastpitch_align.yaml | 2 -- .../ds_conf/ds_for_fastpitch_align.yaml | 2 -- .../tts/FastPitch_ChineseTTS_Training.ipynb | 8 +------ tutorials/tts/FastPitch_Finetuning.ipynb | 5 +---- .../tts/FastPitch_GermanTTS_Training.ipynb | 10 +-------- .../tts/FastPitch_MixerTTS_Training.ipynb | 5 ----- tutorials/tts/Tacotron2_Training.ipynb | 6 ------ 34 files changed, 26 insertions(+), 91 deletions(-) create mode 100644 scripts/dataset_processing/tts/ljspeech/lj_speech.tsv diff --git a/docs/source/tts/configs.rst b/docs/source/tts/configs.rst index cecaee29673a..38e1629c0a68 100644 --- a/docs/source/tts/configs.rst +++ b/docs/source/tts/configs.rst @@ -96,7 +96,6 @@ Text normalization (TN) converts text from written form into its verbalized form _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: en input_case: cased - whitelist: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" text_normalizer_call_kwargs: verbose: false diff --git a/docs/source/tts/datasets.rst b/docs/source/tts/datasets.rst index 4b2e96baedb6..62d5c2f3ec17 100644 --- a/docs/source/tts/datasets.rst +++ b/docs/source/tts/datasets.rst @@ -45,9 +45,7 @@ LJSpeech .. code-block:: shell-session $ python scripts/dataset_processing/tts/ljspeech/get_data.py \ - --data-root \ - --whitelist-path \ - or default scripts/tts_dataset_files/en/whitelist_lj_speech.tsv + --data-root $ python scripts/dataset_processing/tts/extract_sup_data.py \ --config-path ljspeech/ds_conf \ diff --git a/examples/tts/conf/aligner.yaml b/examples/tts/conf/aligner.yaml index e0008e65fe4b..4fd64e823118 100644 --- a/examples/tts/conf/aligner.yaml +++ b/examples/tts/conf/aligner.yaml @@ -21,7 +21,6 @@ window: hann phoneme_dict_path: "scripts/tts_dataset_files/cmudict-0.7b_nv22.10" heteronyms_path: "scripts/tts_dataset_files/heteronyms-052722" -whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" model: symbols_embedding_dim: 384 @@ -41,7 +40,6 @@ model: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: en input_case: cased - whitelist: ${whitelist_path} text_normalizer_call_kwargs: verbose: false diff --git a/examples/tts/conf/de/fastpitch_align_22050_grapheme.yaml b/examples/tts/conf/de/fastpitch_align_22050_grapheme.yaml index 3217f976347d..85ad6a1e5cc2 100644 --- a/examples/tts/conf/de/fastpitch_align_22050_grapheme.yaml +++ b/examples/tts/conf/de/fastpitch_align_22050_grapheme.yaml @@ -28,8 +28,6 @@ lowfreq: 0 highfreq: null window: hann -whitelist_path: "nemo_text_processing/text_normalization/de/data/whitelist.tsv" - model: learn_alignment: true bin_loss_warmup_epochs: 100 @@ -58,7 +56,6 @@ model: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: de input_case: cased - whitelist: ${whitelist_path} text_normalizer_call_kwargs: verbose: false diff --git a/examples/tts/conf/de/fastpitch_align_22050_mix.yaml b/examples/tts/conf/de/fastpitch_align_22050_mix.yaml index ddd25d07bb38..d7fabcb4de5a 100644 --- a/examples/tts/conf/de/fastpitch_align_22050_mix.yaml +++ b/examples/tts/conf/de/fastpitch_align_22050_mix.yaml @@ -30,7 +30,6 @@ window: hann phoneme_dict_path: "scripts/tts_dataset_files/de/de_nv230119.dict" heteronyms_path: "scripts/tts_dataset_files/de/de_nv230119.heteronyms" -whitelist_path: "nemo_text_processing/text_normalization/de/data/whitelist.tsv" model: learn_alignment: true @@ -60,7 +59,6 @@ model: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: de input_case: cased - whitelist: ${whitelist_path} text_normalizer_call_kwargs: verbose: false diff --git a/examples/tts/conf/de/fastpitch_align_44100_grapheme.yaml b/examples/tts/conf/de/fastpitch_align_44100_grapheme.yaml index 5395ccb57080..57bc8ffedf4b 100644 --- a/examples/tts/conf/de/fastpitch_align_44100_grapheme.yaml +++ b/examples/tts/conf/de/fastpitch_align_44100_grapheme.yaml @@ -28,8 +28,6 @@ lowfreq: 0 highfreq: null window: hann -whitelist_path: "nemo_text_processing/text_normalization/de/data/whitelist.tsv" - model: learn_alignment: true bin_loss_warmup_epochs: 100 @@ -58,7 +56,6 @@ model: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: de input_case: cased - whitelist: ${whitelist_path} text_normalizer_call_kwargs: verbose: false diff --git a/examples/tts/conf/de/fastpitch_align_44100_phoneme.yaml b/examples/tts/conf/de/fastpitch_align_44100_phoneme.yaml index 832a37505ca4..2dc66ce69231 100644 --- a/examples/tts/conf/de/fastpitch_align_44100_phoneme.yaml +++ b/examples/tts/conf/de/fastpitch_align_44100_phoneme.yaml @@ -28,8 +28,6 @@ lowfreq: 0 highfreq: null window: hann -whitelist_path: "nemo_text_processing/text_normalization/de/data/whitelist.tsv" - model: learn_alignment: true bin_loss_warmup_epochs: 100 @@ -58,7 +56,6 @@ model: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: de input_case: cased - whitelist: ${whitelist_path} text_normalizer_call_kwargs: verbose: false diff --git a/examples/tts/conf/fastpitch_align_44100.yaml b/examples/tts/conf/fastpitch_align_44100.yaml index 29ef03109ef6..aa1845c5498d 100644 --- a/examples/tts/conf/fastpitch_align_44100.yaml +++ b/examples/tts/conf/fastpitch_align_44100.yaml @@ -29,7 +29,6 @@ window: hann phoneme_dict_path: "scripts/tts_dataset_files/cmudict-0.7b_nv22.10" heteronyms_path: "scripts/tts_dataset_files/heteronyms-052722" -whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" model: learn_alignment: true @@ -59,7 +58,6 @@ model: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: en input_case: cased - whitelist: ${whitelist_path} text_normalizer_call_kwargs: verbose: false diff --git a/examples/tts/conf/fastpitch_align_ipa.yaml b/examples/tts/conf/fastpitch_align_ipa.yaml index d1a55d909e1e..639a332c2a41 100644 --- a/examples/tts/conf/fastpitch_align_ipa.yaml +++ b/examples/tts/conf/fastpitch_align_ipa.yaml @@ -30,7 +30,6 @@ window: hann phoneme_dict_path: "scripts/tts_dataset_files/ipa_cmudict-0.7b_nv22.10.txt" heteronyms_path: "scripts/tts_dataset_files/heteronyms-052722" -whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" model: learn_alignment: true @@ -60,7 +59,6 @@ model: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: en input_case: cased - whitelist: ${whitelist_path} text_normalizer_call_kwargs: verbose: false diff --git a/examples/tts/conf/fastpitch_align_v1.05.yaml b/examples/tts/conf/fastpitch_align_v1.05.yaml index aa31b52fd5ec..7e14bbf53ee9 100644 --- a/examples/tts/conf/fastpitch_align_v1.05.yaml +++ b/examples/tts/conf/fastpitch_align_v1.05.yaml @@ -30,7 +30,6 @@ window: hann phoneme_dict_path: "scripts/tts_dataset_files/cmudict-0.7b_nv22.10" heteronyms_path: "scripts/tts_dataset_files/heteronyms-052722" -whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" model: learn_alignment: true @@ -60,7 +59,6 @@ model: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: en input_case: cased - whitelist: ${whitelist_path} text_normalizer_call_kwargs: verbose: false diff --git a/examples/tts/conf/mixer-tts-x.yaml b/examples/tts/conf/mixer-tts-x.yaml index 5f55dc02a095..be0f3449f28d 100644 --- a/examples/tts/conf/mixer-tts-x.yaml +++ b/examples/tts/conf/mixer-tts-x.yaml @@ -30,8 +30,6 @@ window: hann lm_model: albert -whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" - model: bin_loss_start_ratio: 0.2 bin_loss_warmup_epochs: 100 @@ -63,7 +61,6 @@ model: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: en input_case: cased - whitelist: ${whitelist_path} text_normalizer_call_kwargs: verbose: false diff --git a/examples/tts/conf/mixer-tts.yaml b/examples/tts/conf/mixer-tts.yaml index 7668b9d5bf96..510993cd1761 100644 --- a/examples/tts/conf/mixer-tts.yaml +++ b/examples/tts/conf/mixer-tts.yaml @@ -30,7 +30,6 @@ window: hann phoneme_dict_path: "scripts/tts_dataset_files/cmudict-0.7b_nv22.10" heteronyms_path: "scripts/tts_dataset_files/heteronyms-052722" -whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" model: bin_loss_start_ratio: 0.2 @@ -61,7 +60,6 @@ model: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: en input_case: cased - whitelist: ${whitelist_path} text_normalizer_call_kwargs: verbose: false diff --git a/examples/tts/conf/rad-tts_dec.yaml b/examples/tts/conf/rad-tts_dec.yaml index 6192d1538399..f15d4209e938 100644 --- a/examples/tts/conf/rad-tts_dec.yaml +++ b/examples/tts/conf/rad-tts_dec.yaml @@ -8,8 +8,6 @@ export_dir: ??? sup_data_path: ??? sup_data_types: ["log_mel", "align_prior_matrix", "pitch", "voiced_mask", "p_voiced", "energy"] -whitelist_path: null # https://github.com/NVIDIA/NeMo-text-processing/blob/main/nemo_text_processing/text_normalization/en/data/whitelist/tts.tsv default will be used - # these frame-wise values depend on pitch_fmin and pitch_fmax, you can get values # by running `scripts/dataset_processing/tts/extract_sup_data.py` pitch_mean: ??? # e.g. 212.35873413085938 for LJSpeech @@ -48,7 +46,6 @@ model: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: en input_case: cased - whitelist: ${whitelist_path} text_normalizer_call_kwargs: verbose: false diff --git a/examples/tts/conf/rad-tts_dec_ipa.yaml b/examples/tts/conf/rad-tts_dec_ipa.yaml index f135eb713b1c..83e2f3c41950 100644 --- a/examples/tts/conf/rad-tts_dec_ipa.yaml +++ b/examples/tts/conf/rad-tts_dec_ipa.yaml @@ -31,7 +31,6 @@ window: "hann" phoneme_dict_path: "scripts/tts_dataset_files/ipa_cmudict-0.7b_nv22.10.txt" heteronyms_path: "scripts/tts_dataset_files/heteronyms-052722" -whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" mapping_file_path: "" model: @@ -49,7 +48,6 @@ model: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: en input_case: cased - whitelist: ${whitelist_path} text_normalizer_call_kwargs: verbose: false diff --git a/examples/tts/conf/rad-tts_feature_pred.yaml b/examples/tts/conf/rad-tts_feature_pred.yaml index 58c7481f0674..2e5c9fa2b492 100644 --- a/examples/tts/conf/rad-tts_feature_pred.yaml +++ b/examples/tts/conf/rad-tts_feature_pred.yaml @@ -8,8 +8,6 @@ export_dir: ??? sup_data_path: ??? sup_data_types: ["log_mel", "align_prior_matrix", "pitch", "voiced_mask", "p_voiced", "energy"] -whitelist_path: null # default https://github.com/NVIDIA/NeMo-text-processing/blob/main/nemo_text_processing/text_normalization/en/data/whitelist/tts.tsv will be used - # these frame-wise values depend on pitch_fmin and pitch_fmax, you can get values # by running `scripts/dataset_processing/tts/extract_sup_data.py` pitch_mean: ??? # e.g. 212.35873413085938 for LJSpeech @@ -47,7 +45,6 @@ model: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: en input_case: cased - whitelist: ${whitelist_path} text_normalizer_call_kwargs: verbose: false diff --git a/examples/tts/conf/rad-tts_feature_pred_ipa.yaml b/examples/tts/conf/rad-tts_feature_pred_ipa.yaml index 9e0615b1f81b..2e8e44b2e06e 100644 --- a/examples/tts/conf/rad-tts_feature_pred_ipa.yaml +++ b/examples/tts/conf/rad-tts_feature_pred_ipa.yaml @@ -29,7 +29,6 @@ window: "hann" phoneme_dict_path: "scripts/tts_dataset_files/ipa_cmudict-0.7b_nv22.10.txt" heteronyms_path: "scripts/tts_dataset_files/heteronyms-052722" -whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" mapping_file_path: "" model: @@ -47,7 +46,6 @@ model: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: en input_case: cased - whitelist: ${whitelist_path} text_normalizer_call_kwargs: verbose: false diff --git a/examples/tts/conf/tacotron2.yaml b/examples/tts/conf/tacotron2.yaml index 4d30f25e4ae3..cece80d54b25 100644 --- a/examples/tts/conf/tacotron2.yaml +++ b/examples/tts/conf/tacotron2.yaml @@ -11,7 +11,6 @@ sup_data_types: null phoneme_dict_path: "scripts/tts_dataset_files/cmudict-0.7b_nv22.10" heteronyms_path: "scripts/tts_dataset_files/heteronyms-052722" -whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" model: pitch_fmin: 65.40639132514966 @@ -31,7 +30,6 @@ model: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: en input_case: cased - whitelist: ${whitelist_path} text_normalizer_call_kwargs: verbose: false diff --git a/examples/tts/conf/vits.yaml b/examples/tts/conf/vits.yaml index 64b1cf6ff12d..de0db6ef21cd 100644 --- a/examples/tts/conf/vits.yaml +++ b/examples/tts/conf/vits.yaml @@ -13,7 +13,6 @@ sup_data_types: null phoneme_dict_path: "scripts/tts_dataset_files/ipa_cmudict-0.7b_nv22.10.txt" heteronyms_path: "scripts/tts_dataset_files/heteronyms-052722" -whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" # Default values from librosa.pyin pitch_fmin: 65.40639132514966 @@ -53,7 +52,6 @@ model: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: en input_case: cased - whitelist: ${whitelist_path} text_normalizer_call_kwargs: verbose: false diff --git a/examples/tts/conf/vits_44100.yaml b/examples/tts/conf/vits_44100.yaml index d666ae31f048..b1d645a0c143 100644 --- a/examples/tts/conf/vits_44100.yaml +++ b/examples/tts/conf/vits_44100.yaml @@ -23,7 +23,6 @@ window: hann phoneme_dict_path: "scripts/tts_dataset_files/ipa_cmudict-0.7b_nv22.10.txt" heteronyms_path: "scripts/tts_dataset_files/heteronyms-052722" -whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" model: n_speakers: 13000 @@ -48,7 +47,6 @@ model: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: en input_case: cased - whitelist: ${whitelist_path} text_normalizer_call_kwargs: verbose: false diff --git a/examples/tts/conf/zh/fastpitch_align_22050.yaml b/examples/tts/conf/zh/fastpitch_align_22050.yaml index c5275ba44fb8..f1c491a3a4c3 100644 --- a/examples/tts/conf/zh/fastpitch_align_22050.yaml +++ b/examples/tts/conf/zh/fastpitch_align_22050.yaml @@ -28,7 +28,6 @@ lowfreq: 0 highfreq: null window: hann -whitelist_path: "nemo_text_processing/text_normalization/zh/data/whitelist/default.tsv" phoneme_dict_path: "scripts/tts_dataset_files/zh/pinyin_dict_nv_22.10.txt" model: @@ -59,7 +58,6 @@ model: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: zh input_case: cased - whitelist: ${whitelist_path} text_normalizer_call_kwargs: verbose: false diff --git a/nemo/collections/tts/torch/tts_dataset.yaml b/nemo/collections/tts/torch/tts_dataset.yaml index 2ca29630910e..3fc2798efeea 100644 --- a/nemo/collections/tts/torch/tts_dataset.yaml +++ b/nemo/collections/tts/torch/tts_dataset.yaml @@ -22,7 +22,6 @@ tts_dataset: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: en input_case: cased - whitelist: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" text_normalizer_call_kwargs: verbose: False diff --git a/scripts/dataset_processing/tts/hui_acg/ds_conf/ds_for_fastpitch_align.yaml b/scripts/dataset_processing/tts/hui_acg/ds_conf/ds_for_fastpitch_align.yaml index ab3717edf926..25db823926d1 100644 --- a/scripts/dataset_processing/tts/hui_acg/ds_conf/ds_for_fastpitch_align.yaml +++ b/scripts/dataset_processing/tts/hui_acg/ds_conf/ds_for_fastpitch_align.yaml @@ -3,7 +3,6 @@ name: "ds_for_fastpitch_align" manifest_filepath: ??? sup_data_path: ??? sup_data_types: [ "align_prior_matrix", "pitch" ] -whitelist_path: "nemo_text_processing/text_normalization/de/data/whitelist.tsv" dataset: _target_: nemo.collections.tts.torch.data.TTSDataset @@ -30,7 +29,6 @@ dataset: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: de input_case: cased - whitelist: ${whitelist_path} text_normalizer_call_kwargs: verbose: false diff --git a/scripts/dataset_processing/tts/ljspeech/ds_conf/ds_for_fastpitch_align.yaml b/scripts/dataset_processing/tts/ljspeech/ds_conf/ds_for_fastpitch_align.yaml index 8e66cb3497aa..28a2550c69a1 100644 --- a/scripts/dataset_processing/tts/ljspeech/ds_conf/ds_for_fastpitch_align.yaml +++ b/scripts/dataset_processing/tts/ljspeech/ds_conf/ds_for_fastpitch_align.yaml @@ -3,7 +3,6 @@ name: "ds_for_fastpitch_align" manifest_filepath: "train_manifest.json" sup_data_path: "sup_data" sup_data_types: [ "align_prior_matrix", "pitch" ] -whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" phoneme_dict_path: "scripts/tts_dataset_files/cmudict-0.7b_nv22.10" heteronyms_path: "scripts/tts_dataset_files/heteronyms-052722" @@ -31,7 +30,6 @@ dataset: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: en input_case: cased - whitelist: ${whitelist_path} text_normalizer_call_kwargs: verbose: false diff --git a/scripts/dataset_processing/tts/ljspeech/ds_conf/ds_for_mixer_tts.yaml b/scripts/dataset_processing/tts/ljspeech/ds_conf/ds_for_mixer_tts.yaml index bfec225353d3..f902937371ad 100644 --- a/scripts/dataset_processing/tts/ljspeech/ds_conf/ds_for_mixer_tts.yaml +++ b/scripts/dataset_processing/tts/ljspeech/ds_conf/ds_for_mixer_tts.yaml @@ -3,7 +3,6 @@ name: "ds_for_mixer_tts" manifest_filepath: "train_manifest.json" sup_data_path: "sup_data" sup_data_types: [ "align_prior_matrix", "pitch" ] -whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" phoneme_dict_path: "scripts/tts_dataset_files/cmudict-0.7b_nv22.10" heteronyms_path: "scripts/tts_dataset_files/heteronyms-052722" @@ -31,7 +30,6 @@ dataset: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: en input_case: cased - whitelist: ${whitelist_path} text_normalizer_call_kwargs: verbose: false diff --git a/scripts/dataset_processing/tts/ljspeech/ds_conf/ds_for_mixer_tts_x.yaml b/scripts/dataset_processing/tts/ljspeech/ds_conf/ds_for_mixer_tts_x.yaml index 89cf74f6b7de..242c8a61360a 100644 --- a/scripts/dataset_processing/tts/ljspeech/ds_conf/ds_for_mixer_tts_x.yaml +++ b/scripts/dataset_processing/tts/ljspeech/ds_conf/ds_for_mixer_tts_x.yaml @@ -3,7 +3,6 @@ name: "ds_for_mixer_tts_x" manifest_filepath: "train_manifest.json" sup_data_path: "sup_data" sup_data_types: [ "align_prior_matrix", "pitch", "lm_tokens" ] -whitelist_path: "scripts/tts_dataset_files/en/whitelist_lj_speech.tsv" dataset: _target_: nemo.collections.tts.torch.data.MixerTTSXDataset @@ -30,7 +29,6 @@ dataset: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: en input_case: cased - whitelist: ${whitelist_path} text_normalizer_call_kwargs: verbose: false diff --git a/scripts/dataset_processing/tts/ljspeech/get_data.py b/scripts/dataset_processing/tts/ljspeech/get_data.py index 0d933f8a29ca..733f9b76b354 100644 --- a/scripts/dataset_processing/tts/ljspeech/get_data.py +++ b/scripts/dataset_processing/tts/ljspeech/get_data.py @@ -27,7 +27,7 @@ def get_args(): parser = argparse.ArgumentParser(description='Download LJSpeech and create manifests with predefined split') parser.add_argument("--data-root", required=True, type=Path) - parser.add_argument('--whitelist-path', type=str, default=None) + parser.add_argument('--whitelist-path', type=str, default="lj_speech.tsv") args = parser.parse_args() return args diff --git a/scripts/dataset_processing/tts/ljspeech/lj_speech.tsv b/scripts/dataset_processing/tts/ljspeech/lj_speech.tsv new file mode 100644 index 000000000000..a55a04b9ab68 --- /dev/null +++ b/scripts/dataset_processing/tts/ljspeech/lj_speech.tsv @@ -0,0 +1,21 @@ +Mr. mister +Mrs. misses +Dr. doctor +Drs. doctors +Co. company +Lt. lieutenant +Sgt. sergeant +St. saint +Jr. junior +Maj. major +Hon. honorable +Gov. governor +Capt. captain +Esq. esquire +Gen. general +Ltd. limited +Rev. reverend +Col. colonel +Mt. mount +Ft. fort +etc. et cetera diff --git a/scripts/dataset_processing/tts/sfbilingual/ds_conf/ds_for_fastpitch_align.yaml b/scripts/dataset_processing/tts/sfbilingual/ds_conf/ds_for_fastpitch_align.yaml index f675e49954f0..46bb35b66c08 100755 --- a/scripts/dataset_processing/tts/sfbilingual/ds_conf/ds_for_fastpitch_align.yaml +++ b/scripts/dataset_processing/tts/sfbilingual/ds_conf/ds_for_fastpitch_align.yaml @@ -3,7 +3,6 @@ name: "ds_for_fastpitch_align" manifest_filepath: "train_manifest.json" sup_data_path: "sup_data" sup_data_types: [ "align_prior_matrix", "pitch" ] -whitelist_path: null # default whitelist will be used from https://raw.githubusercontent.com/NVIDIA/NeMo-text-processing/main/nemo_text_processing/text_normalization/zh/data/whitelist/default.tsv phoneme_dict_path: "scripts/tts_dataset_files/zh/pinyin_dict_nv_22.10.txt" dataset: @@ -33,7 +32,6 @@ dataset: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: zh input_case: cased - whitelist: ${whitelist_path} text_normalizer_call_kwargs: verbose: false diff --git a/scripts/dataset_processing/tts/thorsten_neutral/ds_conf/ds_for_fastpitch_align.yaml b/scripts/dataset_processing/tts/thorsten_neutral/ds_conf/ds_for_fastpitch_align.yaml index 709a212395b9..48f4f6b2a530 100644 --- a/scripts/dataset_processing/tts/thorsten_neutral/ds_conf/ds_for_fastpitch_align.yaml +++ b/scripts/dataset_processing/tts/thorsten_neutral/ds_conf/ds_for_fastpitch_align.yaml @@ -3,7 +3,6 @@ name: "ds_for_fastpitch_align" manifest_filepath: "train_manifest.json" sup_data_path: "sup_data" sup_data_types: [ "align_prior_matrix", "pitch" ] -whitelist_path: "nemo_text_processing/text_normalization/de/data/whitelist.tsv" dataset: _target_: nemo.collections.tts.torch.data.TTSDataset @@ -32,7 +31,6 @@ dataset: _target_: nemo_text_processing.text_normalization.normalize.Normalizer lang: de input_case: cased - whitelist: ${whitelist_path} text_normalizer_call_kwargs: verbose: false diff --git a/tutorials/tts/FastPitch_ChineseTTS_Training.ipynb b/tutorials/tts/FastPitch_ChineseTTS_Training.ipynb index 0542a006ed91..5735017ea64d 100644 --- a/tutorials/tts/FastPitch_ChineseTTS_Training.ipynb +++ b/tutorials/tts/FastPitch_ChineseTTS_Training.ipynb @@ -95,7 +95,6 @@ "!cd NeMoChineseTTS && wget https://raw.githubusercontent.com/nvidia/NeMo/$BRANCH/scripts/dataset_processing/tts/sfbilingual/ds_conf/ds_for_fastpitch_align.yaml\n", "!cd NeMoChineseTTS && wget https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/examples/tts/conf/zh/fastpitch_align_22050.yaml\n", "!cd NeMoChineseTTS && wget https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/examples/tts/conf/hifigan/hifigan.yaml\n", - "!cd NeMoChineseTTS && wget https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/nemo_text_processing/text_normalization/zh/data/whitelist/default.tsv\n", "!cd NeMoChineseTTS && mkdir -p model/train_ds && cd model/train_ds && wget https://raw.githubusercontent.com/nvidia/NeMo/$BRANCH/examples/tts/conf/hifigan/model/train_ds/train_ds_finetune.yaml\n", "!cd NeMoChineseTTS && mkdir -p model/validation_ds && cd model/validation_ds && wget https://raw.githubusercontent.com/nvidia/NeMo/$BRANCH/examples/tts/conf/hifigan/model/validation_ds/val_ds_finetune.yaml\n", "!cd NeMoChineseTTS && mkdir -p model/generator && cd model/generator && wget https://raw.githubusercontent.com/nvidia/NeMo/$BRANCH/examples/tts/conf/hifigan/model/generator/v1.yaml" @@ -382,8 +381,6 @@ "\n", "2. The `text_normalizer.lang` is set to `zh`, in order to use the tokenizer defined in `nemo_text_processing/text_normalization/zh` (as discussed above).\n", "\n", - "3. Update the `whitelist_path` to point to Chinese whitelist: `nemo_text_processing/text_normalization/zh/data/whitelist/default.tsv`\n", - "\n", "Final config looks like:\n", "\n", "```yaml\n", @@ -392,7 +389,6 @@ "manifest_filepath: \"train_manifest.json\"\n", "sup_data_path: \"sup_data\"\n", "sup_data_types: [ \"align_prior_matrix\", \"pitch\" ]\n", - "whitelist_path: \"nemo_text_processing/text_normalization/zh/data/whitelist/default.tsv\"\n", "\n", "phoneme_dict_path: \"scripts/tts_dataset_files/zh/pinyin_dict.txt\"\n", "\n", @@ -420,7 +416,6 @@ " _target_: nemo_text_processing.text_normalization.normalize.Normalizer\n", " lang: zh\n", " input_case: cased\n", - " whitelist: ${whitelist_path}\n", "\n", " text_normalizer_call_kwargs:\n", " verbose: false\n", @@ -491,7 +486,7 @@ "metadata": {}, "source": [ "Before we train our model, let's define model config. Most of the model config stays the same as defined here: `examples/tts/conf/fastpitch_align_44100.yaml`, except:\n", - "1. Make the same changes that we made in dataset config for `text_tokenizer`, `text_normalizer` and `whitelist_path`.\n", + "1. Make the same changes that we made in dataset config for `text_tokenizer` and `text_normalizer`.\n", "\n", "2. The `pitch_mean` and `pitch_std` are updated to the values reported by the `extract_sup_data.py` script.\n", "\n", @@ -553,7 +548,6 @@ " train_dataset=./train_manifest.json \\\n", " validation_datasets=./val_manifest.json \\\n", " sup_data_path=./sup-data-phonemes \\\n", - " whitelist_path=./default.tsv \\\n", " exp_manager.exp_dir=resultChineseTTS \\\n", " trainer.max_epochs=1 \\\n", " trainer.check_val_every_n_epoch=1 \\\n", diff --git a/tutorials/tts/FastPitch_Finetuning.ipynb b/tutorials/tts/FastPitch_Finetuning.ipynb index fe607944c1e8..208f31841505 100755 --- a/tutorials/tts/FastPitch_Finetuning.ipynb +++ b/tutorials/tts/FastPitch_Finetuning.ipynb @@ -248,7 +248,6 @@ "!mkdir -p tts_dataset_files && cd tts_dataset_files \\\n", "&& wget https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/scripts/tts_dataset_files/cmudict-0.7b_nv22.10 \\\n", "&& wget https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/scripts/tts_dataset_files/heteronyms-052722 \\\n", - "&& wget https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/nemo_text_processing/text_normalization/en/data/whitelist/tts.tsv \\\n", "&& cd .." ] }, @@ -290,7 +289,6 @@ " sup_data_path=./fastpitch_sup_data \\\n", " phoneme_dict_path=tts_dataset_files/cmudict-0.7b_nv22.10 \\\n", " heteronyms_path=tts_dataset_files/heteronyms-052722 \\\n", - " whitelist_path=tts_dataset_files/tts.tsv \\\n", " exp_manager.exp_dir=./ljspeech_to_9017_no_mixing_5_mins \\\n", " +init_from_nemo_model=./tts_en_fastpitch_align.nemo \\\n", " +trainer.max_steps=1000 ~trainer.max_epochs \\\n", @@ -322,9 +320,8 @@ " \n", "* `phoneme_dict_path=tts_dataset_files/cmudict-0.7b_nv22.10 \n", "heteronyms_path=tts_dataset_files/heteronyms-052722\n", - "whitelist_path=tts_dataset_files/tts.tsv \n", "`\n", - " * We tell the script where `phoneme_dict_path`, `heteronyms-052722` and `whitelist_path` are located. These are the additional files we downloaded earlier, and are used in preprocessing the data.\n", + " * We tell the script where `phoneme_dict_path` and `heteronyms-052722` are located. These are the additional files we downloaded earlier, and are used in preprocessing the data.\n", " \n", "* `exp_manager.exp_dir=./ljspeech_to_9017_no_mixing_5_mins`\n", " * Where we want to save our log files, tensorboard file, checkpoints, and more.\n", diff --git a/tutorials/tts/FastPitch_GermanTTS_Training.ipynb b/tutorials/tts/FastPitch_GermanTTS_Training.ipynb index 0b2e4f3fe132..05e8555c9f0a 100644 --- a/tutorials/tts/FastPitch_GermanTTS_Training.ipynb +++ b/tutorials/tts/FastPitch_GermanTTS_Training.ipynb @@ -93,7 +93,6 @@ "!cd NeMoGermanTTS && wget https://raw.githubusercontent.com/nvidia/NeMo/$BRANCH/scripts/dataset_processing/tts/extract_sup_data.py\n", "!cd NeMoGermanTTS && wget https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/examples/tts/conf/de/fastpitch_align_22050.yaml\n", "!cd NeMoGermanTTS && wget https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/examples/tts/conf/hifigan/hifigan.yaml\n", - "!cd NeMoGermanTTS && wget https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/nemo_text_processing/text_normalization/de/data/whitelist.tsv\n", "!cd NeMoGermanTTS && mkdir -p model/train_ds && cd model/train_ds && wget https://raw.githubusercontent.com/nvidia/NeMo/$BRANCH/examples/tts/conf/hifigan/model/train_ds/train_ds_finetune.yaml\n", "!cd NeMoGermanTTS && mkdir -p model/validation_ds && cd model/validation_ds && wget https://raw.githubusercontent.com/nvidia/NeMo/$BRANCH/examples/tts/conf/hifigan/model/validation_ds/val_ds_finetune.yaml\n", "!cd NeMoGermanTTS && mkdir -p model/generator && cd model/generator && wget https://raw.githubusercontent.com/nvidia/NeMo/$BRANCH/examples/tts/conf/hifigan/model/generator/v1.yaml" @@ -446,8 +445,6 @@ "\n", "2. The `text_normalizer.lang` is set to `de`, in order to use the tokenizer defined in `nemo_text_processing/text_normalization/de` (as discussed above).\n", "\n", - "3. Update the `whitelist_path` to point to German whitelist: `nemo_text_processing/text_normalization/de/data/whitelist.tsv`\n", - "\n", "Final config looks like:\n", "\n", "```yaml\n", @@ -456,7 +453,6 @@ "manifest_filepath: \"train_manifest.json\"\n", "sup_data_path: \"sup_data\"\n", "sup_data_types: [ \"align_prior_matrix\", \"pitch\" ]\n", - "whitelist_path: \"NeMoGermanTTS/whitelist.tsv\"\n", "\n", "dataset:\n", " _target_: nemo.collections.tts.torch.data.TTSDataset\n", @@ -482,7 +478,6 @@ " _target_: nemo_text_processing.text_normalization.normalize.Normalizer\n", " lang: de\n", " input_case: cased\n", - " whitelist: ${whitelist_path}\n", "\n", " text_normalizer_call_kwargs:\n", " verbose: false\n", @@ -511,7 +506,6 @@ "manifest_filepath: \"train_manifest.json\"\n", "sup_data_path: \"sup_data\"\n", "sup_data_types: [ \"align_prior_matrix\", \"pitch\" ]\n", - "whitelist_path: \"NeMoGermanTTS/whitelist.tsv\"\n", "\n", "dataset:\n", " _target_: nemo.collections.tts.torch.data.TTSDataset\n", @@ -537,7 +531,6 @@ " _target_: nemo_text_processing.text_normalization.normalize.Normalizer\n", " lang: de\n", " input_case: cased\n", - " whitelist: ${whitelist_path}\n", "\n", " text_normalizer_call_kwargs:\n", " verbose: false\n", @@ -608,7 +601,7 @@ "metadata": {}, "source": [ "Before we train our model, let's define model config. Most of the model config stays the same as defined here: `examples/tts/conf/fastpitch_align_44100.yaml`, except:\n", - "1. Make the same changes that we made in dataset config for `text_tokenizer`, `text_normalizer` and `whitelist_path`.\n", + "1. Make the same changes that we made in dataset config for `text_tokenizer` and `text_normalizer`.\n", "\n", "2. The `pitch_mean` and `pitch_std` are updated to the values reported by the `extract_sup_data.py` script.\n", "\n", @@ -666,7 +659,6 @@ " train_dataset=../DataGermanTTS/thorsten-de/train_manifest_phonemes.json \\\n", " validation_datasets=../DataGermanTTS/thorsten-de/val_manifest_phonemes.json \\\n", " sup_data_path=../DataGermanTTS/thorsten-de/phonemes/ \\\n", - " whitelist_path=./whitelist.tsv \\\n", " exp_manager.exp_dir=resultGermanTTS \\\n", " trainer.max_epochs=1 \\\n", " trainer.check_val_every_n_epoch=1 \\\n", diff --git a/tutorials/tts/FastPitch_MixerTTS_Training.ipynb b/tutorials/tts/FastPitch_MixerTTS_Training.ipynb index 52d555faa934..05434c03e6d0 100644 --- a/tutorials/tts/FastPitch_MixerTTS_Training.ipynb +++ b/tutorials/tts/FastPitch_MixerTTS_Training.ipynb @@ -230,7 +230,6 @@ "!mkdir -p tts_dataset_files && cd tts_dataset_files \\\n", "&& wget https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/scripts/tts_dataset_files/cmudict-0.7b_nv22.10 \\\n", "&& wget https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/scripts/tts_dataset_files/heteronyms-052722 \\\n", - "&& wget https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/scripts/tts_dataset_files/en/whitelist_lj_speech.tsv \\\n", "&& cd .." ] }, @@ -281,7 +280,6 @@ "text_normalizer = Normalizer(\n", " lang=\"en\", \n", " input_case=\"cased\", \n", - " whitelist=\"tts_dataset_files/lj_speech.tsv\"\n", ")\n", "\n", "text_normalizer_call_kwargs = {\n", @@ -420,7 +418,6 @@ "text_normalizer = Normalizer(\n", " lang=\"en\", \n", " input_case=\"cased\", \n", - " whitelist=\"tts_dataset_files/lj_speech.tsv\"\n", ")\n", "\n", "text_normalizer_call_kwargs = {\n", @@ -501,7 +498,6 @@ " validation_datasets=tests/data/asr/an4_val.json \\\n", " sup_data_types=\"['align_prior_matrix', 'pitch']\" \\\n", " sup_data_path={fastpitch_sup_data_path} \\\n", - " whitelist_path=tts_dataset_files/lj_speech.tsv \\\n", " pitch_mean={pitch_mean} \\\n", " pitch_std={pitch_std} \\\n", " pitch_fmin={pitch_min} \\\n", @@ -558,7 +554,6 @@ "sup_data_path={mixer_tts_sup_data_path} \\\n", "phoneme_dict_path=tts_dataset_files/cmudict-0.7b_nv22.10 \\\n", "heteronyms_path=tts_dataset_files/heteronyms-052722 \\\n", - "whitelist_path=tts_dataset_files/lj_speech.tsv \\\n", "pitch_mean={pitch_mean} \\\n", "pitch_std={pitch_std} \\\n", "model.train_ds.dataloader_params.batch_size=6 \\\n", diff --git a/tutorials/tts/Tacotron2_Training.ipynb b/tutorials/tts/Tacotron2_Training.ipynb index 28ca250a8b58..a12cb980dc0b 100644 --- a/tutorials/tts/Tacotron2_Training.ipynb +++ b/tutorials/tts/Tacotron2_Training.ipynb @@ -168,11 +168,6 @@ " && wget https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/scripts/tts_dataset_files/cmudict-0.7b_nv22.10 \\\n", " && wget https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/scripts/tts_dataset_files/heteronyms-052722 \\\n", " && cd ..)\n", - " \n", - "!(mkdir -p nemo_text_processing/text_normalization/en/data/whitelist/ \\\n", - " && cd nemo_text_processing/text_normalization/en/data/whitelist/ \\\n", - " && wget https://raw.githubusercontent.com/NVIDIA/NeMo/$BRANCH/scripts/tts_dataset_files/en/whitelist_lj_speech.tsv \\\n", - " && cd ..)" ] }, { @@ -235,7 +230,6 @@ "\n", "phoneme_dict_path: \"scripts/tts_dataset_files/cmudict-0.7b_nv22.10\"\n", "heteronyms_path: \"scripts/tts_dataset_files/heteronyms-052722\"\n", - "whitelist_path: \"scripts/tts_dataset_files/en/whitelist_lj_speech.tsv\"\n", "```\n", "\n", "The first part of the yaml defines dataset parameters used by Tacotron. Then in the head of 'model' section there are processing - related parameters. You can see\n", From 6cbc570aed616ced51a1cd45b853fe7a65d621c3 Mon Sep 17 00:00:00 2001 From: ekmb Date: Tue, 14 Feb 2023 18:38:18 -0800 Subject: [PATCH 14/19] delete wordid file Signed-off-by: ekmb --- .../wordid_to_ipa-0.7b_nv22.10.tsv | 326 ------------------ 1 file changed, 326 deletions(-) delete mode 100644 scripts/tts_dataset_files/wordid_to_ipa-0.7b_nv22.10.tsv diff --git a/scripts/tts_dataset_files/wordid_to_ipa-0.7b_nv22.10.tsv b/scripts/tts_dataset_files/wordid_to_ipa-0.7b_nv22.10.tsv deleted file mode 100644 index d46ae401895a..000000000000 --- a/scripts/tts_dataset_files/wordid_to_ipa-0.7b_nv22.10.tsv +++ /dev/null @@ -1,326 +0,0 @@ -ABSTRACT_ADJ-NOU ˈæbˌstɹækt -ABSTRACT_VRB æbˈstɹækt -ABUSE_NOU əbˈjus -ABUSE_VRB əbˈjuz -ABUSES_NOU əbˈjusɪz -ABUSES_VRB əbˈjuzɪz -ADDICT_NOU ˈæˌdɪkt -ADDICT_VRB əˈdɪkt -ADVOCATE_NOU ˈædvəkət -ADVOCATE_VRB ˈædvəˌkeɪt -AFFECT_NOU-PSY ˈæˌfɛkt -AFFECT əˈfɛkt -AFFILIATE_NOU əˈfɪliət -AFFILIATE_VRB əˈfɪliˌeɪt -AGED_ADJ ˈeɪdʒɪd -AGED ˈeɪdʒd -AGGREGATE_ADJ-NOU ˈæɡɹəɡət -AGGREGATE_VRB ˈæɡɹəɡeɪt -ALTERNATE_ADJ-NOU ˈɔltɚnət -ALTERNATE_VRB ˈɔltɚˌneɪt -ANALYSES_NOU æˈnælɪˌsiz -ANALYSES_VRB ænəˈlaɪˌzɪz -ANIMATE_ADJ-NOU ˈænəmət -ANIMATE_VRB ˈænəˌmeɪt -APPROPRIATE_ADJ əˈpɹoʊpɹiət -APPROPRIATE_VRB əˈpɹoʊpɹiˌeɪt -APPROXIMATE_ADJ-NOU əˈpɹɑksəmət -APPROXIMATE_VRB əˈpɹɑksəˌmeɪt -ARTICULATE_ADJ ɑɹˈtɪkjələt -ARTICULATE_VRB ɑɹˈtɪkjəˌleɪt -ASSOCIATE_ADJ-NOU əˈsoʊsiət -ASSOCIATE_VRB əˈsoʊsiˌeɪt -ATTRIBUTE_NOU ˈætɹəbˌjut -ATTRIBUTE_VRB əˈtɹɪbˌjut -AUGUST_ADJ ˌɑˈɡəst -AUGUST ˈɑɡəst -AUGUST_NAM ˈaʊˌɡʊst -AXES_NOU ˈækˌsiz -AXES_NOU-VRB ˈækˌsɪz -BASS ˈbeɪs -BASS_CORP ˈbæs -BLESSED_ADJ ˈblɛsəd -BLESSED_VRB ˈblɛst -BOLOGNA_GEO bəˈloʊˌŋɑ -BOLOGNA bəˈloʊni -BOW_NOU-KNOT ˈboʊ -BOW_NOU-SHIP ˈbaʊ -BUFFET_NOU bəˈfeɪ -BUFFET_VRB ˈbʌfət -CELTIC_ADJ-NOU-SPORTS ˈsɛltɪk -CELTIC ˈkɛltɪk -CLOSE_ADJ-NOU ˈkloʊs -CLOSE_VRB ˈkloʊz -COMBINE_NOU ˈkɑmbaɪn -COMBINE_VRB kəmˈbaɪn -COMPACT_ADJ-NOU ˈkɑmpækt -COMPACT_VRB kəmˈpækt -COMPOUND_NOU ˈkɑmpaʊnd -COMPOUND_VRB kəmˈpaʊnd -COMPRESS_NOU ˈkɑmpɹɛs -COMPRESS kəmˈpɹɛs -CONDUCT_NOU ˈkɑndəkt -CONDUCT_VRB kɑnˈdʌkt -CONFINES_NOU ˈkɑnˌfaɪnz -CONFINES_VRB kənˈfaɪnz -CONFLICT_NOU ˈkɑnflɪkt -CONFLICT_VRB kənˈflɪkt -CONGLOMERATE_ADJ-NOU kənˈɡlɑmɚət -CONGLOMERATE_VRB kənˈɡlɑmɝˌeɪt -CONJUGATE_ADJ-NOU ˈkɑndʒəˌɡeɪt -CONJUGATE_VRB ˈkɑndʒəˌɡeɪt -CONSCRIPT_NOU ˈkɑnsˌkɹɪpt -CONSCRIPT_VRB kənsˈkɹɪpt -CONSOLE_NOU ˈkɑnsoʊl -CONSOLE_VRB kənˈsoʊl -CONSORT_NOU ˈkɑnˌsɔɹt -CONSORT_VRB kənˈsɔɹt -CONSTRUCT_NOU ˈkɑnstɹəkt -CONSTRUCT_VRB kənˈstɹʌkt -CONSUMMATE_ADJ ˈkɑnsəmət -CONSUMMATE_VRB ˈkɑnsəˌmeɪt -CONTENT_ADJ-NOU-VRB kənˈtɛnt -CONTENT_NOU ˈkɑntɛnt -CONTEST_NOU ˈkɑntɛst -CONTEST_VRB kənˈtɛst -CONTRACT_NOU ˈkɑnˌtɹækt -CONTRACT_VRB kənˈtɹækt -CONTRAST_NOU ˈkɑntɹæst -CONTRAST_VRB kənˈtɹæst -CONVERSE_ADJ-NOU ˈkɑnvɚs -CONVERSE_VRB kənˈvɝs -CONVERT_NOU ˈkɑnvɚt -CONVERT_VRB kənˈvɝt -CONVICT_NOU ˈkɑnvɪkt -CONVICT_VRB kənˈvɪkt -COORDINATE_ADJ-NOU koʊˈɔɹdənət -COORDINATE_VRB koʊˈɔɹdəˌneɪt -CORRELATE_NOU ˈkɔɹələt -CORRELATE_NOU-VRB ˈkɔɹəˌleɪt -DECREASE_NOU ˈdiˌkɹis -DECREASE_VRB dɪˈkɹis -DEFECT_NOU ˈdifɛkt -DEFECT_VRB dɪˈfɛkt -DEGENERATE_ADJ-NOU dɪˈdʒɛnɚət -DEGENERATE_VRB dɪˈdʒɛnɚˌeɪt -DELEGATE_NOU ˈdɛləɡət -DELEGATE_VRB ˈdɛləˌɡeɪt -DELIBERATE_ADJ dɪˈlɪbɚət -DELIBERATE_VRB dɪˈlɪbɚˌeɪt -DESERT_NOU ˈdɛzɚt -DESERT_VRB dɪˈzɝt -DEVIATE_NOU ˈdiˌviət -DEVIATE_VRB ˈdiviˌeɪt -DIAGNOSES_NOU ˌdaɪəɡˈnoʊsiz -DIAGNOSES_VRB ˌdaɪəɡˈnoʊsəz -DIFFUSE_ADJ dɪfˈjus -DIFFUSE_VRB dɪfˈjuz -DISCARD_NOU ˈdɪsˌkɑɹd -DISCARD_VRB dəsˈkɑɹd -DISCHARGE_NOU ˈdɪsˌtʃɑɹdʒ -DISCHARGE_VRB dɪsˈtʃɑɹdʒ -DISCOUNT_NOU ˈdɪskaʊnt -DISCOUNT_VRB dɪˈskaʊnt -DOCUMENT_NOU ˈdɑkjumɛnt -DOCUMENT_VRB ˈdɑkjəmɛnt -DOVE ˈdʌv -DOVE_VRB ˈdoʊv -DUPLICATE_ADJ-NOU ˈdupləkət -DUPLICATE_VRB ˈdupləˌkeɪt -ELABORATE_ADJ ɪˈlæbɹət -ELABORATE_VRB ɪˈlæbɚˌeɪt -ENTRANCE_NOU ˈɛntɹəns -ENTRANCE_VRB əˈntɹæns -ESCORT_NOU ˈɛskɔɹt -ESCORT_VRB ɛˈskɔɹt -ESTIMATE_NOU ˈɛstəmət -ESTIMATE_VRB ˈɛstəˌmeɪt -EXCUSE_NOU ɪkˈskjus -EXCUSE_VRB ɪkˈskjuz -EXPATRIATE_NOU ɛkˈspeɪtɹiət -EXPATRIATE_VRB ɛkˈspeɪtɹiˌeɪt -EXPLOIT_NOU ˈɛksˌplɔɪt -EXPLOIT_VRB ˌɛksˈplɔɪt -EXPORT_NOU ˈɛkspɔɹt -EXPORT_VRB əkˈspɔɹt -EXPOSE_NOU ˌɛkˌspoʊˈzeɪ -EXPOSE_VRB ɪkˈspoʊz -EXTRACT_NOU ˈɛkˌstɹækt -EXTRACT_VRB ɪkˈstɹækt -FRAGMENT_NOU ˈfɹæɡmənt -FRAGMENT_VRB ˌfɹæɡˈmɛnt -FREQUENT_ADJ ˈfɹikwənt -FREQUENT_VRB ˌfɹiˈkwɛnt -GRADUATE_ADJ-NOU ˈɡɹædʒuwət -GRADUATE_VRB ˈɡɹædʒuˌeɪt -HOUSE_NOU ˈhaʊs -HOUSE_VRB ˈhaʊz -IMPACT_NOU ˈɪmpækt -IMPACT_VRB ˌɪmˈpækt -IMPLANT_NOU ˈɪmˌplænt -IMPLANT_VRB ˌɪmˈplænt -IMPLEMENT_NOU ˈɪmpləmənt -IMPLEMENT_VRB ˈɪmpləˌmɛnt -IMPORT_NOU ˈɪmˌpɔɹt -IMPORT_VRB ˌɪmˈpɔɹt -INCENSE_NOU ˈɪnˌsɛns -INCENSE_VRB ˌɪnˈsɛns -INCLINE_NOU ˈɪnklaɪn -INCLINE_VRB ˌɪnˈklaɪn -INCREASE_NOU ˈɪnˌkɹis -INCREASE_VRB ˌɪnˈkɹis -INCREMENT_NOU ˈɪnkɹəmənt -INCREMENT_VRB ˈɪnkɹəˌmɛnt -INITIATE_NOU əˈnɪˌʃiət -INITIATE_VRB ˌɪˈnɪʃiˌeɪt -INSERT_NOU ˈɪnˌsɝt -INSERT_VRB ˌɪnˈsɝt -INSTRUMENT_NOU ˈɪnstɹəmənt -INSTRUMENT_VRB ˈɪnstɹəˌmɛnt -INSULT_NOU ˈɪnˌsʌlt -INSULT_VRB ˌɪnˈsʌlt -INTERCHANGE_NOU ˈɪntɚˌtʃeɪndʒ -INTERCHANGE_VRB ˌɪntɚˈtʃeɪndʒ -INTIMATE_ADJ ˈɪntəmət -INTIMATE_VRB ˈɪntɪˌmeɪt -INTRIGUE_NOU ˈɪntɹiɡ -INTRIGUE_NOU-VRB ˌɪnˈtɹiɡ -INVALID_ADJ ˌɪnˈvæləd -INVALID_NOU ˈɪnvələd -INVERT_ADJ-NOU ˈɪnvɝt -INVERT_VRB ˌɪnˈvɝt -INVITE_NOU ˈɪnˌvaɪt -INVITE_VRB ˌɪnˈvaɪt -ISOLATE_NOU ˈaɪsələt -ISOLATE ˈaɪsəˌleɪt -JESUS ˈdʒizəs -JESUS_ES ˌheɪˈsus -JOB_BIBLE ˈdʒoʊb -JOB ˈdʒɑb -LAMINATE_NOU ˈlæmənət -LAMINATE_VRB ˈlæməˌneɪt -LEAD_NOU ˈlɛd -LEAD_NOU-VRB ˈlid -LEARNED_ADJ ˈlɝnɪd -LEARNED_VRB ˈlɝnd -LIVE_ADJ ˈlaɪv -LIVE_VRB ˈlɪv -LIVES_NOU ˈlaɪvz -LIVES_VRB ˈlɪvz -MATE_NOU ˈmɑˌteɪ -MATE ˈmeɪt -MINUTE_ADJ maɪˈnut -MINUTE ˈmɪnət -MISUSE_NOU mɪsˈjus -MISUSE_VRB mɪsˈjuz -MOBILE ˈmoʊbəl -MOBILE_GEO ˌmoʊˈbil -MOBILE_NOU-ART ˈmoʊˌbil -MODERATE_ADJ-NOU ˈmɑdɚət -MODERATE_VRB ˈmɑdɚˌeɪt -MOPED_NOU ˈmoʊpɛd -MOPED_VRB ˈmoʊpt -MOUTH_NOU ˈmaʊθ -MOUTH_VRB ˈmaʊð -NESTLE_NAM ˈnɛsˈli -NESTLE_VRB ˈnɛsəl -OBJECT_NOU ˈɑbdʒɛkt -OBJECT_VRB əbˈdʒɛkt -ORNAMENT_NOU ˈɔɹnəmənt -ORNAMENT_VRB ˌɔɹnəˈmɛnt -OVERTHROW_NOU ˈoʊvɚˌθɹoʊ -OVERTHROW_VRB ˌoʊvɚˈθɹoʊ -PASTY_ADJ ˈpeɪˌsti -PASTY_NOU ˈpæsˌti -PERFECT_ADJ ˈpɝˌfɪkt -PERFECT_VRB pɚˈfɛkt -PERFUME_NOU ˈpɝˌfjum -PERFUME_VRB pɚˈfjum -PERMIT_NOU ˈpɝˌmɪt -PERMIT_VRB pɚˈmɪt -PERVERT_NOU ˈpɝvɚt -PERVERT_VRB pɚˈvɝt -PIGMENT_NOU ˈpɪɡmɛnt -PIGMENT_VRB pəɡˈmɛnt -POLISH_GEO ˈpoʊlɪʃ -POLISH ˈpɑlɪʃ -POSTULATE_NOU ˈpɑstʃələt -POSTULATE_VRB ˈpɑstʃəˌleɪt -PRECIPITATE_ADJ-NOU pɹəˈsɪpətət -PRECIPITATE_VRB pɹɪˈsɪpɪˌteɪt -PREDICATE_NOU ˈpɹɛdɪkət -PREDICATE_VRB ˈpɹɛdəˌkeɪt -PRESENT_ADJ-NOU ˈpɹɛzənt -PRESENT_VRB pɹiˈzɛnt -PRODUCE_NOU ˈpɹoʊdus -PRODUCE_VRB pɹəˈdus -PROGRESS_NOU ˈpɹɑˌɡɹɛs -PROGRESS_VRB pɹəˈɡɹɛs -PROJECT_NOU ˈpɹɑdʒɛkt -PROJECT_VRB pɹɑˈdʒɛkt -PROTEST_NOU ˈpɹoʊˌtɛst -PROTEST_VRB pɹəˈtɛst -RAVEL_NAM ɹəˈvɛl -RAVEL_NOU ˈɹævəl -READING_EN ˈɹidɪŋ -READING_GEO ˈɹɛdɪŋ -READ_PAST ˈɹɛd -READ_PRESENT ˈɹid -REBEL_NOU ˈɹɛbəl -REBEL_VRB ɹɪˈbɛl -RECORD_NOU ˈɹɛkɚd -RECORD_VRB ɹəˈkɔɹd -RECOUNT_NOU ˈɹiˌkaʊnt -RECOUNT_VRB ˌɹiˈkaʊnt -REFUND_NOU ˈɹiˌfʌnd -REFUND_VRB ɹɪˈfʌnd -REFUSE_NOU ˈɹɛfˌjuz -REFUSE_VRB ɹəfˈjuz -REJECT_NOU ˈɹidʒɛkt -REJECT_VRB ɹɪˈdʒɛkt -RERELEASE ˈɹiɹəˌlis -RERELEASE_VRB ˌɹiɹəˈlis -RESUME_NOU ˈɹɛzəˌmeɪ -RESUME_VRB ɹiˈzum -RETARD_NOU ˈɹitɑɹd -RETARD_VRB ɹɪˈtɑɹd -RODEO_GEO ˌɹoʊˈdeɪˌoʊ -RODEO ˈɹoʊdiˌoʊ -ROW_1 ˈɹoʊ -ROW_2 ˈɹaʊ -SAKE_JP ˈsɑˌkeɪ -SAKE ˈseɪk -SEPARATE_ADJ ˈsɛpɚɪt -SEPARATE_VRB ˈsɛpɚˌeɪt -SOW_NOU ˈsaʊ -SOW ˈsoʊ -SUBJECT_ADJ-NOU ˈsʌbdʒɪkt -SUBJECT_VRB səbˈdʒɛkt -SUBORDINATE_ADJ-NOU səˈbɔɹdənət -SUBORDINATE_VRB səˈbɔɹdəˌneɪt -SUPPLEMENT_NOU ˈsʌpləmənt -SUPPLEMENT_VRB ˈsʌpləmənt -SUSPECT_ADJ-NOU ˈsʌsˌpɛkt -SUSPECT_VRB səˈspɛkt -SYNDICATE_NOU ˈsɪndɪkət -SYNDICATE_VRB ˈsɪndəˌkeɪt -TEAR_NOU ˈtɪɹ -TEAR_VRB ˈtɛɹ -TRANSFORM_NOU ˈtɹænsfɔɹm -TRANSFORM tɹænsˈfɔɹm -TRANSPLANT_NOU ˈtɹænsˌplænt -TRANSPLANT_VRB tɹænsˈplænt -TRANSPORT_NOU ˈtɹænspɔɹt -TRANSPORT_VRB tɹænˈspɔɹt -UPSET_NOU ˈʌpˌsɛt -UPSET_VRB əpˈsɛt -USES_NOU ˈjusəz -USES_VRB ˈjuzəz -USE_NOU ˈjus -USE_VRB ˈjuz -WIND_NOU ˈwɪnd -WIND_VRB ˈwaɪnd -WINDS_NOU ˈwɪndz -WINDS_VRB ˈwaɪndz -WOUND_NOU-VRB ˈwund -WOUND_VRB ˈwaʊnd From 2696d10b58a8eb641fe148e8271177a6716ef7de Mon Sep 17 00:00:00 2001 From: ekmb Date: Tue, 14 Feb 2023 19:56:16 -0800 Subject: [PATCH 15/19] remove pynini_install from tutorials Signed-off-by: ekmb --- .../duplex_text_normalization/duplex_decoder.py | 4 +++- nemo/collections/tts/torch/data.py | 4 +++- tutorials/AudioTranslationSample.ipynb | 6 +----- tutorials/VoiceSwapSample.ipynb | 4 ---- tutorials/tts/Aligner_Inference_Examples.ipynb | 4 +--- tutorials/tts/FastPitch_ChineseTTS_Training.ipynb | 4 +--- tutorials/tts/FastPitch_Finetuning.ipynb | 6 ++---- tutorials/tts/FastPitch_GermanTTS_Training.ipynb | 6 ++---- tutorials/tts/FastPitch_MixerTTS_Training.ipynb | 6 ++---- .../tts/Inference_DurationPitchControl.ipynb | 4 +--- tutorials/tts/Inference_ModelSelect.ipynb | 4 +--- tutorials/tts/NeMo_TTS_Primer.ipynb | 15 --------------- tutorials/tts/Pronunciation_customization.ipynb | 6 ++---- tutorials/tts/Tacotron2_Training.ipynb | 4 +--- 14 files changed, 20 insertions(+), 57 deletions(-) diff --git a/nemo/collections/nlp/models/duplex_text_normalization/duplex_decoder.py b/nemo/collections/nlp/models/duplex_text_normalization/duplex_decoder.py index 861676acf316..7d4cac46cc28 100644 --- a/nemo/collections/nlp/models/duplex_text_normalization/duplex_decoder.py +++ b/nemo/collections/nlp/models/duplex_text_normalization/duplex_decoder.py @@ -111,7 +111,9 @@ def setup_cgs(self, cfg: DictConfig): self.cg_normalizer = NormalizerWithAudio(input_case=input_case, lang=self.lang) else: self.cg_normalizer = None - logging.warning("`pynini` not installed, please install via nemo_text_processing/pynini_install.sh") + logging.warning( + "`nemo_text_processing` is not installed, see https://github.com/NVIDIA/NeMo-text-processing for details" + ) @typecheck() def forward(self, input_ids, decoder_input_ids, attention_mask, labels): diff --git a/nemo/collections/tts/torch/data.py b/nemo/collections/tts/torch/data.py index a5e0e01f3a41..36d5c4b3634c 100644 --- a/nemo/collections/tts/torch/data.py +++ b/nemo/collections/tts/torch/data.py @@ -203,7 +203,9 @@ def __init__( if self.text_normalizer is None: self.text_normalizer_call = None elif not PYNINI_AVAILABLE: - raise ImportError("pynini is not installed, please install via nemo_text_processing/install_pynini.sh") + raise ImportError( + "`nemo_text_processing` is not installed, see https://github.com/NVIDIA/NeMo-text-processing for details" + ) else: self.text_normalizer_call = ( self.text_normalizer.normalize diff --git a/tutorials/AudioTranslationSample.ipynb b/tutorials/AudioTranslationSample.ipynb index c4fec16c4181..12b354725982 100644 --- a/tutorials/AudioTranslationSample.ipynb +++ b/tutorials/AudioTranslationSample.ipynb @@ -39,11 +39,7 @@ "outputs": [], "source": [ "BRANCH = 'main'\n", - "!python -m pip install git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[all]\n", - "\n", - "# install Pynini for text normalization\n", - "! wget https://raw.githubusercontent.com/NVIDIA/NeMo/main/nemo_text_processing/install_pynini.sh\n", - "! bash install_pynini.sh" + "!python -m pip install git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[all]\n" ] }, { diff --git a/tutorials/VoiceSwapSample.ipynb b/tutorials/VoiceSwapSample.ipynb index 016737f26a9f..4f22e2cb82a6 100644 --- a/tutorials/VoiceSwapSample.ipynb +++ b/tutorials/VoiceSwapSample.ipynb @@ -41,10 +41,6 @@ "source": [ "BRANCH = 'main'\n", "!python -m pip install git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[all]\n", - "\n", - "# install Pynini for text normalization\n", - "! wget https://raw.githubusercontent.com/NVIDIA/NeMo/main/nemo_text_processing/install_pynini.sh\n", - "! bash install_pynini.sh" ] }, { diff --git a/tutorials/tts/Aligner_Inference_Examples.ipynb b/tutorials/tts/Aligner_Inference_Examples.ipynb index f6acbfa2c0d4..7a94102b3dee 100644 --- a/tutorials/tts/Aligner_Inference_Examples.ipynb +++ b/tutorials/tts/Aligner_Inference_Examples.ipynb @@ -43,9 +43,7 @@ "# # If you're using Colab and not running locally, uncomment and run this cell.\n", "# !apt-get install sox libsndfile1 ffmpeg\n", "# !pip install wget text-unidecode\n", - "# !python -m pip install git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[all]\n", - "# !wget https://raw.githubusercontent.com/NVIDIA/NeMo/main/nemo_text_processing/install_pynini.sh\n", - "# !bash install_pynini.sh" + "# !python -m pip install git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[all]\n" ] }, { diff --git a/tutorials/tts/FastPitch_ChineseTTS_Training.ipynb b/tutorials/tts/FastPitch_ChineseTTS_Training.ipynb index 5735017ea64d..57ea4f20bea2 100644 --- a/tutorials/tts/FastPitch_ChineseTTS_Training.ipynb +++ b/tutorials/tts/FastPitch_ChineseTTS_Training.ipynb @@ -54,10 +54,8 @@ "BRANCH = 'main'\n", "# # If you're using Colab and not running locally, uncomment and run this cell.\n", "# !apt-get install sox libsndfile1 ffmpeg\n", - "# !pip install wget text-unidecode pynini==2.1.4 scipy==1.7.3\n", + "# !pip install wget text-unidecode scipy==1.7.3\n", "# !python -m pip install git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[all]\n", - "# !wget https://raw.githubusercontent.com/NVIDIA/NeMo/main/nemo_text_processing/install_pynini.sh\n", - "# !bash install_pynini.sh\n", "# !pip install pypinyin opencc-python-reimplemented" ] }, diff --git a/tutorials/tts/FastPitch_Finetuning.ipynb b/tutorials/tts/FastPitch_Finetuning.ipynb index 208f31841505..be4e99c3caab 100755 --- a/tutorials/tts/FastPitch_Finetuning.ipynb +++ b/tutorials/tts/FastPitch_Finetuning.ipynb @@ -60,10 +60,8 @@ "BRANCH = 'main'\n", "# # If you're using Google Colab and not running locally, uncomment and run this cell.\n", "# !apt-get install sox libsndfile1 ffmpeg\n", - "# !pip install wget text-unidecode pynini==2.1.4\n", - "# !python -m pip install git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[all]\n", - "# !wget https://raw.githubusercontent.com/NVIDIA/NeMo/main/nemo_text_processing/install_pynini.sh\n", - "# !bash install_pynini.sh" + "# !pip install wget text-unidecode \n", + "# !python -m pip install git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[all]\n" ] }, { diff --git a/tutorials/tts/FastPitch_GermanTTS_Training.ipynb b/tutorials/tts/FastPitch_GermanTTS_Training.ipynb index 05e8555c9f0a..110720c9ce33 100644 --- a/tutorials/tts/FastPitch_GermanTTS_Training.ipynb +++ b/tutorials/tts/FastPitch_GermanTTS_Training.ipynb @@ -54,10 +54,8 @@ "BRANCH = 'main'\n", "# # If you're using Colab and not running locally, uncomment and run this cell.\n", "# !apt-get install sox libsndfile1 ffmpeg\n", - "# !pip install wget text-unidecode pynini==2.1.4 scipy==1.7.3\n", - "# !python -m pip install git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[all]\n", - "# !wget https://raw.githubusercontent.com/NVIDIA/NeMo/main/nemo_text_processing/install_pynini.sh\n", - "# !bash install_pynini.sh" + "# !pip install wget text-unidecode scipy==1.7.3\n", + "# !python -m pip install git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[all]\n" ] }, { diff --git a/tutorials/tts/FastPitch_MixerTTS_Training.ipynb b/tutorials/tts/FastPitch_MixerTTS_Training.ipynb index 05434c03e6d0..8916825f92df 100644 --- a/tutorials/tts/FastPitch_MixerTTS_Training.ipynb +++ b/tutorials/tts/FastPitch_MixerTTS_Training.ipynb @@ -53,10 +53,8 @@ "BRANCH = 'main'\n", "# # If you're using Colab and not running locally, uncomment and run this cell.\n", "# !apt-get install sox libsndfile1 ffmpeg\n", - "# !pip install wget text-unidecode pynini==2.1.4 scipy==1.7.3\n", - "# !python -m pip install git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[all]\n", - "# !wget https://raw.githubusercontent.com/NVIDIA/NeMo/main/nemo_text_processing/install_pynini.sh\n", - "# !bash install_pynini.sh" + "# !pip install wget text-unidecode scipy==1.7.3\n", + "# !python -m pip install git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[all]\n" ] }, { diff --git a/tutorials/tts/Inference_DurationPitchControl.ipynb b/tutorials/tts/Inference_DurationPitchControl.ipynb index c4879f38274c..831f4b0b3c4d 100644 --- a/tutorials/tts/Inference_DurationPitchControl.ipynb +++ b/tutorials/tts/Inference_DurationPitchControl.ipynb @@ -50,9 +50,7 @@ "# # If you're using Google Colab and not running locally, uncomment and run this cell.\n", "# !apt-get install sox libsndfile1 ffmpeg\n", "# !pip install wget text-unidecode\n", - "# !python -m pip install git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[all]\n", - "# !wget https://raw.githubusercontent.com/NVIDIA/NeMo/main/nemo_text_processing/install_pynini.sh\n", - "# !bash install_pynini.sh" + "# !python -m pip install git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[all]\n" ] }, { diff --git a/tutorials/tts/Inference_ModelSelect.ipynb b/tutorials/tts/Inference_ModelSelect.ipynb index 8fe398edafa6..223c1f62a19a 100644 --- a/tutorials/tts/Inference_ModelSelect.ipynb +++ b/tutorials/tts/Inference_ModelSelect.ipynb @@ -50,9 +50,7 @@ "# # If you're using Google Colab and not running locally, uncomment and run this cell.\n", "# !apt-get install sox libsndfile1 ffmpeg\n", "# !pip install wget text-unidecode\n", - "# !python -m pip install git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[all]\n", - "# !wget https://raw.githubusercontent.com/NVIDIA/NeMo/main/nemo_text_processing/install_pynini.sh\n", - "# !bash install_pynini.sh" + "# !python -m pip install git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[all]\n" ] }, { diff --git a/tutorials/tts/NeMo_TTS_Primer.ipynb b/tutorials/tts/NeMo_TTS_Primer.ipynb index e323536fa56b..12e3984fbe03 100644 --- a/tutorials/tts/NeMo_TTS_Primer.ipynb +++ b/tutorials/tts/NeMo_TTS_Primer.ipynb @@ -46,21 +46,6 @@ "!git clone https://github.com/NVIDIA/NeMo.git $NEMO_DIR" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "goNEA8hOAmAP", - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "# Install pynini for text processing\n", - "!bash $NEMO_DIR/nemo_text_processing/install_pynini.sh" - ] - }, { "cell_type": "markdown", "metadata": { diff --git a/tutorials/tts/Pronunciation_customization.ipynb b/tutorials/tts/Pronunciation_customization.ipynb index 14269ce33455..44a376b596ac 100644 --- a/tutorials/tts/Pronunciation_customization.ipynb +++ b/tutorials/tts/Pronunciation_customization.ipynb @@ -29,10 +29,8 @@ "BRANCH = 'main'\n", "# # If you're using Google Colab and not running locally, uncomment and run this cell.\n", "# !apt-get install sox libsndfile1 ffmpeg\n", - "# !pip install wget text-unidecode pynini==2.1.4\n", - "# !python -m pip install git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[all]\n", - "# !wget https://raw.githubusercontent.com/NVIDIA/NeMo/main/nemo_text_processing/install_pynini.sh\n", - "# !bash install_pynini.sh" + "# !pip install wget text-unidecode \n", + "# !python -m pip install git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[all]\n" ] }, { diff --git a/tutorials/tts/Tacotron2_Training.ipynb b/tutorials/tts/Tacotron2_Training.ipynb index a12cb980dc0b..7f3c65a81249 100644 --- a/tutorials/tts/Tacotron2_Training.ipynb +++ b/tutorials/tts/Tacotron2_Training.ipynb @@ -58,9 +58,7 @@ "# # If you're using Colab and not running locally, uncomment and run this cell.\n", "# !apt-get install sox libsndfile1 ffmpeg\n", "# !pip install wget text-unidecode\n", - "# !python -m pip install git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[all]\n", - "# !wget https://raw.githubusercontent.com/NVIDIA/NeMo/main/nemo_text_processing/install_pynini.sh\n", - "# !bash install_pynini.sh" + "# !python -m pip install git+https://github.com/NVIDIA/NeMo.git@$BRANCH#egg=nemo_toolkit[all]\n" ] }, { From b3c3ca64e3326fcbe533f1f706e10c064dee46f9 Mon Sep 17 00:00:00 2001 From: ekmb Date: Tue, 14 Feb 2023 20:07:24 -0800 Subject: [PATCH 16/19] update requirements Signed-off-by: ekmb --- .../requirements_nemo_text_processing.txt | 1 - requirements/requirements_tts.txt | 1 + setup.py | 36 ++----------------- 3 files changed, 4 insertions(+), 34 deletions(-) delete mode 100644 requirements/requirements_nemo_text_processing.txt diff --git a/requirements/requirements_nemo_text_processing.txt b/requirements/requirements_nemo_text_processing.txt deleted file mode 100644 index 989ce0ac03b0..000000000000 --- a/requirements/requirements_nemo_text_processing.txt +++ /dev/null @@ -1 +0,0 @@ -nemo_text_processing==0.1.6rc0 diff --git a/requirements/requirements_tts.txt b/requirements/requirements_tts.txt index d4604a044191..e4f246c210c3 100644 --- a/requirements/requirements_tts.txt +++ b/requirements/requirements_tts.txt @@ -9,3 +9,4 @@ nltk pandas pypinyin pypinyin-dict +nemo_text_processing==0.1.6rc0 diff --git a/setup.py b/setup.py index 328bbec60d44..315fed8b0caf 100644 --- a/setup.py +++ b/setup.py @@ -79,8 +79,6 @@ def req_file(filename, folder="requirements"): extras_require = { # User packages 'test': req_file("requirements_test.txt"), - # NeMo Tools - 'nemo_text_processing': req_file("requirements_nemo_text_processing.txt"), # Lightning Collections Packages 'core': req_file("requirements_lightning.txt"), 'common': req_file('requirements_common.txt'), @@ -95,39 +93,11 @@ def req_file(filename, folder="requirements"): extras_require['all'] = list(chain(extras_require.values())) # Add lightning requirements as needed -extras_require['nemo_text_processing'] = list(chain([extras_require['nemo_text_processing'], extras_require['core']])) extras_require['common'] = list(chain([extras_require['common'], extras_require['core']])) -extras_require['test'] = list( - chain( - [ - extras_require['tts'], - extras_require['core'], - extras_require['common'], - extras_require['nemo_text_processing'], - ] - ) -) +extras_require['test'] = list(chain([extras_require['tts'], extras_require['core'], extras_require['common'],])) extras_require['asr'] = list(chain([extras_require['asr'], extras_require['core'], extras_require['common']])) -extras_require['nlp'] = list( - chain( - [ - extras_require['nlp'], - extras_require['core'], - extras_require['common'], - extras_require['nemo_text_processing'], - ] - ) -) -extras_require['tts'] = list( - chain( - [ - extras_require['tts'], - extras_require['core'], - extras_require['common'], - extras_require['nemo_text_processing'], - ] - ) -) +extras_require['nlp'] = list(chain([extras_require['nlp'], extras_require['core'], extras_require['common'],])) +extras_require['tts'] = list(chain([extras_require['tts'], extras_require['core'], extras_require['common'],])) # TTS has extra dependencies extras_require['tts'] = list(chain([extras_require['tts'], extras_require['asr']])) From eb8adf21c73e890a523a118b21ae2e74100b480d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 15 Feb 2023 04:08:35 +0000 Subject: [PATCH 17/19] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- requirements/requirements_tts.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/requirements_tts.txt b/requirements/requirements_tts.txt index e4f246c210c3..f21fda57e6ff 100644 --- a/requirements/requirements_tts.txt +++ b/requirements/requirements_tts.txt @@ -5,8 +5,8 @@ jieba kornia librosa matplotlib +nemo_text_processing==0.1.6rc0 nltk pandas pypinyin pypinyin-dict -nemo_text_processing==0.1.6rc0 From 9e8e0dcb827cdd07a6928d97281e7ba2a5059665 Mon Sep 17 00:00:00 2001 From: ekmb Date: Tue, 14 Feb 2023 20:24:48 -0800 Subject: [PATCH 18/19] add support warning Signed-off-by: ekmb --- nemo/collections/tts/models/aligner.py | 1 + nemo/collections/tts/models/fastpitch.py | 1 + nemo/collections/tts/models/mixer_tts.py | 1 + nemo/collections/tts/models/radtts.py | 1 + nemo/collections/tts/models/tacotron2.py | 1 + nemo/collections/tts/models/vits.py | 1 + 6 files changed, 6 insertions(+) diff --git a/nemo/collections/tts/models/aligner.py b/nemo/collections/tts/models/aligner.py index ce1eeba309ef..4f0744776641 100644 --- a/nemo/collections/tts/models/aligner.py +++ b/nemo/collections/tts/models/aligner.py @@ -103,6 +103,7 @@ def _setup_tokenizer(self, cfg): cfg.text_tokenizer.g2p['_target_'] = cfg.text_tokenizer.g2p['_target_'].replace( "nemo_text_processing.g2p", "nemo.collections.tts.g2p" ) + logging.warning("This checkpoint support will be dropped after r1.18.0.") g2p_kwargs = {} diff --git a/nemo/collections/tts/models/fastpitch.py b/nemo/collections/tts/models/fastpitch.py index 70c6cd6f14f5..d22c8d47038c 100644 --- a/nemo/collections/tts/models/fastpitch.py +++ b/nemo/collections/tts/models/fastpitch.py @@ -206,6 +206,7 @@ def _setup_tokenizer(self, cfg): cfg.text_tokenizer.g2p['_target_'] = cfg.text_tokenizer.g2p['_target_'].replace( "nemo_text_processing.g2p", "nemo.collections.tts.g2p" ) + logging.warning("This checkpoint support will be dropped after r1.18.0.") g2p_kwargs = {} diff --git a/nemo/collections/tts/models/mixer_tts.py b/nemo/collections/tts/models/mixer_tts.py index 5f59e93554c5..225076b7b027 100644 --- a/nemo/collections/tts/models/mixer_tts.py +++ b/nemo/collections/tts/models/mixer_tts.py @@ -153,6 +153,7 @@ def _setup_tokenizer(self, cfg): cfg.text_tokenizer.g2p['_target_'] = cfg.text_tokenizer.g2p['_target_'].replace( "nemo_text_processing.g2p", "nemo.collections.tts.g2p" ) + logging.warning("This checkpoint support will be dropped after r1.18.0.") g2p_kwargs = {} diff --git a/nemo/collections/tts/models/radtts.py b/nemo/collections/tts/models/radtts.py index ac2bf2dd19b1..f7f1344eaf37 100644 --- a/nemo/collections/tts/models/radtts.py +++ b/nemo/collections/tts/models/radtts.py @@ -342,6 +342,7 @@ def _setup_tokenizer(self, cfg): cfg.text_tokenizer.g2p['_target_'] = cfg.text_tokenizer.g2p['_target_'].replace( "nemo_text_processing.g2p", "nemo.collections.tts.g2p" ) + logging.warning("This checkpoint support will be dropped after r1.18.0.") g2p_kwargs = {} diff --git a/nemo/collections/tts/models/tacotron2.py b/nemo/collections/tts/models/tacotron2.py index 20fee29a7c31..2ed69c8809a0 100644 --- a/nemo/collections/tts/models/tacotron2.py +++ b/nemo/collections/tts/models/tacotron2.py @@ -328,6 +328,7 @@ def _setup_tokenizer(self, cfg): cfg.text_tokenizer.g2p['_target_'] = cfg.text_tokenizer.g2p['_target_'].replace( "nemo_text_processing.g2p", "nemo.collections.tts.g2p" ) + logging.warning("This checkpoint support will be dropped after r1.18.0.") g2p_kwargs = {} diff --git a/nemo/collections/tts/models/vits.py b/nemo/collections/tts/models/vits.py index 832197195742..7b2f20743184 100644 --- a/nemo/collections/tts/models/vits.py +++ b/nemo/collections/tts/models/vits.py @@ -118,6 +118,7 @@ def _setup_tokenizer(self, cfg): cfg.text_tokenizer.g2p['_target_'] = cfg.text_tokenizer.g2p['_target_'].replace( "nemo_text_processing.g2p", "nemo.collections.tts.g2p" ) + logging.warning("This checkpoint support will be dropped after r1.18.0.") g2p_kwargs = {} From e304858cd301f0ad038f0c5ac4549f58b63b2d47 Mon Sep 17 00:00:00 2001 From: ekmb Date: Wed, 15 Feb 2023 11:20:32 -0800 Subject: [PATCH 19/19] review Signed-off-by: ekmb --- README.rst | 6 +---- .../tts/aligner_heteronym_disambiguation.py | 12 +++++----- examples/tts/g2p/g2p_train_and_evaluate.py | 2 +- .../text_to_speech/tts_tokenizers.py | 22 +++++++++---------- nemo/collections/tts/g2p/data/ctc_g2p.py | 2 +- nemo/collections/tts/g2p/data/t5_g2p.py | 2 +- nemo/collections/tts/g2p/modules.py | 6 ++--- .../export_wikihomograph_data_to_manifest.py | 4 ++-- 8 files changed, 26 insertions(+), 30 deletions(-) diff --git a/README.rst b/README.rst index 7aa25ba88f21..7bbc1fe5037a 100644 --- a/README.rst +++ b/README.rst @@ -243,11 +243,7 @@ Transformer Engine enables FP8 training on NVIDIA Hopper GPUs. NeMo Text Processing ~~~~~~~~~~~~~~~~~~~~ -NeMo Text Processing, specifically (Inverse) Text Normalization, requires `Pynini `_ to be installed. - -.. code-block:: bash - - bash NeMo/nemo_text_processing/install_pynini.sh +NeMo Text Processing, specifically (Inverse) Text Normalization, is now a separate repository `https://github.com/NVIDIA/NeMo-text-processing `_. Docker containers: ~~~~~~~~~~~~~~~~~~ diff --git a/examples/tts/aligner_heteronym_disambiguation.py b/examples/tts/aligner_heteronym_disambiguation.py index ad6b1ac13eee..fe5d89747a2b 100644 --- a/examples/tts/aligner_heteronym_disambiguation.py +++ b/examples/tts/aligner_heteronym_disambiguation.py @@ -26,7 +26,7 @@ """ -G2P_paper disambiguation using an Aligner model's input embedding distances. +G2P disambiguation using an Aligner model's input embedding distances. Does not handle OOV and leaves them as graphemes. @@ -46,7 +46,7 @@ def get_args(): """Retrieve arguments for disambiguation. """ - parser = argparse.ArgumentParser("G2P_paper disambiguation using Aligner input embedding distances.") + parser = argparse.ArgumentParser("G2P disambiguation using Aligner input embedding distances.") # TODO(jocelynh): Make this required=False with default download from NGC once ckpt uploaded parser.add_argument('--model', required=True, type=str, help="Path to Aligner model checkpoint (.nemo file).") parser.add_argument( @@ -108,7 +108,7 @@ def disambiguate_candidates(aligner, text, spec, spec_len, confidence, device, h Note: This could be sped up if multiple words' candidates were batched, but this is conceptually easier. """ - # Grab original G2P_paper result + # Grab original G2P result aligner_g2p = aligner.tokenizer.g2p base_g2p = aligner_g2p(text) @@ -122,7 +122,7 @@ def disambiguate_candidates(aligner, text, spec, spec_len, confidence, device, h has_heteronym = False for word in words: - # Retrieve the length of the word in the default G2P_paper conversion + # Retrieve the length of the word in the default G2P conversion g2p_default_len = len(aligner_g2p(word)) # Check if word needs to be disambiguated @@ -134,7 +134,7 @@ def disambiguate_candidates(aligner, text, spec, spec_len, confidence, device, h candidate_prons_and_lengths = [] for pron in aligner_g2p.phoneme_dict[word]: - # Replace graphemes in the base G2P_paper result with the current variant + # Replace graphemes in the base G2P result with the current variant candidate = base_g2p[:word_start_idx] + pron + base_g2p[word_start_idx + g2p_default_len :] candidate_tokens = aligner.tokenizer.encode_from_g2p(candidate) @@ -247,7 +247,7 @@ def disambiguate_dataset( count = 0 for line in f_in: - # Retrieve entry and base G2P_paper conversion for full text + # Retrieve entry and base G2P conversion for full text entry = json.loads(line) # Set punct_post_process=True in order to preserve words with apostrophes text = aligner.normalizer.normalize(entry['text'], punct_post_process=True) diff --git a/examples/tts/g2p/g2p_train_and_evaluate.py b/examples/tts/g2p/g2p_train_and_evaluate.py index e2edef695180..d7a98a4d77ad 100644 --- a/examples/tts/g2p/g2p_train_and_evaluate.py +++ b/examples/tts/g2p/g2p_train_and_evaluate.py @@ -40,7 +40,7 @@ Example of the config file: NeMo/examples/text_processing/g2p/conf/t5_g2p.yaml -# Training Conformer-G2P_paper Model and evaluation at the end of training: +# Training Conformer-G2P Model and evaluation at the end of training: python examples/text_processing/g2p/g2p_train_and_evaluate.py \ # (Optional: --config-path= --config-name=) \ model.train_ds.manifest_filepath="" \ diff --git a/nemo/collections/common/tokenizers/text_to_speech/tts_tokenizers.py b/nemo/collections/common/tokenizers/text_to_speech/tts_tokenizers.py index 2ee5ee489722..a708355622d4 100644 --- a/nemo/collections/common/tokenizers/text_to_speech/tts_tokenizers.py +++ b/nemo/collections/common/tokenizers/text_to_speech/tts_tokenizers.py @@ -446,11 +446,11 @@ def encode(self, text): def encode_from_g2p(self, g2p_text: List[str], raw_text: Optional[str] = None): """ - Encodes text that has already been run through G2P_paper. - Called for encoding to tokens after text preprocessing and G2P_paper. + Encodes text that has already been run through G2P. + Called for encoding to tokens after text preprocessing and G2P. Args: - g2p_text: G2P_paper's output, could be a mixture of phonemes and graphemes, + g2p_text: G2P's output, could be a mixture of phonemes and graphemes, e.g. "see OOV" -> ['S', 'IY1', ' ', 'O', 'O', 'V'] raw_text: original raw input """ @@ -533,11 +533,11 @@ def __init__( """ if not hasattr(g2p, "symbols"): logging.error( - f"Please make sure the G2P_paper module passed into the IPATokenizer has a `symbols` attribute. " + f"Please make sure the G2P module passed into the IPATokenizer has a `symbols` attribute. " f"This is required in order to build the tokenizer vocabulary.\n" f"Expected e.g. IPAG2P, found {type(g2p)}" ) - raise ValueError("G2P_paper modules passed into the IPATokenizer must have `symbols` defined.") + raise ValueError("G2P modules passed into the IPATokenizer must have `symbols` defined.") if locale is not None: validate_locale(locale) @@ -595,14 +595,14 @@ def encode(self, text: str) -> List[int]: def encode_from_g2p(self, g2p_text: List[str], raw_text: Optional[str] = None) -> List[int]: """ - Tokenize the `g2p_text` that has been already run through G2P_paper. Each item in the `g2p_text` would be encoded as + Tokenize the `g2p_text` that has been already run through G2P. Each item in the `g2p_text` would be encoded as one of the integer IDs predefined in `self._token2id`. Note that this function should be called after `self.text_preprocessing_func` and `self.g2p` functions Args: - g2p_text (List[str]): a sequence of tokens from G2P_paper's output. It could be a sequence of phonemes, a sequence + g2p_text (List[str]): a sequence of tokens from G2P's output. It could be a sequence of phonemes, a sequence of graphemes, or a mixture of both. For example, `['ˈ', 's', 'i', ' ', '#O', '#O', '#V']`, which is the - G2P_paper's output of the text "see OOV", where '#' is prepended to each grapheme in order to distinguish + G2P's output of the text "see OOV", where '#' is prepended to each grapheme in order to distinguish graphemes from phonemes if there are overlaps in between. The prefix '#' can be customized in `nemo.collections.tts.g2p.modules.IPAG2P.grapheme_prefix`. raw_text (str): the original text after calling `self.text_preprocessing_func`. It is optional. It is only @@ -731,11 +731,11 @@ def encode(self, text): def encode_from_g2p(self, g2p_text: List[str], raw_text: Optional[str] = None): """ - Encodes text that has already been run through G2P_paper. - Called for encoding to tokens after text preprocessing and G2P_paper. + Encodes text that has already been run through G2Pr. + Called for encoding to tokens after text preprocessing and G2P. Args: - g2p_text: G2P_paper's output, could be a mixture of Chinese phonemes and English letters. + g2p_text: G2P's output, could be a mixture of Chinese phonemes and English letters. raw_text: original raw input """ ps, space, tokens = [], self.tokens[self.space], set(self.tokens) diff --git a/nemo/collections/tts/g2p/data/ctc_g2p.py b/nemo/collections/tts/g2p/data/ctc_g2p.py index 6b0052db6996..792a92a10fa6 100644 --- a/nemo/collections/tts/g2p/data/ctc_g2p.py +++ b/nemo/collections/tts/g2p/data/ctc_g2p.py @@ -37,7 +37,7 @@ def __init__( with_labels: bool = True, ): """ - Creates a dataset to train a CTC-based G2P_paper models. + Creates a dataset to train a CTC-based G2P models. Args: manifest_filepath: path to a .json manifest that contains "phoneme_field" and "grapheme_field" diff --git a/nemo/collections/tts/g2p/data/t5_g2p.py b/nemo/collections/tts/g2p/data/t5_g2p.py index 200d057fb0ab..0e4c5f2697b6 100644 --- a/nemo/collections/tts/g2p/data/t5_g2p.py +++ b/nemo/collections/tts/g2p/data/t5_g2p.py @@ -40,7 +40,7 @@ def __init__( with_labels: bool = True, ): """ - Dataset to train T5-based G2P_paper generative model. + Dataset to train T5-based G2P generative model. Args: manifest_filepath: path to a .json manifest that contains "phoneme_field" and "grapheme_field" diff --git a/nemo/collections/tts/g2p/modules.py b/nemo/collections/tts/g2p/modules.py index 67b0f901cf0d..219d595ec05a 100644 --- a/nemo/collections/tts/g2p/modules.py +++ b/nemo/collections/tts/g2p/modules.py @@ -102,7 +102,7 @@ def __init__( phoneme_probability: Optional[float] = None, mapping_file: Optional[str] = None, ): - """English G2P_paper module. This module converts words from grapheme to phoneme representation using phoneme_dict in CMU dict format. + """English G2P module. This module converts words from grapheme to phoneme representation using phoneme_dict in CMU dict format. Optionally, it can ignore words which are heteronyms, ambiguous or marked as unchangeable by word_tokenize_func (see code for details). Ignored words are left unchanged or passed through apply_to_oov_word for handling. Args: @@ -310,7 +310,7 @@ def __init__( mapping_file: Optional[str] = None, ) -> None: """ - Generic IPA G2P_paper module. This module converts words from graphemes to International Phonetic Alphabet + Generic IPA G2P module. This module converts words from graphemes to International Phonetic Alphabet representations. Optionally, it can ignore heteronyms, ambiguous words, or words marked as unchangeable by `word_tokenize_func` (see code for details). Ignored words are left unchanged or passed through `apply_to_oov_word` for handling. @@ -683,7 +683,7 @@ def __init__( mapping_file: Optional[str] = None, word_segmenter: Optional[str] = None, ): - """Chinese G2P_paper module. This module first converts Chinese characters into pinyin sequences using pypinyin, then pinyin sequences would + """Chinese G2P module. This module first converts Chinese characters into pinyin sequences using pypinyin, then pinyin sequences would be further converted into phoneme sequences using pinyin_dict_nv_22.10.txt dict file. For Chinese and English bilingual sentences, the English words would be converted into letters. Args: diff --git a/scripts/dataset_processing/g2p/export_wikihomograph_data_to_manifest.py b/scripts/dataset_processing/g2p/export_wikihomograph_data_to_manifest.py index 28f9cdd4e499..0819dc8384ce 100644 --- a/scripts/dataset_processing/g2p/export_wikihomograph_data_to_manifest.py +++ b/scripts/dataset_processing/g2p/export_wikihomograph_data_to_manifest.py @@ -125,8 +125,8 @@ def correct_wikihomograph_data(sentence: str, start: int = None, end: int = None if sentence in corrections: start, end = corrections[sentence] - sentence = sentence.replace("2014Coordinate", "2014 Coordinate") # for normalized data for G2P_paper OOV models - sentence = sentence.replace("AAA", "triple A") # for normalized data for G2P_paper OOV models + sentence = sentence.replace("2014Coordinate", "2014 Coordinate") # for normalized data for G2P OOV models + sentence = sentence.replace("AAA", "triple A") # for normalized data for G2P OOV models return sentence, start, end

P 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 diff --git a/tutorials/text_processing/images/pipeline.png b/tutorials/text_processing/images/pipeline.png deleted file mode 100644 index ae9132ff24e5f44cbafdf05a34b4fac8d84c5bd7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 225984 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%0DR(xz<_$NO0D)>-bS1bA{t{n=Q;<|duJZ9P4 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