def remove_epsilon(fsa: Fsa) -> Fsa: '''Remove epsilons (symbol zero) in the input Fsa. Caution: It only works on for CPU and doesn't support autograd. Args: fsa: The input FSA. It can be either a single FSA or an FsaVec. Must be top-sorted. Returns: The result Fsa, it's equivalent to the input `fsa` under tropical semiring but will be epsilon-free. It will be the same as the input `fsa` if the input `fsa` is epsilon-free. Otherwise, a new epsilon-free fsa is returned and the input `fsa` is NOT modified. ''' assert fsa.is_cpu() assert fsa.requires_grad is False if fsa.properties & fsa_properties.EPSILON_FREE != 0: return fsa ragged_arc, arc_map = _k2.remove_epsilon(fsa.arcs) aux_labels = None if hasattr(fsa, 'aux_labels'): aux_labels = index_attr(fsa.aux_labels, arc_map) out_fsa = Fsa(ragged_arc, aux_labels) for name, value in fsa.named_non_tensor_attr(): setattr(out_fsa, name, value) return out_fsa
def remove_epsilon(fsa: Fsa) -> Fsa: '''Remove epsilons (symbol zero) in the input Fsa. Args: fsa: The input FSA. It can be either a single FSA or an FsaVec. Works either for CPU or GPU, but the algorithm is different. We can only use the CPU algorithm if the input is top-sorted, and the GPU algorithm, while it works for CPU, may not be very fast. `fsa` must be free of epsilon loops that have score greater than 0. Returns: The resulting Fsa, it's equivalent to the input `fsa` under tropical semiring but will be epsilon-free. It will be the same as the input `fsa` if the input `fsa` is epsilon-free. Otherwise, a new epsilon-free fsa is returned and the input `fsa` is NOT modified. ''' if fsa.properties & fsa_properties.EPSILON_FREE != 0: return fsa ragged_arc, arc_map = _k2.remove_epsilon(fsa.arcs, fsa.properties) out_fsa = k2.utils.fsa_from_unary_function_ragged(fsa, ragged_arc, arc_map) return out_fsa
def remove_epsilon(fsa: Fsa) -> Fsa: '''Remove epsilons (symbol zero) in the input Fsa. Args: fsa: The input FSA. It can be either a single FSA or an FsaVec. Works either for CPU or GPU, but the algorithm is different. We can only use the CPU algorithm if the input is top-sorted, and the GPU algorithm, while it works for CPU, may not be very fast. `fsa` must be free of epsilon loops that have score greater than 0. Returns: The resulting Fsa is equivalent to the input `fsa` under the tropical semiring but will be epsilon-free. Any linear tensor attributes, such as 'aux_labels', will have been turned into ragged labels after removing fillers (i.e. labels whose value equals fsa.XXX_filler if the attribute name is XXX), counting -1's on final-arcs as fillers even if the filler value for that attribute is not -1. ''' ragged_arc, arc_map = _k2.remove_epsilon(fsa.arcs, fsa.properties) out_fsa = k2.utils.fsa_from_unary_function_ragged(fsa, ragged_arc, arc_map, remove_filler=True) if hasattr(out_fsa, 'aux_labels') and \ isinstance(out_fsa.aux_labels, k2.RaggedInt): out_fsa.aux_labels = k2.ragged.remove_values_eq(out_fsa.aux_labels, 0) return out_fsa
def remove_epsilon(fsa: Fsa) -> Fsa: '''Remove epsilons (symbol zero) in the input Fsa. Caution: It only works on for CPU and doesn't support autograd. Args: fsa: The input FSA. It can be either a single FSA or an FsaVec. Must be top-sorted. Returns: The result Fsa, it's equivalent to the input ``fsa`` under tropical semiring but will be epsilon-free. It will be the same as the input ``fsa`` if the input ``fsa`` is epsilon-free. Otherwise, a new epsilon-free fsa is returned and the input ``fsa`` is NOT modified. ''' properties = getattr(fsa, 'properties', None) if properties is not None and properties & fsa_properties.EPSILON_FREE != 0: return fsa ragged_arc = _k2.remove_epsilon(fsa.arcs) out_fsa = Fsa(ragged_arc) for name, value in fsa.named_non_tensor_attr(): setattr(out_fsa, name, value) return out_fsa
def test_without_empty_list(self): for device in self.devices: s = ''' 0 1 0 0 0 1 1 0 1 2 -1 0 2 ''' scores = torch.tensor([1, 2, 3], dtype=torch.float32, device=device, requires_grad=True) scores_copy = scores.detach().clone().requires_grad_(True) src = k2.Fsa.from_str(s).to(device) src.scores = scores src.attr1 = "hello" src.attr2 = "k2" float_attr = torch.tensor([0.1, 0.2, 0.3], dtype=torch.float32, requires_grad=True, device=device) src.float_attr = float_attr.detach().clone().requires_grad_(True) src.int_attr = torch.tensor([1, 2, 3], dtype=torch.int32, device=device) src.ragged_attr = k2.RaggedInt( '[ [10 20] [30 40 50] [60 70] ]').to(device) ragged_arc, arc_map = _k2.remove_epsilon(src.arcs, src.properties) dest = k2.utils.fsa_from_unary_function_ragged( src, ragged_arc, arc_map) assert dest.attr1 == src.attr1 assert dest.attr2 == src.attr2 expected_arc_map = k2.RaggedInt('[ [1] [0 2] [2] ]') self.assertEqual(str(arc_map), str(expected_arc_map)) expected_int_attr = k2.RaggedInt('[ [2] [1 3] [3] ]') self.assertEqual(str(dest.int_attr), str(expected_int_attr)) expected_ragged_attr = k2.RaggedInt( '[ [30 40 50] [10 20 60 70] [60 70] ]') self.assertEqual(str(dest.ragged_attr), str(expected_ragged_attr)) expected_float_attr = torch.empty_like(dest.float_attr) expected_float_attr[0] = float_attr[1] expected_float_attr[1] = float_attr[0] + float_attr[2] expected_float_attr[2] = float_attr[2] assert torch.all(torch.eq(dest.float_attr, expected_float_attr)) expected_scores = torch.empty_like(dest.scores) expected_scores[0] = scores_copy[1] expected_scores[1] = scores_copy[0] + scores_copy[2] expected_scores[2] = scores_copy[2] assert torch.all(torch.eq(dest.scores, expected_scores)) scale = torch.tensor([10, 20, 30]).to(float_attr) (dest.float_attr * scale).sum().backward() (expected_float_attr * scale).sum().backward() assert torch.all(torch.eq(src.float_attr.grad, float_attr.grad)) (dest.scores * scale).sum().backward() (expected_scores * scale).sum().backward() assert torch.all(torch.eq(scores.grad, scores_copy.grad))