Skip to content

Commit d84fcec

Browse files
committed
feat: give FSTs the option to have a descriptive string name
1 parent 1184516 commit d84fcec

File tree

1 file changed

+33
-7
lines changed

1 file changed

+33
-7
lines changed

utility/fst_object.py

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,16 +55,18 @@ class FST:
5555
"""A class representing finite state transducers.
5656
5757
Attributes:
58-
Q (list): a list of states;
59-
Sigma (list): a list of symbols of the input alphabet;
60-
Gamma (list): a list of symbols of the output alphabet;
61-
qe (str): name of the unique initial state;
62-
E (list): a list of transitions;
58+
name (str): a descriptive name for this FST.
59+
Q (list): a list of states.
60+
Sigma (list): a list of symbols of the input alphabet.
61+
Gamma (list): a list of symbols of the output alphabet.
62+
qe (str): name of the unique initial state.
63+
E (list): a list of transitions.
6364
stout (dict): a collection of state outputs.
6465
"""
6566

6667
def __init__(self, Sigma=None, Gamma=None):
6768
"""Initializes the FST object."""
69+
self.name = "unnamed-FST"
6870
self.Q = None
6971
self.Sigma = Sigma
7072
self.Gamma = Gamma
@@ -73,7 +75,11 @@ def __init__(self, Sigma=None, Gamma=None):
7375
self.stout = None
7476

