Library API
Core library functionality.
The pstrain.api module is the recommended public API. It re-exports most of
pstrain.lib and adds higher-level entry points; the modules are not
interchangeable.
Public API
pstrain public API.
This is the recommended entry point for using pstrain programmatically. CLI and web clients should call into this API.
The API exposes JSON-returning functions and typed objects and drivers, including models, configurations, and the training pipeline.
Example:
from pstrain.api import (
# Project setup
setup_project,
validate_project,
ValidationReport,
# Configuration
Profile,
# Data structures
Dictionary,
Phoneset,
# Models
create_model,
CIModel,
CDModel,
# Training steps
run_step_ci_hmm,
)
- class pstrain.api.CDModel(config='baseline')[source]
Bases:
ModelContext-Dependent (triphone) acoustic model.
- Parameters:
config (str)
- classmethod from_string(value)[source]
Get model class from string identifier.
- Parameters:
value (str) – Model type string (e.g., “cd”, “CD”, “context-dependent”)
- Returns:
Model class (CDModel)
- Raises:
ValueError – If model type is unknown
- Return type:
- class pstrain.api.CIModel(config='baseline')[source]
Bases:
ModelContext-Independent (monophone) acoustic model.
- Parameters:
config (str)
- classmethod from_string(value)[source]
Get model class from string identifier.
- Parameters:
value (str) – Model type string (e.g., “ci”, “CD”, “context-independent”)
- Returns:
Model class (CIModel)
- Raises:
ValueError – If model type is unknown
- Return type:
- class pstrain.api.CMUDict[source]
Bases:
DictionaryCMUDict-style dictionary with ARPABET stress handling.
Extends Dictionary with ARPABET-specific features: - Stress marker parsing and manipulation - Stress-stripped variants for training - Vowel/consonant classification
- classmethod from_file(path)[source]
Load CMUDict from file.
Overrides parent to return CMUDict instance.
- get_stressed_vowels(word)[source]
Get vowels with their stress levels for a word.
- Parameters:
word (str) – Word to look up
- Returns:
List of (vowel, stress_level) tuples stress_level is 0, 1, 2, or -1 if no stress marker
- Return type:
Example
get_stressed_vowels(“HELLO”) -> [(“AH”, 0), (“OW”, 1)] # for HH AH0 L OW1
- strip_stress_from_entries()[source]
Create a copy with stress markers removed from all phones.
- Returns:
New CMUDict with stress-free pronunciations
- Return type:
Note
This may create duplicate entries (e.g., “read” R IY D and R EH D both become R IY D and R EH D without stress). Duplicates are automatically deduplicated.
- class pstrain.api.CMUDictSource(dictionary, phones, symbols, license, source_dictionary, requested_ref, resolved_ref, cache_directory)[source]
Bases:
objectPaths and provenance for a cached upstream CMUdict checkout.
- Parameters:
- __init__(dictionary, phones, symbols, license, source_dictionary, requested_ref, resolved_ref, cache_directory)
- class pstrain.api.Dictionary[source]
Bases:
objectPronunciation dictionary with Unicode and case-sensitive support.
Supports: - UTF-8 encoding - Case-sensitive words (hello != Hello != HELLO) - Sphinx-style variants: word, word(2), word(3), … - Multi-word entries: New_York, ice_cream - Any phoneset (ARPABET, IPA, X-SAMPA, custom)
- add_entry(word, phonemes)[source]
Add pronunciation entry to dictionary.
Automatically handles duplicate pronunciations: - Same word + same pronunciation → Skip silently (true duplicate) - Same word + different pronunciation → Create variant (word(2), word(3), etc.)
- Parameters:
- Return type:
None
Examples
READ R EH D # Stored as READ READ R IY D # Automatically stored as READ(2) READ R EH D # Skipped (duplicate of first)
- filter_to_vocabulary(vocabulary, include_variants=True)[source]
Create filtered dictionary containing only words in vocabulary.
- Parameters:
- Returns:
New Dictionary containing matched words
- Return type:
Examples
Given dict: read, read(2), hello, world vocab = {“read”, “hello”}
include_variants=True: read, read(2), hello include_variants=False: read, hello (no read(2))
vocab = {“read(2)”} include_variants=True: read(2) only (exact variant specified) include_variants=False: read(2) only
- classmethod from_file(path)[source]
Load dictionary from file.
- Parameters:
path (Path) – Path to dictionary file (UTF-8 encoded)
- Returns:
Dictionary instance
- Raises:
ValueError – If file has encoding or format errors
- Return type:
- get_variants(base_word)[source]
Get all pronunciation variants for a base word.
- Parameters:
base_word (str) – Base word (without variant suffix)
- Returns:
List of pronunciations (each is a list of phonemes) Returns empty list if word not found
- Return type:
Examples
get_variants(“read”) -> [[“R”, “IY”, “D”], [“R”, “EH”, “D”]]
- merge(other)[source]
Merge another dictionary into this one.
- Parameters:
other (Dictionary) – Dictionary to merge in
- Return type:
None
Note
Uses add_entry which automatically handles: - Deduplicating identical pronunciations - Creating variants for different pronunciations - Renumbering variants sequentially
- property pronunciations: dict[str, list[list[str]]]
Get all pronunciations as a dictionary mapping base words to variant lists.
- Returns:
Dict mapping base word to list of pronunciations Each pronunciation is a list of phonemes
Examples
- {“read”: [[“R”, “IY”, “D”], [“R”, “EH”, “D”]],
“hello”: [[“HH”, “AH”, “L”, “OW”]]}
- class pstrain.api.FeatureConfig(*, samprate=16000, ncep=13, nfilt=25, nfft=512, lowerf=130.0, upperf=6800.0, alpha=0.97, dither=True, seed=-1, remove_dc=True, remove_noise=True, frate=100, wlen=0.025625, feat_type='1s_c_d_dd', lifter=22, transform='dct', agc='none', cmn='batch', cmninit='40,3,-1', varnorm='no')[source]
Bases:
StrictModelAcoustic front-end parameters.
- Parameters:
samprate (Annotated[int, FieldInfo(annotation=NoneType, required=True, description='Audio sample rate in Hz', metadata=[Gt(gt=0)])])
ncep (Annotated[int, FieldInfo(annotation=NoneType, required=True, description='Number of cepstral coefficients', metadata=[Gt(gt=0)])])
nfilt (Annotated[int, FieldInfo(annotation=NoneType, required=True, description='Number of mel filters', metadata=[Gt(gt=0)])])
nfft (Annotated[int, FieldInfo(annotation=NoneType, required=True, description='FFT size', metadata=[Gt(gt=0)])])
lowerf (Annotated[float, FieldInfo(annotation=NoneType, required=True, description='Lower filter-bank frequency in Hz', metadata=[Ge(ge=0)])])
upperf (Annotated[float, FieldInfo(annotation=NoneType, required=True, description='Upper filter-bank frequency in Hz', metadata=[Gt(gt=0)])])
alpha (Annotated[float, FieldInfo(annotation=NoneType, required=True, description='Pre-emphasis coefficient')])
dither (Annotated[bool, FieldInfo(annotation=NoneType, required=True, description='Add half-bit dither to input audio')])
seed (Annotated[int, FieldInfo(annotation=NoneType, required=True, description='Seed for deterministic input dithering')])
remove_dc (Annotated[bool, FieldInfo(annotation=NoneType, required=True, description='Remove DC offset from each frame')])
remove_noise (Annotated[bool, FieldInfo(annotation=NoneType, required=True, description='Remove noise with spectral subtraction')])
frate (Annotated[int, FieldInfo(annotation=NoneType, required=True, description='Feature frame rate in Hz', metadata=[Gt(gt=0)])])
wlen (Annotated[float, FieldInfo(annotation=NoneType, required=True, description='Analysis window length in seconds', metadata=[Gt(gt=0)])])
feat_type (Annotated[str, FieldInfo(annotation=NoneType, required=True, description='Sphinx feature stream type')])
lifter (Annotated[int, FieldInfo(annotation=NoneType, required=True, description='Cepstral lifter window', metadata=[Ge(ge=0)])])
transform (Annotated[str, FieldInfo(annotation=NoneType, required=True, description='Filter-bank transform')])
agc (Annotated[str, FieldInfo(annotation=NoneType, required=True, description='Automatic gain-control mode')])
cmn (Annotated[str, FieldInfo(annotation=NoneType, required=True, description='Cepstral mean-normalization mode')])
cmninit (Annotated[str, FieldInfo(annotation=NoneType, required=True, description='Initial cepstral mean vector for live CMN')])
varnorm (Annotated[str, FieldInfo(annotation=NoneType, required=True, description='Cepstral variance-normalization mode')])
- model_config = {'extra': 'forbid', 'frozen': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pstrain.api.FileType(*values)[source]
Bases:
EnumKnown file types in pstrain/Sphinx ecosystem.
- DICTIONARY = 'dictionary'
- FEATURES = 'features'
- FILEIDS = 'fileids'
- FILLER_DICT = 'filler_dict'
- GAUDEN_COUNTS = 'gauden_counts'
- LM_ARPA = 'lm_arpa'
- MDEF = 'mdef'
- MEANS = 'means'
- MIXTURE_WEIGHTS = 'mixture_weights'
- MODEL = 'model'
- PHONESET = 'phoneset'
- SENDUMP = 'sendump'
- TRANSCRIPTION = 'transcription'
- TRANSITION_MATRICES = 'transition_matrices'
- UNKNOWN = 'unknown'
- VARIANCES = 'variances'
- class pstrain.api.Model(config='baseline')[source]
Bases:
ABCBase class for acoustic models.
Each model type (CI, CD) should inherit from this class and implement the abstract methods to define its specific behavior.
- Parameters:
config (str)
- __init__(config='baseline')[source]
Initialize model.
- Parameters:
config (str) – Model configuration name (e.g., “baseline”, “1g”, “lda”)
- Return type:
None
- abstractmethod classmethod from_string(value)[source]
Get model class from string identifier.
- Parameters:
value (str) – Model type string (e.g., “ci”, “CD”, “context-independent”)
- Returns:
Model class
- Raises:
ValueError – If model type is unknown
- Return type:
- abstractmethod get_default_training_params()[source]
Get default training parameters for this model type.
- class pstrain.api.ModelCompareResult(dir_a, dir_b, components, topology_compatible)[source]
Bases:
objectResult of comparing two complete models.
- Parameters:
- __init__(dir_a, dir_b, components, topology_compatible)
- class pstrain.api.Phoneset(phones)[source]
Bases:
objectPhone inventory with validation and mapping capabilities.
Supports: - Loading from file (one phone per line) - Extracting from dictionary - Validating dictionaries against phoneset - Phone mapping between phonesets
- classmethod from_dictionary(dictionary, include_sil=True, silence_phone='SIL')[source]
Extract phoneset from dictionary.
- Parameters:
dictionary (Dictionary) – Dictionary to extract phones from
include_sil (bool) – If True, add silence phone
silence_phone (str) – Symbol for silence (default: “SIL”)
- Returns:
Phoneset instance
- Return type:
Self
- classmethod from_file(path)[source]
Load phoneset from file.
File format: - One phone per line - Comments start with # - UTF-8 encoding, case-sensitive
- Parameters:
path (Path) – Path to phoneset file
- Returns:
Phoneset instance
- Raises:
ValueError – If file has encoding or format errors
- Return type:
- map_phone(phone, mapping, passthrough_unmapped=True)[source]
Map a single phone.
Supports: - One-to-one: “AA” -> “ɑ” produces [“ɑ”] - Expansion: “CH” -> “t ʃ” produces [“t”, “ʃ”] (splits on space) - Deletion: “X” -> “” produces []
- map_pronunciation(phones, mapping, passthrough_unmapped=True)[source]
Map a pronunciation (list of phones).
- class pstrain.api.Profile(*, description='', features=<factory>, training=<factory>, split=<factory>, runner=<factory>, sharding=<factory>, alignment=<factory>)[source]
Bases:
StrictModelOne complete named model-training profile.
- Parameters:
description (Annotated[str, FieldInfo(annotation=NoneType, required=True, description='Human-readable profile purpose')])
features (FeatureConfig)
training (TrainingConfig)
split (SplitConfig)
runner (RunnerConfig)
sharding (ShardingConfig)
alignment (AlignmentConfig)
- alignment: AlignmentConfig
- features: FeatureConfig
- model_config = {'extra': 'forbid', 'frozen': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- runner: RunnerConfig
- sharding: ShardingConfig
- split: SplitConfig
- training: TrainingConfig
- class pstrain.api.PstrainPaths(bin_dir, lib_path, include_dir, project_root, data_dir)[source]
Bases:
objectCollection of pstrain installation paths.
- Parameters:
- __init__(bin_dir, lib_path, include_dir, project_root, data_dir)
- class pstrain.api.TrainingConfig(*, n_state=3, skip_state=False, n_senones=200, a_beam=1e-90, b_beam=1e-10, ci=<factory>, tied=<factory>, untied=<factory>, split_variance_floor_fraction=0.0, max_skip_fraction=0.05, retry_beam_factor=10000000000.0, failed_alignment='recover', bw_checkpoint_iterations=False, arctic_a0302_zero_codebook_band=None, accept_arctic_a0587_known_skip=False, tree_state_weights=(1.0, 0.05, 0.0), tree_rotate_state_weights=True, tree_directional_questions=True, tree_ssplitmax=7, tree_ssplitthr=0.0, tree_csplitmax=2000, tree_csplitthr=0.0, tree_mwfloor=1e-08, tree_intermediate_dumps=False, question_npermute=12, question_quests_per_state=20, question_niter=1, multipron_training=True, optional_final_silence=True, untied_inventory='transcript-reachable', exclusion_schedule=<factory>)[source]
Bases:
StrictModelAcoustic-model training parameters.
- Parameters:
n_state (Annotated[int, FieldInfo(annotation=NoneType, required=True, description='Emitting states per HMM', metadata=[Ge(ge=1)])])
skip_state (Annotated[bool, FieldInfo(annotation=NoneType, required=True, description="Enable SphinxTrain's $CFG_SKIPSTATE topology, adding an arc from each eligible emitting state to the state two positions ahead so a phone can be realized with fewer frames than states. SphinxTrain writes raw 3/1/1 weights and normalizes them on read; pstrain writes the behaviorally equivalent normalized values")])
n_senones (Annotated[int, FieldInfo(annotation=NoneType, required=True, description='Target tied-state count', metadata=[Ge(ge=1)])])
a_beam (Annotated[float, FieldInfo(annotation=NoneType, required=True, description='Forward alignment beam', metadata=[Gt(gt=0)])])
b_beam (Annotated[float, FieldInfo(annotation=NoneType, required=True, description='Backward alignment beam', metadata=[Gt(gt=0)])])
ci (TrainingScheduleConfig)
tied (TrainingScheduleConfig)
untied (TrainingScheduleConfig)
split_variance_floor_fraction (Annotated[float, FieldInfo(annotation=NoneType, required=True, description='Experimental variance lower bound for split training stages, as a fraction of each matching coordinate in the fixed CI-1g or CD-1g variance reference. Zero disables regularization. The reference never advances with later splits; zero reference coordinates contribute no positive floor. Select a nonzero fraction explicitly after evaluation; no universal nonzero value is assumed', metadata=[Strict(strict=True), Ge(ge=0), Le(le=1), _PydanticGeneralMetadata(allow_inf_nan=False)])])
max_skip_fraction (Annotated[float, FieldInfo(annotation=NoneType, required=True, description='Maximum skipped-update fraction', metadata=[Ge(ge=0), Le(le=1)])])
retry_beam_factor (Annotated[float | list[float], PlainValidator(func=~pstrain.lib.config.models._validate_retry_beam_factor, json_schema_input_type=~typing.Any), WithJsonSchema(json_schema={'anyOf': [{'type': 'number', 'exclusiveMinimum': 0}, {'type': 'array', 'items': {'type': 'number', 'exclusiveMinimum': 1}, 'minItems': 1}]}, mode=None), FieldInfo(annotation=NoneType, required=True, description='Factor that widens the forward beam for one retry after an utterance fails to reach its final state, or an ascending list of factors, each greater than 1 and relative to the nominal beam, tried in order until one succeeds; a retry is counted only when that attempt runs')])
failed_alignment (Annotated[Literal['recover', 'abort', 'omit'], FieldInfo(annotation=NoneType, required=True, description='Action when an utterance fails to reach its final state: ``recover`` runs the wider-beam retries in ``retry_beam_factor`` and, if they all fail, reports the utterance and continues without it; ``abort`` fails the run on the first failure; and ``omit`` reports and excludes it without retrying. Skips are counted either way, and ``max_skip_fraction`` still fails the run when they stop being incidental')])
bw_checkpoint_iterations (Annotated[bool, FieldInfo(annotation=NoneType, required=True, description='Retain the compact model files from every completed Baum-Welch pass under ``iterations/NN``; costs roughly one additional model copy per pass and does not retain the much larger ``.bw-accum`` shard accumulators or change which checkpoint is loaded by training. The deprecated ``PSTRAIN_BW_CHECKPOINTS=1`` environment variable can also enable retention, but cannot disable a true profile setting')])
arctic_a0302_zero_codebook_band (Annotated[tuple[int, int] | None, FieldInfo(annotation=NoneType, required=True, description='Accepted inclusive exact-zero codebook occupancy band for the singular Arctic a0302 terminal-alignment exception')])
accept_arctic_a0587_known_skip (Annotated[bool, FieldInfo(annotation=NoneType, required=True, description="Deprecated for live profiles: retained solely for the Arctic pin's retired off-profile provenance; live benchmark cells run exception-free")])
tree_state_weights (Annotated[tuple[float, ...], FieldInfo(annotation=NoneType, required=True, description='Decision-tree state weights', metadata=[MinLen(min_length=1)])])
tree_rotate_state_weights (Annotated[bool, FieldInfo(annotation=NoneType, required=True, description='Apply target-relative tree state weights; disable only for isolation measurements')])
tree_directional_questions (Annotated[bool, FieldInfo(annotation=NoneType, required=True, description='Honor _L/_R tree-question suffixes; disable only for isolation measurements')])
tree_ssplitmax (Annotated[int, FieldInfo(annotation=NoneType, required=True, description='Maximum state splits', metadata=[Ge(ge=0)])])
tree_ssplitthr (Annotated[float, FieldInfo(annotation=NoneType, required=True, description='State split threshold', metadata=[Ge(ge=0)])])
tree_csplitmax (Annotated[int, FieldInfo(annotation=NoneType, required=True, description='Maximum phone-context splits', metadata=[Ge(ge=0)])])
tree_csplitthr (Annotated[float, FieldInfo(annotation=NoneType, required=True, description='Phone-context split threshold', metadata=[Ge(ge=0)])])
tree_mwfloor (Annotated[float, FieldInfo(annotation=NoneType, required=True, description='Tree mixture-weight floor', metadata=[Gt(gt=0)])])
tree_intermediate_dumps (Annotated[bool, FieldInfo(annotation=NoneType, required=True, description='Dump intermediate decision trees to worker diagnostics')])
question_npermute (Annotated[int, FieldInfo(annotation=NoneType, required=True, description='Question permutations', metadata=[Ge(ge=1)])])
question_quests_per_state (Annotated[int, FieldInfo(annotation=NoneType, required=True, description='Questions generated per state', metadata=[Ge(ge=1)])])
question_niter (Annotated[int, FieldInfo(annotation=NoneType, required=True, description='Question generation iterations', metadata=[Ge(ge=1)])])
multipron_training (Annotated[bool, FieldInfo(annotation=NoneType, required=True, description='Sum posteriors over pronunciation variants; when disabled without an explicit inventory policy, untied inventory resolves to upstream-compatible ``linear``')])
optional_final_silence (Annotated[bool, FieldInfo(annotation=NoneType, required=True, description='Permit final transcript silence to consume zero frames; stock SphinxTrain requires that silence to consume at least one frame')])
untied_inventory (Annotated[Literal['all-triphone', 'transcript-reachable', 'linear'], FieldInfo(annotation=NoneType, required=True, description="Untied-model phone inventory policy: ``transcript-reachable`` includes contexts reachable through every pronunciation when multipron training is enabled; upstream-compatible ``linear`` includes contexts observed through each transcript word's first pronunciation; ``all-triphone`` includes the complete phoneset cross-product")])
exclusion_schedule (Annotated[dict[str, dict[Annotated[int, FieldInfo(annotation=NoneType, required=True, metadata=[Strict(strict=True)])] | str, list[str]]], FieldInfo(annotation=NoneType, required=True, description='Experimental stage/pass utterance exclusions')])
- accept_arctic_a0587_known_skip: Annotated[bool, Field(description="Deprecated for live profiles: retained solely for the Arctic pin's retired off-profile provenance; live benchmark cells run exception-free")]
- arctic_a0302_zero_codebook_band: Annotated[tuple[int, int] | None, Field(description='Accepted inclusive exact-zero codebook occupancy band for the singular Arctic a0302 terminal-alignment exception')]
- bw_checkpoint_iterations: Annotated[bool, Field(description='Retain the compact model files from every completed Baum-Welch pass under ``iterations/NN``; costs roughly one additional model copy per pass and does not retain the much larger ``.bw-accum`` shard accumulators or change which checkpoint is loaded by training. The deprecated ``PSTRAIN_BW_CHECKPOINTS=1`` environment variable can also enable retention, but cannot disable a true profile setting')]
- ci: TrainingScheduleConfig
- exclusion_schedule: Annotated[dict[str, dict[Annotated[int, Field(strict=True)] | str, list[str]]], Field(description='Experimental stage/pass utterance exclusions')]
- failed_alignment: Annotated[Literal['recover', 'abort', 'omit'], Field(description='Action when an utterance fails to reach its final state: ``recover`` runs the wider-beam retries in ``retry_beam_factor`` and, if they all fail, reports the utterance and continues without it; ``abort`` fails the run on the first failure; and ``omit`` reports and excludes it without retrying. Skips are counted either way, and ``max_skip_fraction`` still fails the run when they stop being incidental')]
- max_skip_fraction: Annotated[float, Field(ge=0, le=1, description='Maximum skipped-update fraction')]
- model_config = {'extra': 'forbid', 'frozen': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- multipron_training: Annotated[bool, Field(description='Sum posteriors over pronunciation variants; when disabled without an explicit inventory policy, untied inventory resolves to upstream-compatible ``linear``')]
- optional_final_silence: Annotated[bool, Field(description='Permit final transcript silence to consume zero frames; stock SphinxTrain requires that silence to consume at least one frame')]
- question_quests_per_state: Annotated[int, Field(ge=1, description='Questions generated per state')]
- retry_beam_factor: Annotated[RetryBeamFactorSetting, Field(description='Factor that widens the forward beam for one retry after an utterance fails to reach its final state, or an ascending list of factors, each greater than 1 and relative to the nominal beam, tried in order until one succeeds; a retry is counted only when that attempt runs')]
- skip_state: Annotated[bool, Field(description="Enable SphinxTrain's $CFG_SKIPSTATE topology, adding an arc from each eligible emitting state to the state two positions ahead so a phone can be realized with fewer frames than states. SphinxTrain writes raw 3/1/1 weights and normalizes them on read; pstrain writes the behaviorally equivalent normalized values")]
- split_variance_floor_fraction: Annotated[float, Field(ge=0, le=1, allow_inf_nan=False, strict=True, description='Experimental variance lower bound for split training stages, as a fraction of each matching coordinate in the fixed CI-1g or CD-1g variance reference. Zero disables regularization. The reference never advances with later splits; zero reference coordinates contribute no positive floor. Select a nonzero fraction explicitly after evaluation; no universal nonzero value is assumed')]
- tied: TrainingScheduleConfig
- tree_directional_questions: Annotated[bool, Field(description='Honor _L/_R tree-question suffixes; disable only for isolation measurements')]
- tree_intermediate_dumps: Annotated[bool, Field(description='Dump intermediate decision trees to worker diagnostics')]
- tree_rotate_state_weights: Annotated[bool, Field(description='Apply target-relative tree state weights; disable only for isolation measurements')]
- tree_state_weights: Annotated[tuple[float, ...], Field(min_length=1, description='Decision-tree state weights')]
- untied: TrainingScheduleConfig
- untied_inventory: Annotated[Literal['all-triphone', 'transcript-reachable', 'linear'], Field(description="Untied-model phone inventory policy: ``transcript-reachable`` includes contexts reachable through every pronunciation when multipron training is enabled; upstream-compatible ``linear`` includes contexts observed through each transcript word's first pronunciation; ``all-triphone`` includes the complete phoneset cross-product")]
- exception pstrain.api.TutorialExistsError(path)[source]
Bases:
FileExistsErrorRaised when a tutorial destination may not be replaced.
- Parameters:
path (Path)
- Return type:
None
- class pstrain.api.TutorialResult[source]
Bases:
TypedDictJSON-serializable result of a tutorial copy request.
- class pstrain.api.ValidationReport(errors=<factory>, warnings=<factory>, train_utterances=0, test_utterances=0, dev_utterances=0, total_utterances=0, vocabulary_size=0, missing_words=<factory>, dictionary_entries=0, dictionary_base_words=0, phoneset_size=0, has_silence=False, missing_phones=<factory>, audio_files=0, missing_audio=<factory>)[source]
Bases:
objectValidation report with errors and stats.
- Parameters:
- __init__(errors=<factory>, warnings=<factory>, train_utterances=0, test_utterances=0, dev_utterances=0, total_utterances=0, vocabulary_size=0, missing_words=<factory>, dictionary_entries=0, dictionary_base_words=0, phoneset_size=0, has_silence=False, missing_phones=<factory>, audio_files=0, missing_audio=<factory>)
- pstrain.api.build_lm(transcripts, output_path, max_order=3, smoothing='auto')[source]
Build an ARPA language model from transcripts using arpabo.
- Parameters:
- Returns:
Path to the created LM file
- Return type:
Note
“auto” mode uses optimized Katz backoff, which works well for typical speech corpus sizes. For very small corpora, “good_turing” may be better.
- pstrain.api.build_lm_from_file(transcript_file, output_path, max_order=3, smoothing='auto')[source]
Build an ARPA LM from a supported transcription file.
- pstrain.api.compare_auto(file_a, file_b, rtol=1e-05, atol=1e-08)[source]
Auto-detect file types and compare.
- Parameters:
- Returns:
Tuple of (detected_type, comparison_result)
- Raises:
ValueError – If file types don’t match or are unknown
- Return type:
tuple[FileType, CompareResult | ModelCompareResult]
- pstrain.api.compare_features(file_a, file_b, rtol=1e-05, atol=1e-08)[source]
Compare two feature files (.mfc format).
- pstrain.api.compare_gaussians(file_a, file_b, rtol=1e-05, atol=1e-08)[source]
Compare two Gaussian parameter files (means or variances).
- pstrain.api.compare_mixw(file_a, file_b, rtol=1e-05, atol=1e-08)[source]
Compare two mixture weight files.
- pstrain.api.compare_models(dir_a, dir_b, rtol=1e-05, atol=1e-08)[source]
Compare two complete model directories.
Compares all model components: - Critical: mdef (topology), feat.params (feature config) - Parameters: means, variances, mixture_weights, transition_matrices, sendump - Other: noisedict, README, etc.
- Parameters:
- Returns:
ModelCompareResult with detailed comparison of all components
- Return type:
- pstrain.api.compare_tmat(file_a, file_b, rtol=1e-05, atol=1e-08)[source]
Compare two transition matrix files.
- pstrain.api.copy_tutorial(output='arctic_hmm_gmm_tutorial.ipynb', *, force=False, dry_run=False)[source]
Copy the bundled tutorial notebook to output.
If output is an existing directory, the standard tutorial filename is appended. Existing files are protected unless force is true.
- Parameters:
- Return type:
- pstrain.api.create_model(model_type, config='baseline')[source]
Create a model instance.
- Parameters:
- Returns:
Model instance of the specified type
- Raises:
ValueError – If model type is unknown
- Return type:
- pstrain.api.default_cmudict_cache()[source]
Return the CMUdict cache within pstrain’s benchmark cache hierarchy.
- Return type:
- pstrain.api.detect_file_type(path)[source]
Detect file type from path and content.
Detection strategy: 1. Check if it’s a directory (model) 2. Check filename against known patterns 3. Check file extension 4. Check file content/magic bytes
- pstrain.api.extract_features(audio_path, output_path, *, fmt='sphinx', **config)[source]
Extract features from audio file and write to output file.
- Parameters:
- Returns:
Number of frames extracted
- Return type:
- pstrain.api.fetch_cmudict(ref='HEAD', cache=None)[source]
Fetch, convert, and cache CMUdict from its original upstream source.
refmay be a tag, branch, or commit SHA. Network access happens only in a fresh child process. On macOS, an in-process HTTPS download followed by a native worker spawn crashes the interpreter, so the download is kept out of any process that later spawns workers.- Parameters:
- Return type:
- pstrain.api.get_fileids(transcription_path)[source]
Get list of fileids from transcription file.
- Parameters:
transcription_path (Path) – Path to transcription file
- Returns:
List of file IDs (utterance identifiers)
- Raises:
FileNotFoundError – If transcription file does not exist
UnicodeDecodeError – If file is not UTF-8 encoded
- Return type:
- pstrain.api.get_model_class(model_type)[source]
Get model class from model type string.
- Parameters:
model_type (str) – Model type identifier (e.g., “ci”, “cd”)
- Returns:
Model class
- Raises:
ValueError – If model type is unknown
- Return type:
- pstrain.api.get_paths()[source]
Get all pstrain paths.
- Returns:
PstrainPaths with discovered locations (None if not found)
- Return type:
- pstrain.api.init_flat_model(phones, output_dir, n_state=3, n_density=1, ctl_path=None, cep_dir=None, cep_ext='.mfc', feat_type='1s_c_d_dd', ceplen=13, max_skip_fraction=0.05, skip_state=False)[source]
Initialize a complete flat model.
If ctl_path and cep_dir are provided, computes means/variances from the feature data using init_gau. Otherwise creates placeholder files.
- Parameters:
output_dir (Path) – Directory to write model files
n_state (int) – Number of emitting states per phone
n_density (int) – Number of Gaussians per state
ctl_path (Path | None) – Control file for feature computation
cep_dir (Path | None) – Feature directory for computation
cep_ext (str) – Feature file extension
feat_type (str) – Feature type string
ceplen (int) – Cepstral dimension
skip_state (bool) – Add state-to-state-plus-two transition arcs
max_skip_fraction (float)
- Returns:
Dict mapping file type to path
- Return type:
- pstrain.api.package_model(model_dir, output_dir, model_name=None, dictionary_path=None, filler_dict_path=None, include_dict=True, *, overwrite=False)[source]
Package a trained model for distribution.
Creates a complete, self-contained model directory that can be used directly with PocketSphinx and other Sphinx decoders.
- Parameters:
model_dir (Path) – Source model directory
output_dir (Path) – Output directory for packaged model
model_name (str | None) – Name for the model (used in output path)
dictionary_path (Path | None) – Path to pronunciation dictionary
filler_dict_path (Path | None) – Path to filler dictionary
include_dict (bool) – Whether to include dictionary in package
overwrite (bool) – Allow replacement of a recognizable package without a marker
- Returns:
Dict mapping file types to output paths
- Return type:
Notes
A supported package marker permits replacement by default; a recognizable legacy package without a marker requires
overwrite=True. Unrecognized destinations and invalid or unsupported markers are never replaced.On macOS and Linux, replacement opens the source, destination parent, staging directory, retained package, and recovery directory without following their final names. Identities come from those descriptors; validation and traversal remain descriptor-relative; and every rename is reconciled from the filesystem even when its call raises. Windows retains the path-based transaction and makes no guarantee against an active process substituting names during packaging. An asynchronous interruption can also leave a mixed unnamed package on Windows because its path transaction cannot reconcile a rename that completed before raising. No supported platform promises safety against every active same-filesystem race because final directory-entry deletion has no portable conditional-by-descriptor primitive. See
docs/package-safety.mdfor the exact guarantee, recovery instructions, and remaining seams.With no model name,
acoustic,dict,README.txt, andpstrain-package.jsontransition separately. On a handled failure the implementation reconciles each open identity and attempts to restore the previous public set. Individual paths can be absent during that recovery; failures are attached to the initiating exception and recovery directories are retained when certainty is lost. Unrelated entries inoutput_dirare preserved.Example output structure:
dist/models/my-model/ ├── acoustic/ │ ├── feat.params │ ├── mdef │ ├── means │ ├── variances │ ├── mixture_weights │ ├── transition_matrices │ └── noisedict ├── dict/ │ ├── cmudict.dict │ └── filler.dict └── README.txt
- pstrain.api.parse_transcription_file(transcription_path)[source]
Parse transcription file (word-level text, not time-aligned).
- Parameters:
transcription_path (Path) – Path to transcription file
- Returns:
Dict mapping fileid -> transcript text (words only)
- Raises:
FileNotFoundError – If transcription file does not exist
UnicodeDecodeError – If file is not UTF-8 encoded
ValueError – If a nonempty line does not match a supported format
- Return type:
Supports two formats: 1. Simple:
<fileid> <word1> <word2> ...2. Sphinx:[<s>] <word1> <word2> [</s>] (<fileid>)The Sphinx begin/end silence markers are optional, following the convention used by the arpabo language-model tools. A trailing
(<fileid>)token selects this form; otherwise the first whitespace token is the file ID.
- pstrain.api.print_stats(file_path)[source]
Print statistics for a single file.
Detects file type and prints relevant statistics.
- Parameters:
file_path (Path) – Path to file
- Return type:
None
- pstrain.api.resolve_config(project_dir, *, profile_name='default', experiment='default', cli_overrides=None, user_config_path=None)[source]
Resolve built-in < user < project < experiment < CLI.
- pstrain.api.run_build_lm(train_transcripts, output_path, max_order=3, smoothing='auto')[source]
Build an ARPA language model from training transcripts.
Uses arpabo with auto mode (optimized Katz backoff) by default.
- Parameters:
- Returns:
Path to created LM file
- Return type:
- pstrain.api.run_step_cd_hmm_untied(project_dir, experiment='default', config='baseline', dry_run=False, **params)[source]
Run CD HMM untied training.
- Parameters:
- Returns:
Exit code (0 for success, non-zero for failure)
- Return type:
- pstrain.api.run_step_ci_hmm(project_dir, experiment='default', config='baseline', dry_run=False, **params)[source]
Run CI HMM training.
- Parameters:
- Returns:
Exit code (0 for success, non-zero for failure)
- Return type:
- pstrain.api.run_step_features(project_dir, experiment='default', config='baseline', dry_run=False, **params)[source]
Run feature extraction step.
- Parameters:
- Returns:
Exit code (0 for success, non-zero for failure)
- Return type:
- pstrain.api.setup_project(project_dir, transcription_path=None, audio_path=None, dictionary_path=None, phoneset_path=None, filler_dict_path=None, config_path=None, link_audio=False, clobber=False)[source]
Set up a new pstrain project.
- Parameters:
project_dir (Path) – Project directory (create if needed)
transcription_path (Path | None) – Path to transcription file
audio_path (Path | None) – Path to audio directory or file (optional)
dictionary_path (Path | None) – Path to dictionary file
phoneset_path (Path | None) – Path to phoneset file (or extract from dictionary)
filler_dict_path (Path | None) – Path to filler dictionary (optional)
config_path (Path | None) – Path to config file (or create default)
link_audio (bool) – If True and audio_path provided, symlink instead of copying
clobber (bool) – If True, overwrite existing files; if False, skip existing files
- Returns:
Dict with setup status and paths
- Return type:
- pstrain.api.step_cd_hmm_untied(project_dir, experiment='default', config='baseline', **params)[source]
Get the rule definition for CD HMM untied training.
- pstrain.api.step_ci_hmm(project_dir, experiment='default', config='baseline', **params)[source]
Get the rule definition for CI HMM training.
- pstrain.api.step_features(project_dir, experiment='default', config='baseline', **params)[source]
Get the rule definition for feature extraction.
- pstrain.api.strip_dictionary_stress(input_dict, output_dict)[source]
Strip stress, merge duplicate pronunciations, and renumber variants.
This mirrors CMUdict’s PocketSphinx conversion: entries are grouped by their base word, the first occurrence of each distinct stripped pronunciation is retained, words are sorted, and surviving variants are numbered from two. Comments, blank lines, and malformed lines are omitted.
- pstrain.api.strip_stress(phone)[source]
Remove ARPABET stress marker (trailing digit) from phone.
- Parameters:
phone (str) – Phone possibly with stress marker (e.g., “AH0”, “OW1”)
- Returns:
Phone without stress marker (e.g., “AH”, “OW”)
- Return type:
Examples
strip_stress(“AA1”) -> “AA” strip_stress(“AE0”) -> “AE” strip_stress(“HH”) -> “HH” (no change, consonants don’t have stress)
- pstrain.api.validate_file_type(path, expected, deep=False)[source]
Validate that a file matches the expected type.
- pstrain.api.validate_package_destination(model_dir, output_dir, model_name=None, *, include_dict=True, overwrite=False)[source]
Validate a package destination without changing the filesystem.
Named packages replace their complete destination. Unnamed packages replace only their generated entries and preserve unrelated entries in
output_dir. Existing generated entries must belong to a recognizable package in either case. A supported package marker establishes ownership; replacing a legacy package without one requires explicit opt-in.- Parameters:
model_dir (Path) – Source model directory
output_dir (Path) – Output directory for packaged model
model_name (str | None) – Name for the model, as one ordinary path component
include_dict (bool) – Whether packaging will replace the generated dictionary directory
overwrite (bool) – Allow replacement of a recognizable package without a marker
- Returns:
The package directory that packaging will write
- Raises:
ValueError – If the name, source relationship, or existing destination is unsafe
- Return type:
- pstrain.api.validate_project(project_dir, experiment='default')[source]
Validate project structure and files, return detailed report.
- Parameters:
- Returns:
ValidationReport with errors, warnings, and stats
- Return type:
- pstrain.api.TUTORIAL_FILENAME
The filename used for the bundled tutorial notebook.
Project Setup
Project setup implementation for pstrain.
- pstrain.lib.setup.setup_project(project_dir, transcription_path=None, audio_path=None, dictionary_path=None, phoneset_path=None, filler_dict_path=None, config_path=None, link_audio=False, clobber=False)[source]
Set up a new pstrain project.
- Parameters:
project_dir (Path) – Project directory (create if needed)
transcription_path (Path | None) – Path to transcription file
audio_path (Path | None) – Path to audio directory or file (optional)
dictionary_path (Path | None) – Path to dictionary file
phoneset_path (Path | None) – Path to phoneset file (or extract from dictionary)
filler_dict_path (Path | None) – Path to filler dictionary (optional)
config_path (Path | None) – Path to config file (or create default)
link_audio (bool) – If True and audio_path provided, symlink instead of copying
clobber (bool) – If True, overwrite existing files; if False, skip existing files
- Returns:
Dict with setup status and paths
- Return type:
Project Validation
Project validation for pstrain.
- class pstrain.lib.validate.ValidationReport(errors=<factory>, warnings=<factory>, train_utterances=0, test_utterances=0, dev_utterances=0, total_utterances=0, vocabulary_size=0, missing_words=<factory>, dictionary_entries=0, dictionary_base_words=0, phoneset_size=0, has_silence=False, missing_phones=<factory>, audio_files=0, missing_audio=<factory>)[source]
Bases:
objectValidation report with errors and stats.
- Parameters:
- __init__(errors=<factory>, warnings=<factory>, train_utterances=0, test_utterances=0, dev_utterances=0, total_utterances=0, vocabulary_size=0, missing_words=<factory>, dictionary_entries=0, dictionary_base_words=0, phoneset_size=0, has_silence=False, missing_phones=<factory>, audio_files=0, missing_audio=<factory>)
- audio_files: int = 0
- dev_utterances: int = 0
- dictionary_base_words: int = 0
- dictionary_entries: int = 0
- has_silence: bool = False
- property is_valid: bool
True if no errors.
- phoneset_size: int = 0
- test_utterances: int = 0
- total_utterances: int = 0
- train_utterances: int = 0
- vocabulary_size: int = 0
- pstrain.lib.validate.validate_files_exist(files, context='')[source]
Validate that all files in a list exist.
- Parameters:
- Raises:
FileNotFoundError – If any file does not exist
- Return type:
None
- pstrain.lib.validate.validate_project(project_dir, experiment='default')[source]
Validate project structure and files, return detailed report.
- Parameters:
- Returns:
ValidationReport with errors, warnings, and stats
- Return type:
Configuration
Canonical pstrain configuration API.
- class pstrain.lib.config.FeatureConfig(*, samprate=16000, ncep=13, nfilt=25, nfft=512, lowerf=130.0, upperf=6800.0, alpha=0.97, dither=True, seed=-1, remove_dc=True, remove_noise=True, frate=100, wlen=0.025625, feat_type='1s_c_d_dd', lifter=22, transform='dct', agc='none', cmn='batch', cmninit='40,3,-1', varnorm='no')[source]
Bases:
StrictModelAcoustic front-end parameters.
- Parameters:
- agc: Annotated[str, Field(description='Automatic gain-control mode')]
- alpha: Annotated[float, Field(description='Pre-emphasis coefficient')]
- cmn: Annotated[str, Field(description='Cepstral mean-normalization mode')]
- cmninit: Annotated[str, Field(description='Initial cepstral mean vector for live CMN')]
- dither: Annotated[bool, Field(description='Add half-bit dither to input audio')]
- feat_type: Annotated[str, Field(description='Sphinx feature stream type')]
- frate: Annotated[int, Field(gt=0, description='Feature frame rate in Hz')]
- lifter: Annotated[int, Field(ge=0, description='Cepstral lifter window')]
- lowerf: Annotated[float, Field(ge=0, description='Lower filter-bank frequency in Hz')]
- model_config = {'extra': 'forbid', 'frozen': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- ncep: Annotated[int, Field(gt=0, description='Number of cepstral coefficients')]
- nfft: Annotated[int, Field(gt=0, description='FFT size')]
- nfilt: Annotated[int, Field(gt=0, description='Number of mel filters')]
- remove_dc: Annotated[bool, Field(description='Remove DC offset from each frame')]
- remove_noise: Annotated[bool, Field(description='Remove noise with spectral subtraction')]
- samprate: Annotated[int, Field(gt=0, description='Audio sample rate in Hz')]
- seed: Annotated[int, Field(description='Seed for deterministic input dithering')]
- transform: Annotated[str, Field(description='Filter-bank transform')]
- upperf: Annotated[float, Field(gt=0, description='Upper filter-bank frequency in Hz')]
- validate_band()[source]
- Return type:
- varnorm: Annotated[str, Field(description='Cepstral variance-normalization mode')]
- wlen: Annotated[float, Field(gt=0, description='Analysis window length in seconds')]
- class pstrain.lib.config.FieldExplanation(field_path: 'str', value: 'Any', canonical_type: 'str', winner: 'Candidate', overridden: 'tuple[Candidate, ...]', default: 'Any', constraints: 'dict[str, Any]', consumer: 'str', provenance_scope: 'str')[source]
Bases:
object- Parameters:
- __init__(field_path, value, canonical_type, winner, overridden, default, constraints, consumer, provenance_scope)
- canonical_type: str
- consumer: str
- default: Any
- field_path: str
- overridden: tuple[Candidate, ...]
- provenance_scope: str
- value: Any
- winner: Candidate
- class pstrain.lib.config.OverlayDocument(*, config_version=1, profile=None, features=None, training=None, split=None, runner=None, sharding=None, alignment=None)[source]
Bases:
StrictModelCanonical user, project, or experiment field overlay.
- Parameters:
- config_version: Literal[1]
- model_config = {'extra': 'forbid', 'frozen': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pstrain.lib.config.ParameterInfo(key, type, default, description, required)[source]
Bases:
objectInformation about a configuration parameter.
- __init__(key, type, default, description, required)
- default: Any
- description: str
- key: str
- required: bool
- type: str
- class pstrain.lib.config.Profile(*, description='', features=<factory>, training=<factory>, split=<factory>, runner=<factory>, sharding=<factory>, alignment=<factory>)[source]
Bases:
StrictModelOne complete named model-training profile.
- Parameters:
description (str)
features (FeatureConfig)
training (TrainingConfig)
split (SplitConfig)
runner (RunnerConfig)
sharding (ShardingConfig)
alignment (AlignmentConfig)
- alignment: AlignmentConfig
- description: Annotated[str, Field(description='Human-readable profile purpose')]
- features: FeatureConfig
- model_config = {'extra': 'forbid', 'frozen': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- runner: RunnerConfig
- sharding: ShardingConfig
- split: SplitConfig
- training: TrainingConfig
- class pstrain.lib.config.ProfilesDocument(*, config_version=1, profiles)[source]
Bases:
StrictModelCanonical
etc/configs.yamldocument.- config_version: Literal[1]
- model_config = {'extra': 'forbid', 'frozen': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pstrain.lib.config.ResolvedConfig(profile: 'Profile', profile_name: 'str', config_version: 'int', fields: 'dict[str, FieldExplanation]', warnings: 'tuple[str, ...]' = ())[source]
Bases:
object- Parameters:
- __init__(profile, profile_name, config_version, fields, warnings=())
- benchmark_document()[source]
Return a deterministic snapshot of values and winning source kinds.
- config_version: int
- profile: Profile
- profile_name: str
- class pstrain.lib.config.RunnerConfig(*, jobs=None, nice=5)[source]
Bases:
StrictModelLocal pipeline execution policy.
- model_config = {'extra': 'forbid', 'frozen': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- nice: Annotated[int, Field(ge=0, description='Worker niceness increment')]
- class pstrain.lib.config.SplitConfig(*, train_ratio=None, test_count=None, seed=42)[source]
Bases:
StrictModelTrain/test split parameters.
- Parameters:
- model_config = {'extra': 'forbid', 'frozen': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- seed: Annotated[int, Field(description='Deterministic split seed')]
- test_count: Annotated[int | None, Field(ge=0, description='Fixed test utterance count; zero disables an additional holdout')]
- validate_choice()[source]
- Return type:
SplitConfig
- class pstrain.lib.config.TrainingConfig(*, n_state=3, skip_state=False, n_senones=200, a_beam=1e-90, b_beam=1e-10, ci=<factory>, tied=<factory>, untied=<factory>, split_variance_floor_fraction=0.0, max_skip_fraction=0.05, retry_beam_factor=10000000000.0, failed_alignment='recover', bw_checkpoint_iterations=False, arctic_a0302_zero_codebook_band=None, accept_arctic_a0587_known_skip=False, tree_state_weights=(1.0, 0.05, 0.0), tree_rotate_state_weights=True, tree_directional_questions=True, tree_ssplitmax=7, tree_ssplitthr=0.0, tree_csplitmax=2000, tree_csplitthr=0.0, tree_mwfloor=1e-08, tree_intermediate_dumps=False, question_npermute=12, question_quests_per_state=20, question_niter=1, multipron_training=True, optional_final_silence=True, untied_inventory='transcript-reachable', exclusion_schedule=<factory>)[source]
Bases:
StrictModelAcoustic-model training parameters.
- Parameters:
skip_state (bool)
ci (TrainingScheduleConfig)
tied (TrainingScheduleConfig)
untied (TrainingScheduleConfig)
split_variance_floor_fraction (Annotated[float, Strict(strict=True), Ge(ge=0), Le(le=1), _PydanticGeneralMetadata(allow_inf_nan=False)])
retry_beam_factor (Annotated[float | list[float], PlainValidator(func=~pstrain.lib.config.models._validate_retry_beam_factor, json_schema_input_type=~typing.Any), WithJsonSchema(json_schema={'anyOf': [{'type': 'number', 'exclusiveMinimum': 0}, {'type': 'array', 'items': {'type': 'number', 'exclusiveMinimum': 1}, 'minItems': 1}]}, mode=None)])
failed_alignment (Literal['recover', 'abort', 'omit'])
bw_checkpoint_iterations (bool)
accept_arctic_a0587_known_skip (bool)
tree_state_weights (Annotated[tuple[float, ...], MinLen(min_length=1)])
tree_rotate_state_weights (bool)
tree_directional_questions (bool)
tree_intermediate_dumps (bool)
multipron_training (bool)
optional_final_silence (bool)
untied_inventory (Literal['all-triphone', 'transcript-reachable', 'linear'])
exclusion_schedule (dict[str, dict[Annotated[int, FieldInfo(annotation=NoneType, required=True, metadata=[Strict(strict=True)])] | str, list[str]]])
- a_beam: Annotated[float, Field(gt=0, description='Forward alignment beam')]
- accept_arctic_a0587_known_skip: Annotated[bool, Field(description="Deprecated for live profiles: retained solely for the Arctic pin's retired off-profile provenance; live benchmark cells run exception-free")]
- arctic_a0302_zero_codebook_band: Annotated[tuple[int, int] | None, Field(description='Accepted inclusive exact-zero codebook occupancy band for the singular Arctic a0302 terminal-alignment exception')]
- b_beam: Annotated[float, Field(gt=0, description='Backward alignment beam')]
- bw_checkpoint_iterations: Annotated[bool, Field(description='Retain the compact model files from every completed Baum-Welch pass under ``iterations/NN``; costs roughly one additional model copy per pass and does not retain the much larger ``.bw-accum`` shard accumulators or change which checkpoint is loaded by training. The deprecated ``PSTRAIN_BW_CHECKPOINTS=1`` environment variable can also enable retention, but cannot disable a true profile setting')]
- ci: TrainingScheduleConfig
- exclusion_schedule: Annotated[dict[str, dict[Annotated[int, Field(strict=True)] | str, list[str]]], Field(description='Experimental stage/pass utterance exclusions')]
- failed_alignment: Annotated[Literal['recover', 'abort', 'omit'], Field(description='Action when an utterance fails to reach its final state: ``recover`` runs the wider-beam retries in ``retry_beam_factor`` and, if they all fail, reports the utterance and continues without it; ``abort`` fails the run on the first failure; and ``omit`` reports and excludes it without retrying. Skips are counted either way, and ``max_skip_fraction`` still fails the run when they stop being incidental')]
- max_skip_fraction: Annotated[float, Field(ge=0, le=1, description='Maximum skipped-update fraction')]
- model_config = {'extra': 'forbid', 'frozen': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- multipron_training: Annotated[bool, Field(description='Sum posteriors over pronunciation variants; when disabled without an explicit inventory policy, untied inventory resolves to upstream-compatible ``linear``')]
- n_senones: Annotated[int, Field(ge=1, description='Target tied-state count')]
- n_state: Annotated[int, Field(ge=1, description='Emitting states per HMM')]
- optional_final_silence: Annotated[bool, Field(description='Permit final transcript silence to consume zero frames; stock SphinxTrain requires that silence to consume at least one frame')]
- question_niter: Annotated[int, Field(ge=1, description='Question generation iterations')]
- question_npermute: Annotated[int, Field(ge=1, description='Question permutations')]
- question_quests_per_state: Annotated[int, Field(ge=1, description='Questions generated per state')]
- retry_beam_factor: Annotated[RetryBeamFactorSetting, Field(description='Factor that widens the forward beam for one retry after an utterance fails to reach its final state, or an ascending list of factors, each greater than 1 and relative to the nominal beam, tried in order until one succeeds; a retry is counted only when that attempt runs')]
- skip_state: Annotated[bool, Field(description="Enable SphinxTrain's $CFG_SKIPSTATE topology, adding an arc from each eligible emitting state to the state two positions ahead so a phone can be realized with fewer frames than states. SphinxTrain writes raw 3/1/1 weights and normalizes them on read; pstrain writes the behaviorally equivalent normalized values")]
- split_variance_floor_fraction: Annotated[float, Field(ge=0, le=1, allow_inf_nan=False, strict=True, description='Experimental variance lower bound for split training stages, as a fraction of each matching coordinate in the fixed CI-1g or CD-1g variance reference. Zero disables regularization. The reference never advances with later splits; zero reference coordinates contribute no positive floor. Select a nonzero fraction explicitly after evaluation; no universal nonzero value is assumed')]
- tied: TrainingScheduleConfig
- tree_csplitmax: Annotated[int, Field(ge=0, description='Maximum phone-context splits')]
- tree_csplitthr: Annotated[float, Field(ge=0, description='Phone-context split threshold')]
- tree_directional_questions: Annotated[bool, Field(description='Honor _L/_R tree-question suffixes; disable only for isolation measurements')]
- tree_intermediate_dumps: Annotated[bool, Field(description='Dump intermediate decision trees to worker diagnostics')]
- tree_mwfloor: Annotated[float, Field(gt=0, description='Tree mixture-weight floor')]
- tree_rotate_state_weights: Annotated[bool, Field(description='Apply target-relative tree state weights; disable only for isolation measurements')]
- tree_ssplitmax: Annotated[int, Field(ge=0, description='Maximum state splits')]
- tree_ssplitthr: Annotated[float, Field(ge=0, description='State split threshold')]
- tree_state_weights: Annotated[tuple[float, ...], Field(min_length=1, description='Decision-tree state weights')]
- untied: TrainingScheduleConfig
- untied_inventory: Annotated[Literal['all-triphone', 'transcript-reachable', 'linear'], Field(description="Untied-model phone inventory policy: ``transcript-reachable`` includes contexts reachable through every pronunciation when multipron training is enabled; upstream-compatible ``linear`` includes contexts observed through each transcript word's first pronunciation; ``all-triphone`` includes the complete phoneset cross-product")]
- classmethod validate_exclusions(value)[source]
- validate_training()[source]
- Return type:
- validate_tree_state_weights()[source]
- Return type:
- class pstrain.lib.config.TrainingScheduleConfig(*, max_iterations=10, min_iterations=1, convergence_ratio=0.001)[source]
Bases:
StrictModelConvergence controller for one Baum-Welch stage family.
- Parameters:
- convergence_ratio: Annotated[float, Field(gt=0, description="Converge after min_iterations when the finite per-frame log-likelihood increase is between zero and this many nats, inclusive. Negative or nonfinite changes do not indicate convergence. Despite the name -- kept because SphinxTrain's $CFG_CONVERGENCE_RATIO is the same signed per-frame delta -- this is an absolute difference, not a ratio. At the default, corpora of Arctic's size run all ten passes in every schedule, which is the more accurate outcome as measured; treat max_iterations as the operative control. The sphinxtrain profile carries SphinxTrain's own 0.1")]
- max_iterations: Annotated[int, Field(ge=1, description='Maximum training passes')]
- min_iterations: Annotated[int, Field(ge=1, description='Minimum training passes')]
- model_config = {'extra': 'forbid', 'frozen': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- validate_iterations()[source]
- Return type:
TrainingScheduleConfig
- pstrain.lib.config.generate_markdown_docs()[source]
Generate Markdown documentation for all config parameters.
- Returns:
Markdown string suitable for docs
- Return type:
- pstrain.lib.config.generate_rst_docs()[source]
Generate reStructuredText documentation for all config parameters.
- Returns:
RST string suitable for Sphinx docs
- Return type:
- pstrain.lib.config.get_parameter(key)[source]
Get info about a specific parameter.
- Parameters:
key (str) – Dot-separated parameter path (e.g., “audio.sample_rate”)
- Returns:
ParameterInfo or None if not found
- Return type:
ParameterInfo | None
- pstrain.lib.config.get_schema()[source]
Get the canonical profile JSON schema.
- pstrain.lib.config.list_parameters(prefix='')[source]
List all configuration parameters with descriptions.
- pstrain.lib.config.list_profiles(project_dir)[source]
- pstrain.lib.config.migrate_project(project_dir, *, check)[source]
Render or atomically write the canonical profiles document.
- pstrain.lib.config.resolve_config(project_dir, *, profile_name='default', experiment='default', cli_overrides=None, user_config_path=None)[source]
Resolve built-in < user < project < experiment < CLI.
Data Structures
Dictionary
Dictionary utilities.
Core classes: - Dictionary: Base class for pronunciation dictionaries (phoneset-agnostic) - CMUDict: Subclass with ARPABET stress handling
For Phoneset and phone mapping, see pstrain.lib.phoneset.
- class pstrain.lib.dictionary.CMUDict[source]
Bases:
DictionaryCMUDict-style dictionary with ARPABET stress handling.
Extends Dictionary with ARPABET-specific features: - Stress marker parsing and manipulation - Stress-stripped variants for training - Vowel/consonant classification
- classmethod from_file(path)[source]
Load CMUDict from file.
Overrides parent to return CMUDict instance.
- get_primary_stress_position(word)[source]
Get the syllable position (0-indexed) of primary stress.
- get_stressed_vowels(word)[source]
Get vowels with their stress levels for a word.
- Parameters:
word (str) – Word to look up
- Returns:
List of (vowel, stress_level) tuples stress_level is 0, 1, 2, or -1 if no stress marker
- Return type:
Example
get_stressed_vowels(“HELLO”) -> [(“AH”, 0), (“OW”, 1)] # for HH AH0 L OW1
- strip_stress_from_entries()[source]
Create a copy with stress markers removed from all phones.
- Returns:
New CMUDict with stress-free pronunciations
- Return type:
Note
This may create duplicate entries (e.g., “read” R IY D and R EH D both become R IY D and R EH D without stress). Duplicates are automatically deduplicated.
- class pstrain.lib.dictionary.Dictionary[source]
Bases:
objectPronunciation dictionary with Unicode and case-sensitive support.
Supports: - UTF-8 encoding - Case-sensitive words (hello != Hello != HELLO) - Sphinx-style variants: word, word(2), word(3), … - Multi-word entries: New_York, ice_cream - Any phoneset (ARPABET, IPA, X-SAMPA, custom)
- __init__()[source]
Initialize empty dictionary.
- Return type:
None
- add_entry(word, phonemes)[source]
Add pronunciation entry to dictionary.
Automatically handles duplicate pronunciations: - Same word + same pronunciation → Skip silently (true duplicate) - Same word + different pronunciation → Create variant (word(2), word(3), etc.)
- Parameters:
- Return type:
None
Examples
READ R EH D # Stored as READ READ R IY D # Automatically stored as READ(2) READ R EH D # Skipped (duplicate of first)
- contains(word)[source]
Check if word exists in dictionary (exact match, case-sensitive).
- contains_base(base_word)[source]
Check if base word exists (any variant).
- filter_to_vocabulary(vocabulary, include_variants=True)[source]
Create filtered dictionary containing only words in vocabulary.
- Parameters:
- Returns:
New Dictionary containing matched words
- Return type:
Examples
Given dict: read, read(2), hello, world vocab = {“read”, “hello”}
include_variants=True: read, read(2), hello include_variants=False: read, hello (no read(2))
vocab = {“read(2)”} include_variants=True: read(2) only (exact variant specified) include_variants=False: read(2) only
- classmethod from_file(path)[source]
Load dictionary from file.
- Parameters:
path (Path) – Path to dictionary file (UTF-8 encoded)
- Returns:
Dictionary instance
- Raises:
ValueError – If file has encoding or format errors
- Return type:
- get(word)[source]
Get pronunciation for exact word match.
- get_variants(base_word)[source]
Get all pronunciation variants for a base word.
- Parameters:
base_word (str) – Base word (without variant suffix)
- Returns:
List of pronunciations (each is a list of phonemes) Returns empty list if word not found
- Return type:
Examples
get_variants(“read”) -> [[“R”, “IY”, “D”], [“R”, “EH”, “D”]]
- merge(other)[source]
Merge another dictionary into this one.
- Parameters:
other (Dictionary) – Dictionary to merge in
- Return type:
None
Note
Uses add_entry which automatically handles: - Deduplicating identical pronunciations - Creating variants for different pronunciations - Renumbering variants sequentially
- property pronunciations: dict[str, list[list[str]]]
Get all pronunciations as a dictionary mapping base words to variant lists.
- Returns:
Dict mapping base word to list of pronunciations Each pronunciation is a list of phonemes
Examples
- {“read”: [[“R”, “IY”, “D”], [“R”, “EH”, “D”]],
“hello”: [[“HH”, “AH”, “L”, “OW”]]}
- pstrain.lib.dictionary.create_standard_filler_dict(output_path, silence_phone='SIL')[source]
Create standard filler dictionary with configurable silence phone.
- pstrain.lib.dictionary.get_filler_phones(silence_phone='SIL')[source]
Get set of filler phones.
- pstrain.lib.dictionary.get_standard_fillers(silence_phone='SIL')[source]
Get standard filler words and their phones.
- pstrain.lib.dictionary.get_standard_fillers_text(silence_phone='SIL')[source]
Get standard filler dictionary text with configurable silence phone.
Note: NO COMMENTS - PocketSphinx fails to parse comment lines correctly.
- pstrain.lib.dictionary.get_stress(phone)[source]
Get ARPABET stress level from phone.
- Parameters:
phone (str) – Phone possibly with stress marker
- Returns:
0 (no stress), 1 (primary), 2 (secondary), or None if no stress marker
- Return type:
int | None
Examples
get_stress(“AH0”) -> 0 get_stress(“OW1”) -> 1 get_stress(“AE2”) -> 2 get_stress(“HH”) -> None
- pstrain.lib.dictionary.is_vowel(phone)[source]
Check if phone is an ARPABET vowel (stress stripped).
- pstrain.lib.dictionary.strip_dictionary_stress(input_dict, output_dict)[source]
Strip stress, merge duplicate pronunciations, and renumber variants.
This mirrors CMUdict’s PocketSphinx conversion: entries are grouped by their base word, the first occurrence of each distinct stripped pronunciation is retained, words are sorted, and surviving variants are numbered from two. Comments, blank lines, and malformed lines are omitted.
- pstrain.lib.dictionary.strip_phoneset_stress(input_phoneset, output_phoneset)[source]
Strip stress from phoneset file.
- pstrain.lib.dictionary.strip_stress(phone)[source]
Remove ARPABET stress marker (trailing digit) from phone.
- Parameters:
phone (str) – Phone possibly with stress marker (e.g., “AH0”, “OW1”)
- Returns:
Phone without stress marker (e.g., “AH”, “OW”)
- Return type:
Examples
strip_stress(“AA1”) -> “AA” strip_stress(“AE0”) -> “AE” strip_stress(“HH”) -> “HH” (no change, consonants don’t have stress)
Phoneset
Phoneset handling and phone mapping utilities.
A Phoneset defines the inventory of valid phones in an acoustic model. Phone mapping functions convert between different phonetic representations.
Phone Mapping
Maps can be loaded from JSON or text files:
- JSON format:
{“AA”: “ɑ”, “AE”: “æ”, “_description”: “ignored”}
- Text format (-> delimiter):
AA -> ɑ AE -> æ # Comments start with #
Mapping types: - One-to-one: “AA” -> “ɑ” (simple substitution) - Expansion: “CH” -> “t ʃ” (one phone expands to sequence, space-separated) - Deletion: “X” -> “” (phone removed from output)
Mappings are directional (A -> B does not imply B -> A). For bidirectional conversion, provide separate forward and reverse mapping files.
Note: True one-to-many (ambiguous) mappings where one source has multiple possible targets are not supported. Each source phone maps to exactly one target (which may be a sequence or empty).
Stress Removal via Maps
- ARPABET stress can be stripped using a map:
AA0 -> AA AA1 -> AA AA2 -> AA …
This is more general than regex-based stripping and works with any phoneset that uses numeric suffixes for stress/tone.
- class pstrain.lib.phoneset.Phoneset(phones)[source]
Bases:
objectPhone inventory with validation and mapping capabilities.
Supports: - Loading from file (one phone per line) - Extracting from dictionary - Validating dictionaries against phoneset - Phone mapping between phonesets
- __init__(phones)[source]
Initialize phoneset.
- create_mapped_phoneset(mapping)[source]
Create new phoneset by mapping all phones.
- classmethod from_dictionary(dictionary, include_sil=True, silence_phone='SIL')[source]
Extract phoneset from dictionary.
- Parameters:
dictionary (Dictionary) – Dictionary to extract phones from
include_sil (bool) – If True, add silence phone
silence_phone (str) – Symbol for silence (default: “SIL”)
- Returns:
Phoneset instance
- Return type:
Self
- classmethod from_file(path)[source]
Load phoneset from file.
File format: - One phone per line - Comments start with # - UTF-8 encoding, case-sensitive
- Parameters:
path (Path) – Path to phoneset file
- Returns:
Phoneset instance
- Raises:
ValueError – If file has encoding or format errors
- Return type:
- has_sil(silence_phone='SIL')[source]
Check if phoneset includes silence phone.
- map_phone(phone, mapping, passthrough_unmapped=True)[source]
Map a single phone.
Supports: - One-to-one: “AA” -> “ɑ” produces [“ɑ”] - Expansion: “CH” -> “t ʃ” produces [“t”, “ʃ”] (splits on space) - Deletion: “X” -> “” produces []
- map_pronunciation(phones, mapping, passthrough_unmapped=True)[source]
Map a pronunciation (list of phones).
- to_file(path, silence_phone='SIL')[source]
Save phoneset to file.
- pstrain.lib.phoneset.create_stress_strip_map(vowels, stress_markers='012')[source]
Create a phone map that strips stress markers from vowels.
This is a general approach that works with any phoneset using numeric suffixes for stress (like ARPABET) or tone markers.
- Parameters:
- Returns:
{“AA0”: “AA”, “AA1”: “AA”, “AA2”: “AA”, …}
- Return type:
Phone map
Example
>>> vowels = {"AA", "AE", "AH", "AO", "AW", "AY", "EH", "ER", "EY", ... "IH", "IY", "OW", "OY", "UH", "UW"} >>> stress_map = create_stress_strip_map(vowels) >>> stress_map["AA0"] 'AA'
- pstrain.lib.phoneset.load_phone_map_json(map_file)[source]
Load phone mapping from JSON file.
JSON format:
{ "_description": "Optional (ignored)", "PHONE1": "target1", "PHONE2": "target2" }
- pstrain.lib.phoneset.load_phone_map_text(map_file)[source]
Load phone mapping from text file.
Format: SOURCE -> TARGET .. rubric:: Example
AA -> ɑ # Comment
- pstrain.lib.phoneset.reverse_phone_map(phone_map)[source]
Attempt to create reverse mapping from a phone map.
Mappings are directional by design. This utility tries to invert a mapping but may lose information: - Expansion mappings (A -> “x y”) become multiple entries (x -> A, y -> A) - Multiple sources mapping to same target: only last one preserved - Deletions (A -> “”) cannot be reversed
For reliable bidirectional conversion, provide separate forward and reverse mapping files rather than relying on this function.
Transcription
Transcription file handling for pstrain projects.
Transcription = word-level text (what was said), not time-aligned. Alignment = time-aligned phone/word boundaries (when each occurs).
- pstrain.lib.transcription.get_fileids(transcription_path)[source]
Get list of fileids from transcription file.
- Parameters:
transcription_path (Path) – Path to transcription file
- Returns:
List of file IDs (utterance identifiers)
- Raises:
FileNotFoundError – If transcription file does not exist
UnicodeDecodeError – If file is not UTF-8 encoded
- Return type:
- pstrain.lib.transcription.parse_transcription_file(transcription_path)[source]
Parse transcription file (word-level text, not time-aligned).
- Parameters:
transcription_path (Path) – Path to transcription file
- Returns:
Dict mapping fileid -> transcript text (words only)
- Raises:
FileNotFoundError – If transcription file does not exist
UnicodeDecodeError – If file is not UTF-8 encoded
ValueError – If a nonempty line does not match a supported format
- Return type:
Supports two formats: 1. Simple:
<fileid> <word1> <word2> ...2. Sphinx:[<s>] <word1> <word2> [</s>] (<fileid>)The Sphinx begin/end silence markers are optional, following the convention used by the arpabo language-model tools. A trailing
(<fileid>)token selects this form; otherwise the first whitespace token is the file ID.
Models
Base model class and model implementations.
Each model type (CI, CD) is a class that knows about its own training process, parameters, directory structure, and requirements.
Models provide metadata for the pipeline runner: - File paths (inputs, outputs) - Parameters (training settings) - Dependencies (what stages must run first)
Complete-model validation follows native value ranges and enumerations conservatively, but deliberately requires whole-token numeric spellings. Native command-line parsing accepts numeric prefixes and silently truncates fractional spellings for integer options; those lossy spellings are rejected here so a recorded front end cannot describe a value different from the one the native parser actually used.
- class pstrain.lib.model.CDModel(config='baseline')[source]
Bases:
ModelContext-Dependent (triphone) acoustic model.
- Parameters:
config (str)
- property default_topn: int
Default top-n Gaussians for this model type.
- property display_name: str
Human-readable name for the model type.
- classmethod from_string(value)[source]
Get model class from string identifier.
- Parameters:
value (str) – Model type string (e.g., “cd”, “CD”, “context-dependent”)
- Returns:
Model class (CDModel)
- Raises:
ValueError – If model type is unknown
- Return type:
- get_default_training_params()[source]
Get default training parameters for CD models.
- get_training_dependencies()[source]
Get list of dependencies required for CD model training.
- Returns:
[“ci”, “features”, “dictionary”, “split”] Note: CD models depend on CI models, so “ci” is included
- Return type:
List of dependency names
- property model_type: str
Model type identifier (e.g., “ci”, “cd”).
- class pstrain.lib.model.CIModel(config='baseline')[source]
Bases:
ModelContext-Independent (monophone) acoustic model.
- Parameters:
config (str)
- property default_topn: int
Default top-n Gaussians for this model type.
- property display_name: str
Human-readable name for the model type.
- classmethod from_string(value)[source]
Get model class from string identifier.
- Parameters:
value (str) – Model type string (e.g., “ci”, “CD”, “context-independent”)
- Returns:
Model class (CIModel)
- Raises:
ValueError – If model type is unknown
- Return type:
- get_default_training_params()[source]
Get default training parameters for CI models.
- get_training_dependencies()[source]
Get list of dependencies required for CI model training.
- Returns:
[“flat”, “features”, “dictionary”, “split”]
- Return type:
List of dependency names
- property model_type: str
Model type identifier (e.g., “ci”, “cd”).
- class pstrain.lib.model.Model(config='baseline')[source]
Bases:
ABCBase class for acoustic models.
Each model type (CI, CD) should inherit from this class and implement the abstract methods to define its specific behavior.
- Parameters:
config (str)
- __init__(config='baseline')[source]
Initialize model.
- Parameters:
config (str) – Model configuration name (e.g., “baseline”, “1g”, “lda”)
- Return type:
None
- abstract property default_topn: int
Default top-n Gaussians for this model type.
- abstract property display_name: str
Human-readable name for the model type.
- abstractmethod classmethod from_string(value)[source]
Get model class from string identifier.
- Parameters:
value (str) – Model type string (e.g., “ci”, “CD”, “context-independent”)
- Returns:
Model class
- Raises:
ValueError – If model type is unknown
- Return type:
- abstractmethod get_default_training_params()[source]
Get default training parameters for this model type.
- get_flat_dir(experiment_dir)[source]
Get the flat model directory for this model.
- get_hmm_dir(experiment_dir)[source]
Get the trained HMM model directory for this model.
- get_model_dir(experiment_dir)[source]
Get the model directory for this model.
- abstractmethod get_training_dependencies()[source]
Get list of dependencies required for training.
- abstract property model_type: str
Model type identifier (e.g., “ci”, “cd”).
- pstrain.lib.model.create_model(model_type, config='baseline')[source]
Create a model instance.
- Parameters:
- Returns:
Model instance of the specified type
- Raises:
ValueError – If model type is unknown
- Return type:
- pstrain.lib.model.get_model_class(model_type)[source]
Get model class from model type string.
- Parameters:
model_type (str) – Model type identifier (e.g., “ci”, “cd”)
- Returns:
Model class
- Raises:
ValueError – If model type is unknown
- Return type:
- pstrain.lib.model.read_ci_phones(mdef_path)[source]
Read a model’s context-independent phone inventory from its mdef.
The inventory is the set of phones the trained model actually defines. A lexicon pronunciation that uses a phone outside it cannot be loaded: the native lexicon reader drops that pronunciation, and the word then fails much later as an unresolvable transcript token.
- Parameters:
- Returns:
The base (context-independent) phone names, in mdef order.
- Raises:
FileNotFoundError – If the model definition does not exist.
ValueError – If the file is not a model definition this reader understands.
- Return type:
- pstrain.lib.model.read_complete_model_feat_params(model_dir)[source]
Require, validate, and return the complete pstrain front-end record.
Range and enumeration rejection is a conservative subset of native rejection. Numeric spelling is deliberately stricter: the complete token must parse without native-style prefix acceptance or integer truncation. Native parsing remains authoritative otherwise. This does not prove compatibility between the record and the model’s binary tensors.
Native Worker
Crash containment for the guarded native operations. See Native boundary: what is contained, and what is not for which operations are guarded, which are not, and what each exception means.
Crash containment for the contained-all-operations native surface.
One-shot wrappers execute as complete Python/CFFI operations in the helper;
BW, alignment, feature extraction, and logmath use opaque remote objects.
See docs/design/native-boundary.md for the phase contract.
Each Python process owns at most one lazily spawned helper process, created
through an explicit spawn context and reused across calls. The helper
services one request at a time; the owning process classifies every outcome
(clean result, native error return, signal death, nonzero exit, clean exit
mid-request, transport EOF) into the exception hierarchy below.
Diagnostics are captured worker-side: the helper redirects its own stderr –
where every E_INFO/E_ERROR/E_FATAL line lands – into a private
temporary file, and the owner attaches the tail of that file to the raised
exception.
- pstrain.lib.native_worker.GUARDED_OPERATIONS = frozenset({'make_quests', 'mdef_gen_ci', 'object_call', 'object_close', 'object_create', 'prune_tree', 'python_call', 'stdout_redirect', 'stdout_restore'})
Worker protocol operations accepted from containment wrappers and proxies.
- class pstrain.lib.native_worker.NativeObjectProxy(module_name, target_name, args, kwargs, inputs=())[source]
Bases:
objectProxy a stateful CFFI-backed Python object living in the helper.
- Parameters:
- __init__(module_name, target_name, args, kwargs, inputs=())[source]
- call(method_name, *args, **kwargs)[source]
- close()[source]
- Return type:
None
- exception pstrain.lib.native_worker.PstrainError[source]
Bases:
RuntimeErrorBase class for public pstrain failures.
- exception pstrain.lib.native_worker.PstrainInvalidInputError(operation, input_paths, diagnostic='', returncode=None)[source]
Bases:
PstrainNativeErrorPython-side validation rejected the request before it reached a worker.
- exception pstrain.lib.native_worker.PstrainNativeCrashError(*args, signal, **kwargs)[source]
Bases:
PstrainNativeErrorThe native worker died from a signal.
- Parameters:
args (Any)
signal (int)
kwargs (Any)
- Return type:
None
- exception pstrain.lib.native_worker.PstrainNativeError(operation, input_paths, diagnostic='', returncode=None)[source]
Bases:
PstrainErrorA contained native operation failed.
- Parameters:
- Return type:
None
- __init__(operation, input_paths, diagnostic='', returncode=None)[source]
- exception pstrain.lib.native_worker.PstrainNativeFatalError(operation, input_paths, diagnostic='', returncode=None)[source]
Bases:
PstrainNativeErrorThe native worker exited nonzero or returned a diagnosed failure.
- exception pstrain.lib.native_worker.PstrainWorkerError[source]
Bases:
PstrainErrorThe contained worker or process-pool infrastructure is unavailable.
- exception pstrain.lib.native_worker.PstrainWorkerProtocolError(operation, input_paths, diagnostic='', returncode=None)[source]
Bases:
PstrainNativeErrorThe worker violated the request/response protocol.
Raised in particular when the helper exits cleanly (status 0) with a request still outstanding: a successful-looking exit is not a result.
- pstrain.lib.native_worker.call(operation, arguments, inputs)[source]
Execute one guarded native operation in this process’s helper.
- Parameters:
- Raises:
PstrainInvalidInputError – The operation is not routed through the helper. Requests whose serialized representation exceeds 64 KiB are also rejected before anything is sent.
PstrainWorkerError – The helper could not be started, or timed out.
PstrainNativeCrashError – The helper died on a signal.
PstrainWorkerProtocolError – The helper exited cleanly mid-request.
PstrainNativeFatalError – The operation failed with a diagnostic.
PstrainNativeError – The operation failed without one.
- Return type:
- pstrain.lib.native_worker.call_python(module_name, target_name, args, kwargs, inputs=())[source]
Run a complete Python/CFFI operation in the contained helper.
- pstrain.lib.native_worker.close_helper_before_children_are_joined()[source]
Arrange for this process’s helper to be closed early enough at exit.
Call in any process that multiprocessing itself started – a pool worker, or any other
BaseProcess– before it can spawn a helper. Calling it more than once in the same process does nothing further.BaseProcess._bootstraprunsmultiprocessing.util._exit_functionwhen the process body returns, and that joins every non-daemon child with no timeout. The helper is such a child, and it waits on its request pipe until it is told to exit, so a process that leaves one running parks inwaitpidforever, and whatever is waiting on that process parks with it. Registering_shutdownwithatexit()is too late: ordinaryatexithandlers do not run untilsys.exitafterwards. Amultiprocessing.util.Finalizeruns inside_exit_functionitself, ahead of the joins, which is early enough.- Return type:
None
- pstrain.lib.native_worker.contained(function)[source]
Route a complete stateless public CFFI operation through the helper.
Guarded Logmath Wrapper
LogMath is the supported wrapper for native log-domain arithmetic. Other
contents of the private _pstrainc module, including raw library handles and
symbols, are implementation details and are not supported application APIs.
Low-level CFFI bindings to libpstrainc.
The supported model-I/O and logmath operations exported here are coarse
operations routed through the persistent native worker. get_lib() and
dynamic raw-symbol access are private implementation/testing escape hatches;
applications should use the higher-level wrappers in pstrain.lib.
Implementation is organized into submodules: - _cffi.cdef: C type definitions - _cffi.core: FFI initialization and helpers - _cffi.io: Model file I/O - _cffi.logmath: Log-domain math wrapper
- class pstrain.lib._pstrainc.LogMath(base=1.0001, shift=0, use_table=True)[source]
Bases:
objectLog-domain math using C library for numerical stability.
This wraps the C logmath functions which use integer log representations for fast, stable computation of probabilities in log domain.
- __del__()[source]
Free C resources.
- Return type:
None
- __init__(base=1.0001, shift=0, use_table=True)[source]
Initialize log math.
- add(logp, logq)[source]
Add two probabilities in log domain.
Computes log(exp(logp) + exp(logq)) efficiently.
- property base: float
Get the log base.
- exp(logp)[source]
Convert log-domain value back to probability.
- pstrain.lib._pstrainc.get_ffi()[source]
Get the FFI instance.
- Return type:
FFI
- pstrain.lib._pstrainc.get_lib()[source]
Get the loaded library with all C functions.
- Return type:
- pstrain.lib._pstrainc.path_or_null(path)[source]
Convert a path to bytes, or return NULL if None.
Helper to reduce boilerplate when passing optional path arguments to C.
- pstrain.lib._pstrainc.read_dnom(filename)[source]
Read Gaussian density counts from S3 format file.
- pstrain.lib._pstrainc.read_gau(filename)[source]
Read Gaussian parameters from S3 format file.
- pstrain.lib._pstrainc.read_mixw(filename)[source]
Read mixture weights as probabilities, normalizing occupied rows.
- pstrain.lib._pstrainc.read_mixw_counts(filename)[source]
Read stored mixture-weight values without normalization.
- pstrain.lib._pstrainc.read_tmat(filename)[source]
Read transition matrices as probabilities, normalizing occupied rows.
- pstrain.lib._pstrainc.read_tmat_counts(filename)[source]
Read stored transition-matrix values without normalization.
- Parameters:
filename (str) – Input file path
- Returns:
Tuple of (tmat_array, n_tmat, n_state) tmat_array has shape (n_tmat, n_state-1, n_state)
- Raises:
RuntimeError – If file cannot be read
- Return type:
- pstrain.lib._pstrainc.write_dnom(filename, dnom)[source]
Write Gaussian density counts to S3 format file.
These counts track how often each Gaussian is used during BW training. Used by inc_comp to decide which Gaussians to split.
- pstrain.lib._pstrainc.write_gau(filename, gau)[source]
Write Gaussian parameters (means or variances) to S3 format file.
- pstrain.lib._pstrainc.write_mixw(filename, mixw)[source]
Write mixture weights to S3 format file.
- pstrain.lib._pstrainc.write_tmat(filename, tmat)[source]
Write transition matrices to S3 format file.
- Parameters:
filename (str) – Output file path
tmat (npt.NDArray[np.float32]) – Transition matrices, either square (n_tmat, n_state, n_state) including an exit-state row, or stored rectangular shape (n_tmat, n_state-1, n_state). The exit-state row, when present, is excluded; values are stored without row normalization.
- Returns:
0 on success, non-zero on error
- Return type: