chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:25:10 +08:00
commit c397331b1e
3684 changed files with 990993 additions and 0 deletions
@@ -0,0 +1,7 @@
from fun_text_processing.inverse_text_normalization.id.taggers.tokenize_and_classify import (
ClassifyFst,
)
from fun_text_processing.inverse_text_normalization.id.verbalizers.verbalize import VerbalizeFst
from fun_text_processing.inverse_text_normalization.id.verbalizers.verbalize_final import (
VerbalizeFinalFst,
)
@@ -0,0 +1,384 @@
from argparse import ArgumentParser
from typing import List
import regex as re
from fun_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", "", 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 juta", un_normalized)
un_normalized = re.sub(r"(\d)bn?\s*$", r"\1 milyar", 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="./id_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)
@@ -0,0 +1,37 @@
$ dolar
$ dolar Amerika
$ dolar amerika serikat
Rp rupiah
IDR rupiah
£ pound inggris
€ euro
₩ won
nzd dolar selandia baru
rs rupee
chf swiss franc
dkk kroner Denmark
fim markka finlandia
aed dirham emirat arab
¥ yen
czk koruna ceko
mro ouguiya mauritania
pkr rupee pakistan
crc usus besar kosta rika
hk$ dollar Hongkong
npr rupee nepal
awg florin aruba
nok kroner norwegia
tzs shilling tanzan
sek kronor Swedia
cyp pound siprus
r nyata
sar riyal saudi
cve escudo tanjung verde
rsd dinar serbia
dm tanda jerman
shp santo helena pound
php peso filipina
cad dolar Kanada
ssp pound sunda selatan
scr rupiah seychelles
mvr rufiyaa maldivian
1 $ dolar
2 $ dolar Amerika
3 $ dolar amerika serikat
4 Rp rupiah
5 IDR rupiah
6 £ pound inggris
7 euro
8 won
9 nzd dolar selandia baru
10 rs rupee
11 chf swiss franc
12 dkk kroner Denmark
13 fim markka finlandia
14 aed dirham emirat arab
15 ¥ yen
16 czk koruna ceko
17 mro ouguiya mauritania
18 pkr rupee pakistan
19 crc usus besar kosta rika
20 hk$ dollar Hongkong
21 npr rupee nepal
22 awg florin aruba
23 nok kroner norwegia
24 tzs shilling tanzan
25 sek kronor Swedia
26 cyp pound siprus
27 r nyata
28 sar riyal saudi
29 cve escudo tanjung verde
30 rsd dinar serbia
31 dm tanda jerman
32 shp santo helena pound
33 php peso filipina
34 cad dolar Kanada
35 ssp pound sunda selatan
36 scr rupiah seychelles
37 mvr rufiyaa maldivian
@@ -0,0 +1,11 @@
com
uk
fr
net
br
in
ru
de
it
ai
id
1 com
2 uk
3 fr
4 net
5 br
6 in
7 ru
8 de
9 it
10 ai
11 id
@@ -0,0 +1,17 @@
g mail gmail
gmail
n vidia nvidia
nvidia
outlook
hotmail
yahoo
aol
gmx
msn
live
yandex
orange
wanadoo
web
comcast
google
1 g mail gmail
2 gmail
3 n vidia nvidia
4 nvidia
5 outlook
6 hotmail
7 yahoo
8 aol
9 gmx
10 msn
11 live
12 yandex
13 orange
14 wanadoo
15 web
16 comcast
17 google
@@ -0,0 +1,130 @@
. dot
- berlari
- tanda penghubung
_ menggarisbawahi
! tanda seru
$ tanda dollar
& simbol untuk 'dan
' mengutip
* asterisk
+ plus
/ memotong
? tanda tanya
^ sirkomfleks
` kutipan tunggal yang tepat
{ penyangga kiri
| batang vertikal
} penjepit kanan
~ pasang surut
, koma
% persen
# tanda pagar
= sama dengan
@ at
_ garis bawah
~ tilde
≥ lebih besar dari atau sama dengan
≤ lebih kecil dari atau sama dengan
≠ tidak sama dengan
≈ mendekati sama dengan
± kurang lebih
× kali
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
Α alfa
Β beta
Γ gamma
Δ delta
Ε epsilon
Ζ zeta
Θ theta
Ι iota
Κ kappa
∧ lambda
Μ mu
Ν nu
Ξ ksi
Ο omikron
∏ pi
Ρ rho
∑ sigma
Τ tau
Υ upsilon
Φ phi
Χ khi
Ψ psi
Ω omega
α alfa
β beta
γ gamma
δ delta
ε epsilon
ζ zeta
η eta
θ theta
ι iota
κ kappa
λ lambda
μ mu
ν nu
ξ ksi
ο omikron
π pi
ρ rho
σ sigma
τ tau
υ upsilon
φ phi
χ khi
ψ psi
ω omega
1 . dot
2 - berlari
3 - tanda penghubung
4 _ menggarisbawahi
5 ! tanda seru
6 $ tanda dollar
7 & simbol untuk 'dan
8 ' mengutip
9 * asterisk
10 + plus
11 / memotong
12 ? tanda tanya
13 ^ sirkomfleks
14 ` kutipan tunggal yang tepat
15 { penyangga kiri
16 | batang vertikal
17 } penjepit kanan
18 ~ pasang surut
19 , koma
20 % persen
21 # tanda pagar
22 = sama dengan
23 @ at
24 _ garis bawah
25 ~ tilde
26 lebih besar dari atau sama dengan
27 lebih kecil dari atau sama dengan
28 tidak sama dengan
29 mendekati sama dengan
30 ± kurang lebih
31 × kali
32 A A
33 B B
34 C C
35 D D
36 E E
37 F F
38 G G
39 H H
40 I I
41 J J
42 K K
43 L L
44 M M
45 N N
46 O O
47 P P
48 Q Q
49 R R
50 S S
51 T T
52 U U
53 V V
54 W W
55 X X
56 Y Y
57 Z Z
58 a A
59 b B
60 c C
61 d D
62 e E
63 f F
64 g G
65 h H
66 i I
67 j J
68 k K
69 l L
70 m M
71 n N
72 o O
73 p P
74 q Q
75 r R
76 s S
77 t T
78 u U
79 v V
80 w W
81 x X
82 y Y
83 z Z
84 Α alfa
85 Β beta
86 Γ gamma
87 Δ delta
88 Ε epsilon
89 Ζ zeta
90 Θ theta
91 Ι iota
92 Κ kappa
93 lambda
94 Μ mu
95 Ν nu
96 Ξ ksi
97 Ο omikron
98 pi
99 Ρ rho
100 sigma
101 Τ tau
102 Υ upsilon
103 Φ phi
104 Χ khi
105 Ψ psi
106 Ω omega
107 α alfa
108 β beta
109 γ gamma
110 δ delta
111 ε epsilon
112 ζ zeta
113 η eta
114 θ theta
115 ι iota
116 κ kappa
117 λ lambda
118 μ mu
119 ν nu
120 ξ ksi
121 ο omikron
122 π pi
123 ρ rho
124 σ sigma
125 τ tau
126 υ upsilon
127 φ phi
128 χ khi
129 ψ psi
130 ω omega
@@ -0,0 +1,4 @@
k ribu
m juta
b milyar
t triliun
1 k ribu
2 m juta
3 b milyar
4 t triliun
@@ -0,0 +1,361 @@
fahrenheit f
celsius c
kilometer km
meter m
sentimeter cm
milimeter mm
hektar ha
mil mi
meter persegi m²
kilometer persegi km²
kaki ft
persen %
hertz hz
kilowat kw
daya kuda hp
miligram mg
kilogram kg
gigahertz ghz
kilohertz khz
megahertz mhz
volt v
jam h
mega coulomb mc
kedua s
nanometer nm
revolusi per menit rpm
menit min
mili ampere mA
persen %
kilo watt jam kwh
meter kubik m³
mil per jam mph
tera watt tw
mili volt mv
megawatt mw
mikrometer μm
terabyte tb
c c cc
gram g
dalton da
suasana atm
ohm ω
desibel db
peta kedua ps
ons oz
hekto liter hl
mikrogram μg
petagram pg
gigabyte gb
kilobit kb
elektron volt ev
megabita mb
kilobyte kb
kilobit per detik kbps
megabit per detik mbps
batu st
kilo liter kl
tera joule tj
kilo volt kv
mega volt mv
kilonewton kn
megameter mm
satuan astronomi au
halaman yd
radian rad
lumen lm
hekto detik hs
tahi lalat mol
giga pascal gpa
mililiter ml
gigawatt gw
mega ampere ma
simpul kt
kekuatan kilogram kgf
nano gram ng
nanodetik ns
mega siemens ms
batang bar
giga liter gl
mikrodetik μs
desi ampere da
pascal pa
desi detik ds
mili detik ms
meteran desi dm
kubik desi meter dm³
satuan massa atom amu
megabita mb
mega farad mf
becquerel bq
petabit pb
milimeter persegi mm²
sentimeter persegi cm²
mil persegi sq mi
kaki persegi sq ft
kilopascal kpa
candela cd
tera liter tl
mega detik ms
megapascal mpa
meteran peta pm
peta byte pb
giga watt jam gwh
kilo kalori kcal
abu-abu gy
saringan sv
kelas seratus cwt
c c cc
kaki persegi sq ft
inci persegi sq in
kaki persegi sqft
kaki persegi SqFt
milidetik msec
kilowatt jam kw·h
miliampere-jam mA⋅h
kilokalori kcal
kilokalori kCal
kilokalori Kcal
milimeter merkuri mmhg
milimeter merkuri mmHg
milimeter persegi mm2
milimeter kubik mm3
sentimeter persegi cm2
sentimeter kubik cm3
kilometer persegi km2
kilometer kubik km3
desimeter persegi dm2
desimeter kubik dm3
detik sec
jam hrs
mil per jam mph
kiloherts khz
kiloherts kHz
megahertz mhz
megahertz mHz
gigahertz ghz
gigahertz gHz
kilowatt jam kwh
mol mol
sendok the tsp
kilopascal kPa
volume vol
volume Vol
rotasi per menit rpm
detak per menit bpm
galon gal
pascal pa
megapascal mpa
miliampere-jam mah
menit min
detik SEC
kalori cal
kilokalori Cal
meter persegi m2
meter kubik m3
milimeter mm
sentimeter cm
sentimeter CM
kilometer km
kilometer KM
miligram mg
kilogram kg
kilogram KG
jam hr
hertz hz
hertz Hz
mililiter ml
mililiter mL
miliampere mA
sentimeter kubik cc
piksel px
piksel PX
volt v
volt V
kilovolt kv
kilovolt kV
hektar ha
ekar ac
karat ct
inci in
kaki ft
yard yd
nanometer nm
desimeter dm
kilobyte kb
megabyte mb
gigabyte gb
terabyte tb
kilobyte KB
megabyte MB
gigabyte GB
terabyte TB
daya kuda hp
desibel db
desibel dB
kilojoule kj
kilojoule kJ
ons oz
kilowatt kw
kilowatt KW
joule j
joule J
gram g
gram G
liter l
liter L
meter m
ohm-meter Ω·m
mikrometer μm
mikrogram μg
mikroampere μA
derajat Celsius °c
derajat Celsius ˚c
derajat Fahrenheit °f
derajat Fahrenheit ˚f
milimeter persegi mm²
milimeter kubik mm³
sentimeter persegi cm²
sentimeter kubik cm³
kilometer persegi km²
kilometer kubik km³
desimeter kubik dm³
meter persegi m²
meter kubik m³
derajat °
derajat ˚
derajat Celsius ℃
derajat Fahrenheit ℉
ohm Ω
mikrometer μm
milimeter persegi mm2
milimeter kubik mm3
milimeter mm
sentimeter persegi cm2
sentimeter kubik cm3
sentimeter cm
sentimeter CM
kilometer persegi km2
kilometer kubik km3
kilometer km
kilometer KM
desimeter persegi dm2
desimeter kubik dm3
meter persegi m2
meter kubik m3
kaki persegi sq ft
kaki persegi sq. ft
kaki persegi sq.ft
kaki persegi sqft
kaki persegi SqFt
inci persegi sq in
mikrogram μg
miligram mg
kilogram kg
kilogram KG
milidetik msec
detik sec
jam hr
jam hrs
meter per detik m/s
kilometer per jam km/h
mil per jam mph
bit per detik bit/s
bit per detik Bit/s
byte per detik byte/s
byte per detik Byte/s
derajat Celsius °c
derajat Celsius ˚c
derajat Fahrenheit °f
derajat Fahrenheit ˚f
kilokalori kcal
kilokalori kCal
kilokalori Kcal
ons cairan fl.oz
farad per meter F/m
gram per liter g/l
gram per mililiter g/mL
hertz hz
hertz Hz
kiloherts khz
kiloherts kHz
megahertz mhz
megahertz mHz
gigahertz ghz
gigahertz gHz
kilometer per jam km/h
kilowatt per jam kw/h
kilowatt jam kw·h
kilowatt jam kS·h
kilowatt jam kwh
kilowatt jam kSh
mililiter ml
mililiter mL
miligram per mililiter mg/ml
miligram per mililiter mg/mL
miligram per liter mg/l
miligram per liter mg/L
miliampere mA
miliampere-jam mA⋅h
mol mol
ohm-meter Ω·m
siemens per meter S/m
sendok the tsp
mikroampere μA
kilopascal kPa
milimeter merkuri mmhg
milimeter merkuri mmHg
volume vol
volume Vol
sentimeter kubik cc
rotasi per menit rpm
rotasi per menit r/min
detak per menit bpm
piksel px
piksel PX
volt v
volt V
kilovolt kv
kilovolt kV
hektar ha
ekar ac
karat ct
liter l
liter L
galon gal
mol mol
pascal pa
megapascal mpa
miliampere ma
miliampere-jam mah
inci in
kaki ft
yard yd
nanometer nm
meter m
desimeter dm
gram g
gram G
kilobyte kb
megabyte mb
gigabyte gb
terabyte tb
kilobyte KB
megabyte MB
gigabyte GB
terabyte TB
daya kuda hp
desibel db
desibel dB
joule j
joule J
kilojoule kj
kilojoule kJ
ons oz
kilowatt kw
kilowatt KW
menit min
detik SEC
kalori cal
kilokalori Cal
inci "
Can't render this file because it contains an unexpected character in line 361 and column 7.
@@ -0,0 +1,12 @@
Januari
Februari
Maret
April
Mei
Juni
Juli
Agustus
September
Oktober
November
Desember
1 Januari
2 Februari
3 Maret
4 April
5 Mei
6 Juni
7 Juli
8 Agustus
9 September
10 Oktober
11 November
12 Desember
@@ -0,0 +1,9 @@
satu 1
dua 2
tiga 3
empat 4
lima 5
enam 6
tujuh 7
delapan 8
sembilan 9
1 satu 1
2 dua 2
3 tiga 3
4 empat 4
5 lima 5
6 enam 6
7 tujuh 7
8 delapan 8
9 sembilan 9
@@ -0,0 +1 @@
titik .
1 titik .
@@ -0,0 +1,2 @@
ratus
seratus
1 ratus
2 seratus
@@ -0,0 +1,10 @@
ratus 1
seratus 1
dua ratus 2
tiga ratus 3
empat ratus 4
lima ratus 5
enam ratus 6
tujuh ratus 7
delapan ratus 8
sembilan ratus 9
1 ratus 1
2 seratus 1
3 dua ratus 2
4 tiga ratus 3
5 empat ratus 4
6 lima ratus 5
7 enam ratus 6
8 tujuh ratus 7
9 delapan ratus 8
10 sembilan ratus 9
@@ -0,0 +1,15 @@
sepuluh 10
sebelas 11
duabelas 12
dua belas 12
tigabelas 13
tiga belas 13
empatbelas 14
empat belas 14
limabelas 15
lima belas 15
enambelas 16
enam belas 16
tujuh belas 17
delapan belas 18
sembilan belas 19
1 sepuluh 10
2 sebelas 11
3 duabelas 12
4 dua belas 12
5 tigabelas 13
6 tiga belas 13
7 empatbelas 14
8 empat belas 14
9 limabelas 15
10 lima belas 15
11 enambelas 16
12 enam belas 16
13 tujuh belas 17
14 delapan belas 18
15 sembilan belas 19
@@ -0,0 +1,10 @@
ribu 1
seribu 1
dua ribu 2
tiga ribu 3
empat ribu 4
lima ribu 5
enam ribu 6
tujuh ribu 7
delapan ribu 8
sembilan ribu 9
1 ribu 1
2 seribu 1
3 dua ribu 2
4 tiga ribu 3
5 empat ribu 4
6 lima ribu 5
7 enam ribu 6
8 tujuh ribu 7
9 delapan ribu 8
10 sembilan ribu 9
@@ -0,0 +1,23 @@
ribu
seribu
juta
miliar
triliun
milion lipat empat
triliun
sextillion
septillion
oktillion
nonmiliar
satu juta
undecillion
duodecillion
triliun
quattuordecillion
quindecillion
sexdecillion
septendeciliun
octodecillion
novemdecillion
vigintillion
centillion
1 ribu
2 seribu
3 juta
4 miliar
5 triliun
6 milion lipat empat
7 triliun
8 sextillion
9 septillion
10 oktillion
11 nonmiliar
12 satu juta
13 undecillion
14 duodecillion
15 triliun
16 quattuordecillion
17 quindecillion
18 sexdecillion
19 septendeciliun
20 octodecillion
21 novemdecillion
22 vigintillion
23 centillion
@@ -0,0 +1,9 @@
dua puluh 2
tigapuluh 3
empat puluh 4
empat puluh 4
lima puluh 5
enam puluh 6
tujuh puluh 7
delapan puluh 8
sembilan puluh 9
1 dua puluh 2
2 tigapuluh 3
3 empat puluh 4
4 empat puluh 4
5 lima puluh 5
6 enam puluh 6
7 tujuh puluh 7
8 delapan puluh 8
9 sembilan puluh 9
@@ -0,0 +1 @@
nol 0
1 nol 0
@@ -0,0 +1,9 @@
pertama satu
kedua dua
ketiga tiga
keempat empat
kelima lima
keenam enam
ketujuh tujuh
kedelapan delapan
kesembilan sembilan
1 pertama satu
2 kedua dua
3 ketiga tiga
4 keempat empat
5 kelima lima
6 keenam enam
7 ketujuh tujuh
8 kedelapan delapan
9 kesembilan sembilan
@@ -0,0 +1 @@
keduabelas dua belas
1 keduabelas dua belas
@@ -0,0 +1,83 @@
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
1 deer
2 fish
3 sheep
4 foot feet
5 goose geese
6 man men
7 mouse mice
8 tooth teeth
9 woman women
10 won
11 child children
12 ox oxen
13 wife wives
14 wolf wolves
15 analysis analyses
16 criterion criteria
17 lbs
18 focus foci
19 percent
20 hertz
21 kroner krone
22 inch inches
23 calory calories
24 yen
25 megahertz
26 gigahertz
27 kilohertz
28 hertz
29 CC
30 c c
31 horsepower
32 hundredweight
33 kilogram force kilograms force
34 mega siemens
35 revolution per minute revolutions per minute
36 mile per hour miles per hour
37 megabit per second megabits per second
38 square foot square feet
39 kilobit per second kilobits per second
40 degree Celsius degrees Celsius
41 degree Fahrenheit degrees Fahrenheit
42 ATM
43 AU
44 BQ
45 CC
46 CD
47 DA
48 EB
49 EV
50 F
51 GB
52 G
53 GL
54 GPA
55 GY
56 HA
57 H
58 HL
59 GP
60 HS
61 KB
62 KL
63 KN
64 KT
65 KV
66 LM
67 MA
68 MA
69 MB
70 MC
71 MF
72 M
73 MM
74 MS
75 MV
76 MW
77 PB
78 PG
79 PS
80 S
81 TB
82 YB
83 ZB
@@ -0,0 +1,59 @@
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
1 1 59
2 2 58
3 3 57
4 4 56
5 5 55
6 6 54
7 7 53
8 8 52
9 9 51
10 10 50
11 11 49
12 12 48
13 13 47
14 14 46
15 15 45
16 16 44
17 17 43
18 18 42
19 19 41
20 20 40
21 21 39
22 22 38
23 23 37
24 24 36
25 25 35
26 26 34
27 27 33
28 28 32
29 29 31
30 30 30
31 31 29
32 32 28
33 33 27
34 34 26
35 35 25
36 36 24
37 37 23
38 38 22
39 39 21
40 40 20
41 41 19
42 42 18
43 43 17
44 44 16
45 45 15
46 46 14
47 47 13
48 48 12
49 49 11
50 50 10
51 51 9
52 52 8
53 53 7
54 54 6
55 55 5
56 56 4
57 57 3
58 58 2
59 59 1
@@ -0,0 +1,8 @@
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.
1 p m p.m.
2 pm p.m.
3 p.m.
4 p.m p.m.
5 am a.m.
6 a.m.
7 a.m a.m.
8 a m a.m.
@@ -0,0 +1,7 @@
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
1 cst c s t
2 cet c e t
3 pst p s t
4 est e s t
5 pt p t
6 et e t
7 gmt g m t
@@ -0,0 +1,24 @@
satu 1
dua 2
tiga 3
empat 4
lima 5
enam 6
tujuh 7
delapan 8
sembilan 9
sepuluh 10
sebelas 11
dua belas 12
satu 13
dua 14
tiga 15
empat 16
lima 17
enam 18
tujuh 19
delapan 20
sembilan 21
sepuluh 22
sebelas 23
dua belas 24
1 satu 1
2 dua 2
3 tiga 3
4 empat 4
5 lima 5
6 enam 6
7 tujuh 7
8 delapan 8
9 sembilan 9
10 sepuluh 10
11 sebelas 11
12 dua belas 12
13 satu 13
14 dua 14
15 tiga 15
16 empat 16
17 lima 17
18 enam 18
19 tujuh 19
20 delapan 20
21 sembilan 21
22 sepuluh 22
23 sebelas 23
24 dua belas 24
@@ -0,0 +1,12 @@
e.g. misalnya
dr. dokter
mr. tuan
mrs. rindu
st. santo
7-eleven tujuhsebelas
es3 e s tiga
s&p s dan p
ASAP a s a p
AT&T a t dan t
LLP l l p
ATM a t m
1 e.g. misalnya
2 dr. dokter
3 mr. tuan
4 mrs. rindu
5 st. santo
6 7-eleven tujuhsebelas
7 es3 e s tiga
8 s&p s dan p
9 ASAP a s a p
10 AT&T a t dan t
11 LLP l l p
12 ATM a t m
@@ -0,0 +1,209 @@
import os
import string
from pathlib import Path
from typing import Dict
import pynini
from fun_text_processing.inverse_text_normalization.id.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
DAMO_CHAR = utf8.VALID_UTF8_CHAR
DAMO_DIGIT = byte.DIGIT
DAMO_LOWER = pynini.union(*string.ascii_lowercase).optimize()
DAMO_UPPER = pynini.union(*string.ascii_uppercase).optimize()
DAMO_ALPHA = pynini.union(DAMO_LOWER, DAMO_UPPER).optimize()
DAMO_ALNUM = pynini.union(DAMO_DIGIT, DAMO_ALPHA).optimize()
DAMO_HEX = pynini.union(*string.hexdigits).optimize()
DAMO_NON_BREAKING_SPACE = "\u00A0"
DAMO_SPACE = " "
DAMO_WHITE_SPACE = pynini.union(" ", "\t", "\n", "\r", "\u00A0").optimize()
DAMO_NOT_SPACE = pynini.difference(DAMO_CHAR, DAMO_WHITE_SPACE).optimize()
DAMO_NOT_QUOTE = pynini.difference(DAMO_CHAR, r'"').optimize()
DAMO_PUNCT = pynini.union(*map(pynini.escape, string.punctuation)).optimize()
DAMO_GRAPH = pynini.union(DAMO_ALNUM, DAMO_PUNCT).optimize()
DAMO_SIGMA = pynini.closure(DAMO_CHAR)
delete_space = pynutil.delete(pynini.closure(DAMO_WHITE_SPACE))
delete_zero_or_one_space = pynutil.delete(pynini.closure(DAMO_WHITE_SPACE, 0, 1))
insert_space = pynutil.insert(" ")
delete_extra_space = pynini.cross(pynini.closure(DAMO_WHITE_SPACE, 1), " ")
delete_preserve_order = pynini.closure(
pynutil.delete(" preserve_order: true")
| (pynutil.delete(' field_order: "') + DAMO_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 = DAMO_SIGMA + _c + pynini.cross("y", "ies")
_es = DAMO_SIGMA + pynini.union("s", "sh", "ch", "x", "z") + pynutil.insert("es")
_s = DAMO_SIGMA + pynutil.insert("s")
graph_plural = plurals._priority_union(
suppletive,
plurals._priority_union(_ies, plurals._priority_union(_es, _s, DAMO_SIGMA), DAMO_SIGMA),
DAMO_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(DAMO_SPACE, DAMO_NON_BREAKING_SPACE), "", "", DAMO_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", " "), "", "", DAMO_SIGMA)
@@ -0,0 +1,29 @@
2022
300
9999
100001
100
1000
10289
1289
01 2345-6789
14
15
16
17
18
19
20
106
600
100
100
1 miliar
123
123
24 maret
10076
100076
10076 rupiah
76
+62 21 6539-0605
@@ -0,0 +1,29 @@
dua ribu dua puluh dua
tiga ribu
sembilan ribu sembilan ratus sembilan puluh sembilan
seribu satu
ribu
seribu
seribu dua ratus delapan puluh sembilan
ribu dua ratus delapan puluh sembilan
nol satu dua tiga empat lima enam tujuh delapan sembilan
empat belas
limabelas
enambelas
tujuh belas
delapan belas
sembilan belas
dua puluh
seratus enam
enam ratus
ratus
seratus
satu miliar
seratus dua puluh tiga
ratus dua puluh tiga
dua puluh empat maret
ribu tujuh puluh enam
seribu tujuh puluh enam
ribu tujuh puluh enam rupiah
tujuh puluh enam
ditambah enam dua dua satu enam lima tiga sembilan nol enam nol lima
@@ -0,0 +1,29 @@
dua ribu dua puluh dua 2022
tiga ribu 3000
sembilan ribu sembilan ratus sembilan puluh sembilan 9999
seribu satu 1001
ribu 1000
seribu 1000
seribu dua ratus delapan puluh sembilan 1289
ribu dua ratus delapan puluh sembilan 1289
nol satu dua tiga empat lima enam tujuh delapan sembilan 01 2345-6789
empat belas 14
limabelas 15
enambelas 16
tujuh belas 17
delapan belas 18
sembilan belas 19
dua puluh 20
seratus enam 106
enam ratus 600
ratus 100
seratus 100
satu miliar 1 miliar
seratus dua puluh tiga 123
ratus dua puluh tiga 123
dua puluh empat maret 24 maret
ribu tujuh puluh enam 1076
seribu tujuh puluh enam 1076
ribu tujuh puluh enam rupiah 1076 rupiah
tujuh puluh enam 76
ditambah enam dua dua satu enam lima tiga sembilan nol enam nol lima +62 21 6539-0605
1 dua ribu dua puluh dua 2022
2 tiga ribu 3000
3 sembilan ribu sembilan ratus sembilan puluh sembilan 9999
4 seribu satu 1001
5 ribu 1000
6 seribu 1000
7 seribu dua ratus delapan puluh sembilan 1289
8 ribu dua ratus delapan puluh sembilan 1289
9 nol satu dua tiga empat lima enam tujuh delapan sembilan 01 2345-6789
10 empat belas 14
11 limabelas 15
12 enambelas 16
13 tujuh belas 17
14 delapan belas 18
15 sembilan belas 19
16 dua puluh 20
17 seratus enam 106
18 enam ratus 600
19 ratus 100
20 seratus 100
21 satu miliar 1 miliar
22 seratus dua puluh tiga 123
23 ratus dua puluh tiga 123
24 dua puluh empat maret 24 maret
25 ribu tujuh puluh enam 1076
26 seribu tujuh puluh enam 1076
27 ribu tujuh puluh enam rupiah 1076 rupiah
28 tujuh puluh enam 76
29 ditambah enam dua dua satu enam lima tiga sembilan nol enam nol lima +62 21 6539-0605
@@ -0,0 +1,161 @@
import pynini
from fun_text_processing.inverse_text_normalization.id.utils import get_abs_path, num_to_word
from fun_text_processing.inverse_text_normalization.id.graph_utils import (
DAMO_ALPHA,
DAMO_DIGIT,
DAMO_SIGMA,
DAMO_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, deterministic: bool = True):
super().__init__(name="cardinal", kind="classify", deterministic=deterministic)
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_hundreds = pynini.string_file(get_abs_path("data/numbers/hundreds.tsv"))
graph_thousand = pynini.string_file(get_abs_path("data/numbers/thousand.tsv"))
graph_hundred = pynini.cross("ratus", "") | pynini.cross("seratus", "")
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_one_hundred_component = pynini.union(
pynini.cross("ratus", "1") | pynini.cross("seratus", "1")
)
graph_one_hundred_component += delete_space
graph_one_hundred_component += pynini.union(
graph_teen | pynutil.insert("00"),
(graph_ties | pynutil.insert("0")) + delete_space + (graph_digit | pynutil.insert("0")),
)
graph_hundred_component = graph_hundred_component | graph_one_hundred_component
graph_hundred_component_at_least_one_none_zero_digit = graph_hundred_component @ (
pynini.closure(DAMO_DIGIT) + (DAMO_DIGIT - "0") + pynini.closure(DAMO_DIGIT)
)
self.graph_hundred_component_at_least_one_none_zero_digit = (
graph_hundred_component_at_least_one_none_zero_digit
)
graph_thousand = pynini.cross("ribu", "") | pynini.cross("seribu", "")
graph_one_thousand_component = pynini.union(
pynini.cross("ribu", "1") | pynini.cross("seribu", "1")
)
graph_thousands = pynini.union(
graph_hundred_component_at_least_one_none_zero_digit
+ delete_space
+ (pynutil.delete("ribu") | pynutil.delete("seribu")),
pynutil.insert("000", weight=0.1),
)
graph_thousands = graph_thousands | (pynutil.insert("00") + graph_one_thousand_component)
graph_million = pynini.union(
graph_hundred_component_at_least_one_none_zero_digit
+ delete_space
+ (pynutil.delete("juta") | pynutil.delete("sejuta")),
pynutil.insert("000", weight=0.1),
)
graph_billion = pynini.union(
graph_hundred_component_at_least_one_none_zero_digit
+ delete_space
+ (
pynutil.delete("miliar")
| pynutil.delete("semiliar")
| pynutil.delete("milyar")
| pynutil.delete("semilyar")
),
pynutil.insert("000", weight=0.1),
)
graph_trillion = pynini.union(
graph_hundred_component_at_least_one_none_zero_digit
+ delete_space
+ (pynutil.delete("triliun") | pynutil.delete("setriliun")),
pynutil.insert("000", weight=0.1),
)
graph_quadrillion = pynini.union(
graph_hundred_component_at_least_one_none_zero_digit
+ delete_space
+ pynutil.delete("milion lipat empat"),
pynutil.insert("000", weight=0.1),
)
graph_quintillion = pynini.union(
graph_hundred_component_at_least_one_none_zero_digit
+ delete_space
+ pynutil.delete("triliun"),
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_hundred_component,
# graph_digit,
graph_zero,
)
graph = graph @ pynini.union(
pynutil.delete(pynini.closure("0"))
+ pynini.difference(DAMO_DIGIT, "0")
+ pynini.closure(DAMO_DIGIT),
"0",
)
labels_exception = ["nol"]
graph_exception = pynini.union(*labels_exception)
graph = (
pynini.cdrewrite(pynutil.delete("dan"), DAMO_SPACE, DAMO_SPACE, DAMO_SIGMA)
@ (DAMO_ALPHA + DAMO_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("kurang", '"-"') + DAMO_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()
@@ -0,0 +1,150 @@
import pynini
from fun_text_processing.inverse_text_normalization.id.utils import get_abs_path
from fun_text_processing.inverse_text_normalization.id.graph_utils import (
DAMO_ALPHA,
DAMO_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("ratusan", "00s")
graph |= pynini.cross("dua", "2") + delete_space + pynini.cross("ribuan", "000s")
graph |= (
(graph_ties | graph_teen)
+ delete_space
+ (pynini.closure(DAMO_ALPHA, 1) + (pynini.cross("ies", "y") | pynutil.delete("s")))
@ (graph_ties | pynini.cross("sepuluh", "10"))
+ pynutil.insert("s")
)
graph @= pynini.union("1", "2") + DAMO_DIGIT + DAMO_DIGIT + DAMO_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("ratus")
) | pynutil.insert("0")
graph = (
graph_digit
+ delete_space
+ pynutil.delete("ribu")
+ delete_space
+ graph_hundred_component
+ delete_space
+ (graph_teen | graph_ties)
)
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()
@@ -0,0 +1,100 @@
import pynini
from fun_text_processing.inverse_text_normalization.id.utils import get_abs_path
from fun_text_processing.inverse_text_normalization.id.graph_utils import (
DAMO_DIGIT,
GraphFst,
delete_extra_space,
delete_space,
)
from pynini.lib import pynutil
import pdb
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(DAMO_DIGIT, "0")
+ pynini.closure(DAMO_DIGIT)
)
suffix = pynini.union("juta", "miliar", "triliun", "kuadriliun", "triliun", "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 | "ribu")
+ 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") # titik
optional_graph_negative = pynini.closure(
pynutil.insert("negative: ") + pynini.cross("kurang", '"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()
@@ -0,0 +1,100 @@
import pynini
from fun_text_processing.inverse_text_normalization.id.utils import get_abs_path
from fun_text_processing.inverse_text_normalization.id.graph_utils import (
DAMO_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 = (
DAMO_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(DAMO_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(DAMO_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()
@@ -0,0 +1,11 @@
from fun_text_processing.inverse_text_normalization.id.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
@@ -0,0 +1,97 @@
import pynini
from fun_text_processing.inverse_text_normalization.id.utils import get_abs_path
from fun_text_processing.inverse_text_normalization.id.graph_utils import (
DAMO_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("kurang", '"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: "')
+ ((DAMO_SIGMA - "satu") @ cardinal_graph)
+ pynutil.insert('"')
+ pynutil.insert(" }")
+ delete_extra_space
+ unit_plural
)
subgraph_cardinal |= (
pynutil.insert("cardinal { ")
+ optional_graph_negative
+ pynutil.insert('integer: "')
+ pynini.cross("satu", "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()
@@ -0,0 +1,110 @@
import pynini
from fun_text_processing.inverse_text_normalization.id.utils import get_abs_path
from fun_text_processing.inverse_text_normalization.id.graph_utils import (
DAMO_DIGIT,
DAMO_NOT_SPACE,
DAMO_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(DAMO_NOT_SPACE)
+ pynini.accep(" ")
+ pynutil.insert("ratus ")
+ DAMO_SIGMA,
pynini.compose(cardinal_graph, DAMO_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 = (DAMO_DIGIT + DAMO_DIGIT) | (
pynutil.insert("0") + DAMO_DIGIT
)
# twelve dollars (and) fifty cents, zero cents
cents_standalone = (
pynutil.insert('fractional_part: "')
+ pynini.union(
pynutil.add_weight(((DAMO_SIGMA - "satu") @ cardinal_graph), -0.7)
@ add_leading_zero_to_double_digit
+ delete_space
+ pynutil.delete("sen"),
pynini.cross("satu", "01") + delete_space + pynutil.delete("sen"),
)
+ pynutil.insert('"')
)
optional_cents_standalone = pynini.closure(
delete_space
+ pynini.closure(pynutil.delete("dan") + 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: "')
+ ((DAMO_SIGMA - "satu") @ cardinal_graph)
+ pynutil.insert('"')
+ delete_extra_space
+ graph_unit_plural
+ (optional_cents_standalone | optional_cents_suffix)
)
graph_integer |= (
pynutil.insert('integer_part: "')
+ pynini.cross("satu", "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()
@@ -0,0 +1,29 @@
import pynini
from fun_text_processing.inverse_text_normalization.id.utils import get_abs_path
from fun_text_processing.inverse_text_normalization.id.graph_utils import DAMO_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(DAMO_CHAR) + pynini.union(
graph_digit, graph_teens, pynini.cross("tieth", "ty"), pynini.cross("th", "")
) # TODO
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()
@@ -0,0 +1,20 @@
import pynini
from fun_text_processing.inverse_text_normalization.id.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()
@@ -0,0 +1,149 @@
import pynini
from fun_text_processing.inverse_text_normalization.id.utils import get_abs_path
from fun_text_processing.inverse_text_normalization.id.graph_utils import (
DAMO_ALNUM,
DAMO_ALPHA,
DAMO_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, DAMO_DIGIT)
character = digit | DAMO_ALPHA
sequence = character + pynini.closure(pynutil.delete(" ") + character, 2)
sequence = sequence @ (pynini.closure(DAMO_ALNUM) + DAMO_DIGIT + pynini.closure(DAMO_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", "nol")).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("dobel ") + 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, DAMO_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,
DAMO_DIGIT**2
+ pynutil.insert(" ")
+ DAMO_DIGIT**4
+ pynutil.insert("-")
+ DAMO_DIGIT**4,
).optimize()
number_part = (
pynutil.insert('number_part: "') + number_part.optimize() + pynutil.insert('"')
)
cardinal_option = pynini.compose(single_or_double_digit, DAMO_DIGIT ** (2, 3))
country_code = (
pynutil.insert('country_code: "')
+ pynini.closure(pynini.cross("ditambah ", "+"), 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 + DAMO_DIGIT**4
credit_card_graph = pynini.compose(
single_or_double_digit, DAMO_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,
DAMO_DIGIT**3
+ pynutil.insert("-")
+ DAMO_DIGIT**2
+ pynutil.insert("-")
+ DAMO_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()
@@ -0,0 +1,151 @@
import pynini
from fun_text_processing.inverse_text_normalization.id.taggers.cardinal import CardinalFst
from fun_text_processing.inverse_text_normalization.id.utils import get_abs_path, num_to_word
from fun_text_processing.inverse_text_normalization.id.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"))
cardinal = pynutil.add_weight(CardinalFst().graph_no_exception, weight=-0.7)
labels_hour = [
"nol",
"satu",
"dua",
"tiga",
"empat",
"lima",
"enam",
"tujuh",
"delapan",
"sembilan",
"sepuluh",
"sebelas",
"duabelas",
"tigabelas",
]
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("setengah", "30") | pynini.cross("seperempat", "15")
oclock = pynini.cross(pynini.union("jam", "pukul"), "")
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("setengah")
+ delete_extra_space
+ final_graph_hour
)
graph_quarter_time = (
pynutil.insert('minutes: "')
+ pynini.cross("seperempat", "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", "menit")), 0, 1
)
+ delete_space
+ pynutil.delete(pynini.union("sampai"))
+ 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()
@@ -0,0 +1,102 @@
import os
import pynini
from fun_text_processing.inverse_text_normalization.id.taggers.cardinal import CardinalFst
from fun_text_processing.inverse_text_normalization.id.taggers.date import DateFst
from fun_text_processing.inverse_text_normalization.id.taggers.decimal import DecimalFst
from fun_text_processing.inverse_text_normalization.id.taggers.electronic import ElectronicFst
from fun_text_processing.inverse_text_normalization.id.taggers.measure import MeasureFst
from fun_text_processing.inverse_text_normalization.id.taggers.money import MoneyFst
from fun_text_processing.inverse_text_normalization.id.taggers.ordinal import OrdinalFst
from fun_text_processing.inverse_text_normalization.id.taggers.punctuation import PunctuationFst
from fun_text_processing.inverse_text_normalization.id.taggers.telephone import TelephoneFst
from fun_text_processing.inverse_text_normalization.id.taggers.time import TimeFst
from fun_text_processing.inverse_text_normalization.id.taggers.whitelist import WhiteListFst
from fun_text_processing.inverse_text_normalization.id.taggers.word import WordFst
from fun_text_processing.inverse_text_normalization.id.graph_utils import (
GraphFst,
delete_extra_space,
delete_space,
generator_main,
)
from pynini.lib import pynutil
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, "_id_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}.")
@@ -0,0 +1,19 @@
import pynini
from fun_text_processing.inverse_text_normalization.id.utils import get_abs_path
from fun_text_processing.inverse_text_normalization.id.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()
@@ -0,0 +1,15 @@
import pynini
from fun_text_processing.inverse_text_normalization.id.graph_utils import DAMO_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(DAMO_NOT_SPACE, 1) + pynutil.insert('"')
self.fst = word.optimize()
@@ -0,0 +1,40 @@
dua ribu dua puluh dua 2022
nol satu dua tiga empat lima enam tujuh delapan sembilan 0123456789
empat belas 14
limabelas 15
enambelas 16
tujuh belas 17
delapan belas 18
sembilan belas 19
dua puluh 20
seratus enam 106
enam ratus 600
ratus 100
seratus 100
satu juta 1,000,000
satu miliar 1 miliar
seratus dua puluh tiga 123
ratus dua puluh tiga 123
dua puluh empat maret 24th March
seribu dua ratus delapan puluh sembilan 1289
lima juta tiga ribu tujuh puluh enam rupiah Rp5003076
ribu tujuh puluh enam rupiah Rp1076
tujuh puluh enam rupiah dollar $1076
ditambah enam dua dua satu enam lima tiga sembilan nol enam nol lima +62 21 6539-0605
tiga ribu 3000
sembilan ribu sembilan ratus sembilan puluh sembilan 9999
seribu satu 1001
nol 0
satu 1
dua 2
tiga 3
empat 4
lima 5
enam 6
tujuh 7
delapan 8
sembilan 9
sepuluh 10
sebelas 11
dua belas 12
tigabelas 13
1 dua ribu dua puluh dua 2022
2 nol satu dua tiga empat lima enam tujuh delapan sembilan 0123456789
3 empat belas 14
4 limabelas 15
5 enambelas 16
6 tujuh belas 17
7 delapan belas 18
8 sembilan belas 19
9 dua puluh 20
10 seratus enam 106
11 enam ratus 600
12 ratus 100
13 seratus 100
14 satu juta 1,000,000
15 satu miliar 1 miliar
16 seratus dua puluh tiga 123
17 ratus dua puluh tiga 123
18 dua puluh empat maret 24th March
19 seribu dua ratus delapan puluh sembilan 1289
20 lima juta tiga ribu tujuh puluh enam rupiah Rp5003076
21 ribu tujuh puluh enam rupiah Rp1076
22 tujuh puluh enam rupiah dollar $1076
23 ditambah enam dua dua satu enam lima tiga sembilan nol enam nol lima +62 21 6539-0605
24 tiga ribu 3000
25 sembilan ribu sembilan ratus sembilan puluh sembilan 9999
26 seribu satu 1001
27 nol 0
28 satu 1
29 dua 2
30 tiga 3
31 empat 4
32 lima 5
33 enam 6
34 tujuh 7
35 delapan 8
36 sembilan 9
37 sepuluh 10
38 sebelas 11
39 dua belas 12
40 tigabelas 13
@@ -0,0 +1,33 @@
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
@@ -0,0 +1,38 @@
import pynini
from fun_text_processing.inverse_text_normalization.id.graph_utils import (
DAMO_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('"')
+ DAMO_NOT_QUOTE
+ pynutil.delete('"')
+ delete_space,
0,
1,
)
graph = (
pynutil.delete("integer:")
+ delete_space
+ pynutil.delete('"')
+ pynini.closure(DAMO_NOT_QUOTE, 1)
+ pynutil.delete('"')
)
self.numbers = graph
graph = optional_sign + graph
delete_tokens = self.delete_tokens(graph)
self.fst = delete_tokens.optimize()
@@ -0,0 +1,70 @@
import pynini
from fun_text_processing.inverse_text_normalization.id.graph_utils import (
DAMO_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(DAMO_NOT_QUOTE, 1)
+ pynutil.delete('"')
)
day = (
pynutil.delete("day:")
+ delete_space
+ pynutil.delete('"')
+ pynini.closure(DAMO_NOT_QUOTE, 1)
+ pynutil.delete('"')
)
year = (
pynutil.delete("year:")
+ delete_space
+ pynutil.delete('"')
+ pynini.closure(DAMO_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('"')
+ DAMO_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()
@@ -0,0 +1,48 @@
import pynini
from fun_text_processing.inverse_text_normalization.id.graph_utils import (
DAMO_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(DAMO_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(DAMO_NOT_QUOTE, 1)
+ pynutil.delete('"')
)
optional_fractional = pynini.closure(fractional + delete_space, 0, 1)
quantity = (
pynutil.delete("quantity:")
+ delete_space
+ pynutil.delete('"')
+ pynini.closure(DAMO_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()
@@ -0,0 +1,45 @@
import pynini
from fun_text_processing.inverse_text_normalization.id.graph_utils import (
DAMO_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(DAMO_NOT_QUOTE, 1)
+ pynutil.delete('"')
)
domain = (
pynutil.delete("domain:")
+ delete_space
+ pynutil.delete('"')
+ pynini.closure(DAMO_NOT_QUOTE, 1)
+ pynutil.delete('"')
)
protocol = (
pynutil.delete("protocol:")
+ delete_space
+ pynutil.delete('"')
+ pynini.closure(DAMO_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()
@@ -0,0 +1,10 @@
from fun_text_processing.inverse_text_normalization.id.graph_utils import GraphFst
class FractionFst(GraphFst):
"""
Finite state transducer for verbalizing fraction,
"""
def __init__(self):
super().__init__(name="fraction", kind="verbalize")
@@ -0,0 +1,51 @@
import pynini
from fun_text_processing.inverse_text_normalization.id.graph_utils import (
DAMO_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(DAMO_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()
@@ -0,0 +1,30 @@
import pynini
from fun_text_processing.inverse_text_normalization.id.graph_utils import (
DAMO_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(DAMO_CHAR - " ", 1)
+ pynutil.delete('"')
)
graph = unit + delete_space + decimal.numbers
delete_tokens = self.delete_tokens(graph)
self.fst = delete_tokens.optimize()
@@ -0,0 +1,48 @@
import pynini
from fun_text_processing.inverse_text_normalization.id.graph_utils import (
DAMO_NOT_QUOTE,
DAMO_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(DAMO_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]",
DAMO_SIGMA,
)
graph = graph @ suffix
delete_tokens = self.delete_tokens(graph)
self.fst = delete_tokens.optimize()
@@ -0,0 +1,30 @@
import pynini
from fun_text_processing.inverse_text_normalization.id.graph_utils import DAMO_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(DAMO_NOT_QUOTE, 1)
+ pynutil.delete('"')
)
optional_country_code = pynini.closure(
pynutil.delete('country_code: "')
+ pynini.closure(DAMO_NOT_QUOTE, 1)
+ pynutil.delete('"')
+ pynini.accep(" "),
0,
1,
)
delete_tokens = self.delete_tokens(optional_country_code + number_part)
self.fst = delete_tokens.optimize()
@@ -0,0 +1,68 @@
import pynini
from fun_text_processing.inverse_text_normalization.id.graph_utils import (
DAMO_CHAR,
DAMO_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 = (DAMO_DIGIT + DAMO_DIGIT) | (
pynutil.insert("0") + DAMO_DIGIT
)
hour = (
pynutil.delete("hours:")
+ delete_space
+ pynutil.delete('"')
+ pynini.closure(DAMO_DIGIT, 1)
+ pynutil.delete('"')
)
minute = (
pynutil.delete("minutes:")
+ delete_space
+ pynutil.delete('"')
+ pynini.closure(DAMO_DIGIT, 1)
+ pynutil.delete('"')
)
suffix = (
delete_space
+ insert_space
+ pynutil.delete("suffix:")
+ delete_space
+ pynutil.delete('"')
+ pynini.closure(DAMO_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(DAMO_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()
@@ -0,0 +1,47 @@
from fun_text_processing.inverse_text_normalization.id.verbalizers.cardinal import CardinalFst
from fun_text_processing.inverse_text_normalization.id.verbalizers.date import DateFst
from fun_text_processing.inverse_text_normalization.id.verbalizers.decimal import DecimalFst
from fun_text_processing.inverse_text_normalization.id.verbalizers.electronic import ElectronicFst
from fun_text_processing.inverse_text_normalization.id.verbalizers.measure import MeasureFst
from fun_text_processing.inverse_text_normalization.id.verbalizers.money import MoneyFst
from fun_text_processing.inverse_text_normalization.id.verbalizers.ordinal import OrdinalFst
from fun_text_processing.inverse_text_normalization.id.verbalizers.telephone import TelephoneFst
from fun_text_processing.inverse_text_normalization.id.verbalizers.time import TimeFst
from fun_text_processing.inverse_text_normalization.id.verbalizers.whitelist import WhiteListFst
from fun_text_processing.inverse_text_normalization.id.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
@@ -0,0 +1,33 @@
import pynini
from fun_text_processing.inverse_text_normalization.id.verbalizers.verbalize import VerbalizeFst
from fun_text_processing.inverse_text_normalization.id.verbalizers.word import WordFst
from fun_text_processing.inverse_text_normalization.id.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
@@ -0,0 +1,27 @@
import pynini
from fun_text_processing.inverse_text_normalization.id.graph_utils import (
DAMO_CHAR,
DAMO_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(DAMO_CHAR - " ", 1)
+ pynutil.delete('"')
)
graph = graph @ pynini.cdrewrite(pynini.cross("\u00A0", " "), "", "", DAMO_SIGMA)
self.fst = graph.optimize()
@@ -0,0 +1,29 @@
import pynini
from fun_text_processing.inverse_text_normalization.id.graph_utils import (
DAMO_CHAR,
DAMO_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(DAMO_CHAR - " ", 1)
char = (
pynutil.delete("name:")
+ delete_space
+ pynutil.delete('"')
+ chars
+ pynutil.delete('"')
)
graph = char @ pynini.cdrewrite(pynini.cross("\u00A0", " "), "", "", DAMO_SIGMA)
self.fst = graph.optimize()