7577
def __str__(self):
78+
return self.name
79+
80+
def __repr__(self):
7681
return f'''FST(
82+
name='{self.name}',
7783
(Sigma, Gamma)=({self.Sigma}, {self.Gamma}),
7884
Q={self.Q},
7985
qe='{self.qe}',
@@ -183,13 +189,13 @@ def copy_fst(self):
183189
T (FST): a copy of the current FST.
184190
"""
185191
T = FST()
192+
T.name = deepcopy(self.name)
186193
T.Q = deepcopy(self.Q)
187194
T.Sigma = deepcopy(self.Sigma)
188195
T.Gamma = deepcopy(self.Gamma)
189196
T.qe = deepcopy(self.qe)
190197
T.E = deepcopy(self.E)
191198
T.stout = deepcopy(self.stout)
192-
193199
return T
194200

195201
def fresh_state(self, name_prefix):
@@ -260,6 +266,7 @@ def new_rejector(Sigma, Gamma):
260266
"""
261267
# initialize the new FST
262268
F = FST(Sigma, Gamma)
269+
F.name = "rejector-FST"
263270
F.Q, F.E, F.qe, F.stout = ["q"], [], "q", {}
264271

265272
return F
@@ -281,6 +288,7 @@ def new_acceptor(Sigma, Gamma):
281288
"""
282289
# initialize the new FST
283290
F = FST(Sigma, Gamma)
291+
F.name = "acceptor-FST"
284292
F.Q, F.E, F.qe, F.stout = ["q"], [], "q", {"q" : ""}
285293

286294
# add transitions that allow writing any character to input or output
@@ -309,6 +317,7 @@ def trim_inaccessible(F):
309317
"""
310318
# initialize the new FST
311319
G = FST(deepcopy(F.Sigma), deepcopy(F.Gamma))
320+
G.name = f"trim_inaccessible({F})"
312321
Q_set, E_set, G.qe, G.stout = set(), set(), F.qe, {}
313322

314323
# perform a breadth-first traversal of the original FST from the initial state
@@ -350,6 +359,7 @@ def trim_useless(F):
350359
"""
351360
# initialize the new FST
352361
G = FST(deepcopy(F.Sigma), deepcopy(F.Gamma))
362+
G.name = f"trim_useless({F})"
353363
Q_set, E_set, G.qe, G.stout = set(), set(), F.qe, {}
354364

355365
# perform a breadth-first traversal of the original FST from the accepting states
@@ -394,7 +404,10 @@ def trim(F):
394404
Returns:
395405
FST: the trimmed FST.
396406
"""
397-
return trim_useless(trim_inaccessible(F))
407+
G = trim_useless(trim_inaccessible(F))
408+
G.name = f"trim({F})"
409+
410+
return G
398411

399412
def is_empty(F):
400413
"""Checks that the FST accepts no string pairs.
@@ -427,6 +440,7 @@ def expand_inputs(F):
427440
"""
428441
# initialize the new FST
429442
G = FST(deepcopy(F.Sigma), deepcopy(F.Gamma))
443+
G.name = f"expand_inputs({F})"
430444
Q_set, E_set, G.qe, G.stout = set(), set(), FST.encode_state(F.qe, ""), {}
431445

432446
# Construct the new set of states and transitions.
@@ -470,6 +484,7 @@ def expand_final(F):
470484
"""
471485
# initialize the new FST
472486
G = FST(deepcopy(F.Sigma), deepcopy(F.Gamma))
487+
G.name = f"expand_final({F})"
473488
G.Q, G.E, G.qe, G.stout = deepcopy(F.Q), deepcopy(F.E), F.qe, {}
474489

475490
# account for nonempty final outputs by turning them into transitions to new accepting states,
@@ -501,10 +516,12 @@ def invert(F):
501516
FST: the inverted FST.
502517
"""
503518
# we need final outputs to be empty because the FST class does not support final inputs
519+
F_name = F.name
504520
F = expand_final(F)
505521

506522
# initialize the new FST
507523
G = FST(deepcopy(F.Sigma), deepcopy(F.Gamma))
524+
G.name = f"{F_name}⁻¹"
508525
G.Q, G.E, G.qe, G.stout = deepcopy(F.Q), [], F.qe, deepcopy(F.stout)
509526

510527
# copy over the transitions with swapped input an output strings
@@ -533,10 +550,12 @@ def concatenate(F, G):
533550
"""
534551
# we need final outputs of the first machine to be empty
535552
# so that we do not miss output upon traversal to the next machine
553+
F_name = F.name
536554
F = expand_final(F)
537555

538556
# initialize the new FST
539557
H = FST(list(set(F.Sigma) | set(G.Sigma)), list(set(F.Gamma) | set(G.Gamma)))
558+
H.name = f"({F_name} · {G})"
540559
H.Q, H.E, H.qe, H.stout = [], [], FST.encode_state("LEFT", F.qe), {}
541560

542561
# copy over the states from both `F` and `G`
@@ -583,10 +602,12 @@ def kleene_closure(F):
583602
"""
584603
# we need final outputs to be empty
585604
# so that we do not miss output upon traversal back to the initial state
605+
F_name = F.name
586606
F = expand_final(F)
587607

588608
# initialize the new FST
589609
G = FST(deepcopy(F.Sigma), deepcopy(F.Gamma))
610+
G.name = f"{F_name}*"
590611
G.Q, G.E, G.qe, G.stout = deepcopy(F.Q), deepcopy(F.E), F.qe, deepcopy(F.stout)
591612

592613
# for every final state,
@@ -616,6 +637,7 @@ def union(F, G):
616637
"""
617638
# initialize the new FST
618639
H = FST(list(set(F.Sigma) | set(G.Sigma)), list(set(F.Gamma) | set(G.Gamma)))
640+
H.name = f"({F}{G})"
619641
H.Q, H.E, H.qe, H.stout = [], [], FST.encode_state("LEFT", F.qe), {}
620642

621643
# create an epsilon transition to nondeterministically choose between running `F` and `G`
@@ -662,10 +684,12 @@ def intersect(F, G):
662684
# expanding final outputs beforehand makes this construction far easier,
663685
# but it also means that determinism is not an invariant,
664686
# whereas it would be in a more robust implementation
687+
F_name, G_name = F.name, G.name
665688
F, G = expand_final(F), expand_final(G)
666689

667690
# initialize the new FST
668691
H = FST(list(set(F.Sigma) | set(G.Sigma)), list(set(F.Gamma) | set(G.Gamma)))
692+
H.name = f"({F_name}{G_name})"
669693
Q_set, E_set, H.stout = set(), set(), {}
670694
H.qe = FST.encode_state(F.qe, G.qe, ("", ""), ("", ""))
671695

@@ -733,10 +757,12 @@ def compose(F, G):
733757
# expanding final outputs beforehand makes this construction far easier,
734758
# but it also means that determinism is not an invariant,
735759
# whereas it would be in a more robust implementation
760+
F_name, G_name = F.name, G.name
736761
F, G = expand_final(F), expand_final(G)
737762

738763
# initialize the new FST
739764
H = FST(deepcopy(G.Sigma), deepcopy(F.Gamma))
765+
H.name = f"({F_name}{G_name})"
740766
Q_set, E_set, H.stout = set(), set(), {}
741767
H.qe = FST.encode_state(F.qe, G.qe, "")
742768

0 commit comments

Comments
 (0)