From 914e1671203f568acb0f076b668a2f424d31db7d Mon Sep 17 00:00:00 2001 From: David Huggins-Daines Date: Fri, 16 Aug 2024 11:43:03 -0400 Subject: [PATCH] fix(docs): Add Python documentation for algorithm methods (#278) * fix(docs): Add documentation for algorithm methods * fix(docs): black * fix: silence warnings related to typechecking * fix: correct types and listen to mypy * fix: correct corresponding type annotation --- rustfst-python/mkdocs.yml | 2 +- rustfst-python/rustfst/algorithms/compose.py | 11 + rustfst-python/rustfst/algorithms/connect.py | 6 +- rustfst-python/rustfst/algorithms/minimize.py | 24 +- rustfst-python/rustfst/algorithms/optimize.py | 4 +- rustfst-python/rustfst/algorithms/replace.py | 4 +- rustfst-python/rustfst/algorithms/reverse.py | 15 +- .../rustfst/algorithms/rm_epsilon.py | 11 +- .../rustfst/algorithms/shortest_path.py | 34 ++- rustfst-python/rustfst/fst/vector_fst.py | 214 ++++++++++++++++-- 10 files changed, 269 insertions(+), 56 deletions(-) diff --git a/rustfst-python/mkdocs.yml b/rustfst-python/mkdocs.yml index 4c732898f..0efc059b8 100644 --- a/rustfst-python/mkdocs.yml +++ b/rustfst-python/mkdocs.yml @@ -83,7 +83,7 @@ plugins: show_signature_annotations: true show_submodules: true watch: - - rustfst-python/rustfst + - rustfst - docs repo_name: rustfst repo_url: https://github.com/garvys-org/rustfst diff --git a/rustfst-python/rustfst/algorithms/compose.py b/rustfst-python/rustfst/algorithms/compose.py index ef6cd96eb..c145d5071 100644 --- a/rustfst-python/rustfst/algorithms/compose.py +++ b/rustfst-python/rustfst/algorithms/compose.py @@ -64,6 +64,17 @@ class ComposeFilter(Enum): class ComposeConfig: + """ + Configuration for compose operation. + + Args: + compose_filter: Filter which determines allowable matches during + composition operation. + connect: Connect the resulting FST after composition. + matcher1_config: Matcher configuration for left-hand FST. + matcher2_config: Matcher configuration for right-hand FST. + """ + def __init__( self, compose_filter: ComposeFilter = ComposeFilter.AUTOFILTER, diff --git a/rustfst-python/rustfst/algorithms/connect.py b/rustfst-python/rustfst/algorithms/connect.py index dfd39045f..f0e896d4a 100644 --- a/rustfst-python/rustfst/algorithms/connect.py +++ b/rustfst-python/rustfst/algorithms/connect.py @@ -9,7 +9,8 @@ def connect(fst: VectorFst) -> VectorFst: """ - This operation trims an Fst, removing states and trs that are not on successful paths. + This operation trims an Fst in-place, removing states and trs that are not on + successful paths. Examples : @@ -22,8 +23,7 @@ def connect(fst: VectorFst) -> VectorFst: ![connect_out](https://raw.githubusercontent.com/Garvys/rustfst-images-doc/master/images/connect_out.svg?sanitize=true) Returns : - self - + fst """ ret_code = lib.fst_connect(fst.ptr) diff --git a/rustfst-python/rustfst/algorithms/minimize.py b/rustfst-python/rustfst/algorithms/minimize.py index 9f411e82c..c04297d87 100644 --- a/rustfst-python/rustfst/algorithms/minimize.py +++ b/rustfst-python/rustfst/algorithms/minimize.py @@ -11,6 +11,10 @@ class MinimizeConfig: + """ + Configuration for the minimization operation. + """ + def __init__(self, delta=None, allow_nondet=False): if delta is None: delta = KSHORTESTDELTA @@ -27,10 +31,11 @@ def __init__(self, delta=None, allow_nondet=False): def minimize(fst: VectorFst) -> VectorFst: """ - minimize(fst) - Minimize a FST in place - :param fst: Fst - :return: Fst + Minimize an FST in-place + Params: + fst: Fst + Returns: + fst """ ret_code = lib.fst_minimize(fst.ptr) err_msg = "Error while minimizing FST" @@ -41,11 +46,12 @@ def minimize(fst: VectorFst) -> VectorFst: def minimize_with_config(fst: VectorFst, config: MinimizeConfig) -> VectorFst: """ - minimize(fst, config) - Minimize a FST in place - :param fst: Fst - :param config: MinimizeConfig - :return: Fst + Minimize an FST in-place + Params: + fst: Fst + config: Configuration + Returns: + fst """ ret_code = lib.fst_minimize_with_config(fst.ptr, config.ptr) err_msg = "Error while minimizing FST" diff --git a/rustfst-python/rustfst/algorithms/optimize.py b/rustfst-python/rustfst/algorithms/optimize.py index a4b633bfc..13b8bd168 100644 --- a/rustfst-python/rustfst/algorithms/optimize.py +++ b/rustfst-python/rustfst/algorithms/optimize.py @@ -12,7 +12,7 @@ def optimize(fst: VectorFst): """ - Optimize an fst. + Optimize an fst in-place Args: fst: Fst to optimize. """ @@ -23,7 +23,7 @@ def optimize(fst: VectorFst): def optimize_in_log(fst: VectorFst): """ - Optimize an fst in the log semiring. + Optimize an fst in-place in the log semiring. Args: fst: Fst to optimize. """ diff --git a/rustfst-python/rustfst/algorithms/replace.py b/rustfst-python/rustfst/algorithms/replace.py index cbc2976cc..0903a6c72 100644 --- a/rustfst-python/rustfst/algorithms/replace.py +++ b/rustfst-python/rustfst/algorithms/replace.py @@ -1,5 +1,5 @@ from __future__ import annotations -from typing import List +from typing import List, Tuple import ctypes from rustfst.ffi_utils import ( lib, @@ -17,7 +17,7 @@ class LabelFstPair(ctypes.Structure): def replace( - root_idx: int, fst_list: List[(int, VectorFst)], epsilon_on_replace: bool + root_idx: int, fst_list: List[Tuple[int, VectorFst]], epsilon_on_replace: bool ) -> VectorFst: """ Recursively replaces trs in the root FSTs with other FSTs. diff --git a/rustfst-python/rustfst/algorithms/reverse.py b/rustfst-python/rustfst/algorithms/reverse.py index 12fcbb41e..ba6ddee90 100644 --- a/rustfst-python/rustfst/algorithms/reverse.py +++ b/rustfst-python/rustfst/algorithms/reverse.py @@ -8,12 +8,17 @@ from rustfst.fst.vector_fst import VectorFst -def reverse(fst: VectorFst): +def reverse(fst: VectorFst) -> VectorFst: """ - reverse(fst) - reverse an fst - :param fst: Fst - :return: Fst + Reverse an Fst, returning a new Fst which accepts + the same language in reverse order. + + Not to be confused with `inverse`, which does something + totally different! + Args: + fst: Fst to reverse + Returns: + Newly created, reversed Fst. """ reversed_fst = ctypes.c_void_p() diff --git a/rustfst-python/rustfst/algorithms/rm_epsilon.py b/rustfst-python/rustfst/algorithms/rm_epsilon.py index 973868974..f619ab185 100644 --- a/rustfst-python/rustfst/algorithms/rm_epsilon.py +++ b/rustfst-python/rustfst/algorithms/rm_epsilon.py @@ -8,12 +8,13 @@ from rustfst.fst.vector_fst import VectorFst -def rm_epsilon(fst: VectorFst): +def rm_epsilon(fst: VectorFst) -> VectorFst: """ - rm_epsilon(fst) - rm_epsilon an fst - :param fst: Fst - :return: Fst + Return an equivalent FST with epsilon transitions removed. + Args: + fst: Fst + Returns: + Newly created FST with epsilon transitions removed. """ rm_epsilon_fst = ctypes.c_void_p() diff --git a/rustfst-python/rustfst/algorithms/shortest_path.py b/rustfst-python/rustfst/algorithms/shortest_path.py index 74b085492..ac8d4561d 100644 --- a/rustfst-python/rustfst/algorithms/shortest_path.py +++ b/rustfst-python/rustfst/algorithms/shortest_path.py @@ -1,4 +1,5 @@ from __future__ import annotations +from typing import Union import ctypes from rustfst.ffi_utils import ( lib, @@ -11,7 +12,18 @@ class ShortestPathConfig: - def __init__(self, nshortest: int = 1, unique: bool = False, delta=None): + """ + Configuration for shortest-path operation. + + Args: + nshortest: Number of shortest paths to return + unique: Return only unique label sequences + delta: Difference in weights considered significant + """ + + def __init__( + self, nshortest: int = 1, unique: bool = False, delta: Union[float, None] = None + ): if delta is None: delta = KSHORTESTDELTA config = ctypes.pointer(ctypes.c_void_p()) @@ -28,10 +40,11 @@ def __init__(self, nshortest: int = 1, unique: bool = False, delta=None): def shortestpath(fst: VectorFst) -> VectorFst: """ - shortestpath(fst) - construct a FST containing the shortest path of the input FST - :param fst: Fst - :return: Fst + Construct a FST containing the shortest path of the input FST + Args: + fst: Fst + Returns: + Newly-created FST containing only the shortest path of the input FST. """ shortest_path = ctypes.c_void_p() @@ -44,11 +57,12 @@ def shortestpath(fst: VectorFst) -> VectorFst: def shortestpath_with_config(fst: VectorFst, config: ShortestPathConfig) -> VectorFst: """ - shortestpath(fst,config) - construct a FST containing the n-shortest path(s) in the input FST - :param fst: Fst - :param config: ShortestPathConfig - :return: Fst + Construct a FST containing the shortest path of the input FST + Args: + fst: Fst + config: Configuration for shortest-path operation. + Returns: + Newly-created FST containing only the shortest path of the input FST. """ shortest_path = ctypes.c_void_p() diff --git a/rustfst-python/rustfst/fst/vector_fst.py b/rustfst-python/rustfst/fst/vector_fst.py index a1d210828..06f5b5919 100644 --- a/rustfst-python/rustfst/fst/vector_fst.py +++ b/rustfst-python/rustfst/fst/vector_fst.py @@ -13,10 +13,17 @@ from rustfst.iterators import MutableTrsIterator, StateIterator from rustfst.tr import Tr from rustfst.weight import weight_one -from typing import Optional, Union +from typing import Optional, Union, TYPE_CHECKING from pathlib import Path -from typing import List +from typing import List, Tuple + +if TYPE_CHECKING: + from rustfst.algorithms.compose import ComposeConfig + from rustfst.algorithms.determinize import DeterminizeConfig + from rustfst.algorithms.minimize import MinimizeConfig + from rustfst.algorithms.project import ProjectType + from rustfst.algorithms.shortest_path import ShortestPathConfig class VectorFst(Fst): @@ -90,7 +97,7 @@ def add_state(self) -> int: return state_id.value - def set_final(self, state: int, weight: float = None): + def set_final(self, state: int, weight: Union[float, None] = None): """ Sets the final weight for a state. Args: @@ -104,10 +111,10 @@ def set_final(self, state: int, weight: float = None): if weight is None: weight = weight_one() - state = ctypes.c_size_t(state) - weight = ctypes.c_float(weight) + cstate = ctypes.c_size_t(state) + cweight = ctypes.c_float(weight) - ret_code = lib.vec_fst_set_final(self.ptr, state, weight) + ret_code = lib.vec_fst_set_final(self.ptr, cstate, cweight) err_msg = "Error setting final state" check_ffi_error(ret_code, err_msg) @@ -119,8 +126,8 @@ def unset_final(self, state: int): Raises: ValueError: State index out of range. """ - state = ctypes.c_size_t(state) - ret_code = lib.vec_fst_del_final_weight(self.ptr, state) + cstate = ctypes.c_size_t(state) + ret_code = lib.vec_fst_del_final_weight(self.ptr, cstate) err_msg = "Error unsetting final state" check_ffi_error(ret_code, err_msg) @@ -408,7 +415,19 @@ def copy(self) -> VectorFst: return VectorFst(cloned_fst) - def compose(self, other: VectorFst, config=None) -> VectorFst: + def compose( + self, other: VectorFst, config: Union[ComposeConfig, None] = None + ) -> VectorFst: + """ + Compute composition of this Fst with another Fst, returning + the resulting Fst. + Args: + other: Fst to compose with. + config: Config parameters of the composition. + Returns: + The composed Fst. + """ + from rustfst.algorithms.compose import compose, compose_with_config if config: @@ -417,7 +436,8 @@ def compose(self, other: VectorFst, config=None) -> VectorFst: def concat(self, other: VectorFst) -> VectorFst: """ - Compute Fst Concatenation of this Fst with another Fst. Returning the resulting Fst. + Compute Fst Concatenation of this Fst with another Fst, returning the + resulting Fst. Args: other: Fst to concatenate with. @@ -431,7 +451,8 @@ def concat(self, other: VectorFst) -> VectorFst: def connect(self) -> VectorFst: """ - This operation trims an Fst, removing states and trs that are not on successful paths. + This operation trims an Fst in-place, removing states and trs that are + not on successful paths. Examples : @@ -472,22 +493,43 @@ def top_sort(self) -> VectorFst: return top_sort(self) - def determinize(self, config=None) -> VectorFst: + def determinize(self, config: Union[DeterminizeConfig, None] = None) -> VectorFst: + """ + Make an Fst deterministic + Args: + config: Configuration for the determinization operation. + Returns: + The resulting Fst. + """ from rustfst.algorithms.determinize import determinize, determinize_with_config if config: return determinize_with_config(self, config) return determinize(self) - def minimize(self, config=None) -> VectorFst: + def minimize(self, config: Union[MinimizeConfig, None] = None) -> VectorFst: + """ + Minimize an FST in place + Args: + config: Configuration for the minimization operation. + Returns: + self + """ from rustfst.algorithms.minimize import minimize, minimize_with_config if config: return minimize_with_config(self, config) return minimize(self) - def project(self, proj_type=None) -> VectorFst: - from rustfst.algorithms.project import project, ProjectType + def project(self, proj_type: Union[ProjectType, None] = None) -> VectorFst: + """ + Convert a Fst to an acceptor using input or output labels. + Args: + proj_type: Whether to replace input labels or output labels. + Returns: + self + """ + from rustfst.algorithms.project import project, ProjectType # noqa: W0621 if proj_type: return project(self, proj_type) @@ -497,25 +539,94 @@ def project(self, proj_type=None) -> VectorFst: def replace( self, root_label: int, - fst_list: List[(int, VectorFst)], + fst_list: List[Tuple[int, VectorFst]], epsilon_on_replace: bool = False, ) -> VectorFst: + """Recursively replaces trs in the root FSTs with other FSTs. + + Replace supports replacement of trs in one Fst with another + FST. This replacement is recursive. Replace takes an array of + FST(s). The FST on which this method is called represents the + root (or topology) machine. The root FST refers to other FSTs + by recursively replacing trs labeled as non-terminals with the + matching non-terminal FST. Currently Replace uses the output + symbols of the trs to determine whether the transition is a + non-terminal transition or not. A non-terminal can be any + label that is not a non-zero terminal label in the output + alphabet. + + Note that input argument is a vector of pairs. These + correspond to the tuple of non-terminal Label and + corresponding FST. + + Examples: + + - Root Fst : + + ![replace_in_1](https://raw.githubusercontent.com/Garvys/rustfst-images-doc/master/images/replace_in_1.svg?sanitize=true) + + - Fst for non-terminal #NAME : + + ![replace_in_2](https://raw.githubusercontent.com/Garvys/rustfst-images-doc/master/images/replace_in_2.svg?sanitize=true) + + - Fst for non-terminal #FIRSTNAME : + + ![replace_in_3](https://raw.githubusercontent.com/Garvys/rustfst-images-doc/master/images/replace_in_3.svg?sanitize=true) + + - Fst for non-terminal #LASTNAME : + + ![replace_in_4](https://raw.githubusercontent.com/Garvys/rustfst-images-doc/master/images/replace_in_4.svg?sanitize=true) + + - Output : + + ![replace_out](https://raw.githubusercontent.com/Garvys/rustfst-images-doc/master/images/replace_out.svg?sanitize=true) + + Args: + root_label: Label for self + fst_list: Other FSTs + epsilon_on_replace: + + Returns: + The resulting Fst. + + """ from rustfst.algorithms.replace import replace complete_fst_list = [(root_label, self)] + fst_list return replace(root_label, complete_fst_list, epsilon_on_replace) def reverse(self) -> VectorFst: + """ + Reverse an Fst, returning a new Fst which accepts + the same language in reverse order. + + Returns: + Newly created, reversed Fst. + """ from rustfst.algorithms.reverse import reverse return reverse(self) - def rm_epsilon(self): + def rm_epsilon(self) -> VectorFst: + """ + Return an equivalent FST with epsilon transitions removed. + Returns: + Newly created FST with epsilon transitions removed. + """ from rustfst.algorithms.rm_epsilon import rm_epsilon - rm_epsilon(self) + return rm_epsilon(self) - def shortest_path(self, config=None) -> VectorFst: + def shortest_path( + self, config: Union[ShortestPathConfig, None] = None + ) -> VectorFst: + """ + Construct a FST containing the shortest path of the input FST + Args: + config: Configuration for shortest-path operation. + Returns: + Newly-created FST containing only the shortest path of the input FST. + """ from rustfst.algorithms.shortest_path import ( shortestpath, shortestpath_with_config, @@ -526,33 +637,88 @@ def shortest_path(self, config=None) -> VectorFst: return shortestpath(self) def union(self, other_fst: VectorFst) -> VectorFst: + """ + Performs the union of two wFSTs. If A transduces string `x` to `y` with weight `a` + and `B` transduces string `w` to `v` with weight `b`, then their union transduces `x` to `y` + with weight `a` and `w` to `v` with weight `b`. + + Examples: + - Input Fst 1: + + ![union_in_1](https://raw.githubusercontent.com/Garvys/rustfst-images-doc/master/images/union_in_1.svg?sanitize=true) + + - Input Fst 2: + + ![union_in_2](https://raw.githubusercontent.com/Garvys/rustfst-images-doc/master/images/union_in_2.svg?sanitize=true) + + - Union: + + ![union_out](https://raw.githubusercontent.com/Garvys/rustfst-images-doc/master/images/union_out.svg?sanitize=true) + + Args: + other_fst: Fst to perform union with this one. + Returns: + The resulting newly-created Fst. + + """ from rustfst.algorithms.union import union return union(self, other_fst) def optimize(self) -> VectorFst: + """ + Optimize an FST in-place. + Returns: + self + """ from rustfst.algorithms.optimize import optimize optimize(self) return self def optimize_in_log(self) -> VectorFst: + """ + Optimize an fst in-place in the log semiring. + Returns: + self + """ from rustfst.algorithms.optimize import optimize_in_log optimize_in_log(self) return self def tr_sort(self, ilabel_cmp: bool = True): + """Sort trs for an FST in-place according to their input or + output label. + + This is often necessary for composition to work properly. It + corresponds to `ArcSort` in OpenFST. + + Args: + ilabel_cmp: Sort on input labels if `True`, output labels + if `False`. + """ from rustfst.algorithms.tr_sort import tr_sort tr_sort(self, ilabel_cmp) def tr_unique(self): + """Modify an FST in-place, keeping a single instance of trs + leaving the same state, going to the same state and with the + same input labels, output labels and weight. + """ from rustfst.algorithms.tr_unique import tr_unique tr_unique(self) def isomorphic(self, other: VectorFst) -> bool: + """ + Check if this Fst is isomorphic with another + Args: + other: Other Fst. + Returns: + Whether both Fsts are equal. + """ from rustfst.algorithms.isomorphic import isomorphic return isomorphic(self, other) @@ -604,4 +770,14 @@ def __str__(self): return ctypes.string_at(s).decode("utf8") def string_paths(self) -> StringPathsIterator: + """Return an iterator over input/output label sequences in + this FST, *in no particular order*. + + Note that this does not return the best path first. If you + want to do this, you will have to first apply + `shortest_path`. + + Returns: + A iterator over the paths through this FST. + """ return StringPathsIterator(self)