Skip to content

Typo generation

generation

Perform low-level typo sampling for one generation task with MULTYPO.

generate_typos_for_task

generate_typos_for_task(
    word_list: Sequence[str],
    task: TypoGenerationTask,
    config: TypoGenerationConfig,
    *,
    logger: Logger | None = None,
) -> list[RawTypoSample]

Generate noisy samples for all words eligible for one task.

Parameters:

Name Type Description Default
word_list Sequence[str]

Shared source words to consider.

required
task TypoGenerationTask

Task-specific distribution and sampling settings.

required
config TypoGenerationConfig

Shared MULTYPO generator configuration.

required
logger Logger | None

Optional logger for progress and diagnostics.

None

Returns:

Type Description
list[RawTypoSample]

Successful raw typo samples.

Source code in hotstring\typo_generation\generation.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
def generate_typos_for_task(
    word_list: Sequence[str],
    task: TypoGenerationTask,
    config: TypoGenerationConfig,
    *,
    logger: logging.Logger | None = None,
) -> list[RawTypoSample]:
    """Generate noisy samples for all words eligible for one task.

    Args:
        word_list:
            Shared source words to consider.
        task:
            Task-specific distribution and sampling settings.
        config:
            Shared MULTYPO generator configuration.
        logger:
            Optional logger for progress and diagnostics.

    Returns:
        Successful raw typo samples.
    """
    generator = _create_generator(task, config)
    normalized_words = tuple(_normalize_source_word(word) for word in word_list)
    eligible_words = tuple(
        word for word in normalized_words if len(word) >= task.minimum_word_length
    )

    if logger is not None:
        logger.info(
            "Generating typo samples for %d/%d eligible words with typo_rate=%s, "
            "attempts_per_word=%d, distribution=%s",
            len(eligible_words),
            len(normalized_words),
            task.typo_rate,
            task.generation_attempts_per_word,
            task.distribution.distribution,
        )

    generated: list[RawTypoSample] = []
    for target_word in eligible_words:
        for _ in range(task.generation_attempts_per_word):
            noisy_word = generator.insert_typos(
                target_word,
                typo_rate=float(task.typo_rate),
            ).lower()
            if noisy_word == target_word:
                continue
            generated.append(RawTypoSample(noisy_word=noisy_word, target_word=target_word))

    return generated

_create_generator

_create_generator(
    task: TypoGenerationTask, config: TypoGenerationConfig
) -> MultiTypoGenerator

Create a configured MULTYPO generator for one task.

Source code in hotstring\typo_generation\generation.py
66
67
68
69
70
71
72
73
74
75
76
def _create_generator(
    task: TypoGenerationTask,
    config: TypoGenerationConfig,
) -> MultiTypoGenerator:
    """Create a configured MULTYPO generator for one task."""
    return MultiTypoGenerator(
        language=config.language,
        use_excluding_set=config.use_excluding_set,
        typo_distribution=task.distribution.distribution,
        horizontal_vs_vertical=config.horizontal_vs_vertical,
    )

_normalize_source_word

_normalize_source_word(source_word: str) -> str

Validate and normalize one source word to lowercase.

Source code in hotstring\typo_generation\generation.py
79
80
81
82
83
84
85
86
87
88
89
def _normalize_source_word(source_word: str) -> str:
    """Validate and normalize one source word to lowercase."""
    if not isinstance(source_word, str):
        raise TypeError(f"Source words must be strings, not {type(source_word).__name__}.")
    if not source_word:
        raise ValueError("Source words cannot be empty.")
    if any(character.isspace() for character in source_word):
        raise ValueError(
            f"Source word must contain exactly one word and no whitespace: {source_word!r}"
        )
    return source_word.lower()