Source code for pyfcstm.diagnostics.analyzers.thresholds

"""Threshold-based design-health diagnostics."""

from typing import TYPE_CHECKING, Iterable, List

from ...utils.validate import ModelDiagnostic

if TYPE_CHECKING:  # pragma: no cover
    from ..inspect import ModelMetrics, StateInfo


[docs] def collect_threshold_warnings( states: Iterable['StateInfo'], metrics: 'ModelMetrics', *, deep_hierarchy_threshold: int, large_composite_threshold: int, var_to_leaf_ratio_threshold: float, ) -> List[ModelDiagnostic]: """Collect warnings controlled by inspect-model threshold options.""" states = list(states) diagnostics: List[ModelDiagnostic] = [] diagnostics.extend(_high_var_to_leaf_ratio_warnings( metrics, var_to_leaf_ratio_threshold, states, )) diagnostics.extend(_deep_hierarchy_warnings( states, metrics, deep_hierarchy_threshold, )) diagnostics.extend(_large_composite_warnings( states, large_composite_threshold, )) return diagnostics
def _high_var_to_leaf_ratio_warnings(metrics, threshold, states) -> List[ModelDiagnostic]: actual = metrics.var_to_leaf_ratio if actual <= threshold: return [] anchor_state = states[0] if states else None return [ModelDiagnostic( code='W_HIGH_VAR_TO_LEAF_RATIO', span=getattr(anchor_state, 'span', None), severity='warning', message=( f'Variable-to-leaf ratio {actual!r} exceeds threshold ' f'{threshold!r}.' ), refs={ 'n_vars': metrics.n_variables, 'n_leaf_states': metrics.n_states_leaf, 'actual': actual, 'threshold': threshold, }, )] def _deep_hierarchy_warnings(states, metrics, threshold) -> List[ModelDiagnostic]: actual = metrics.max_hierarchy_depth if actual <= threshold: return [] deepest = [ state.path for state in states if state.path.count('.') == actual ] deepest_path = sorted(deepest)[0] if deepest else '' deepest_state = next((state for state in states if state.path == deepest_path), None) return [ModelDiagnostic( code='W_DEEP_HIERARCHY', span=getattr(deepest_state, 'span', None), severity='warning', message=( f'Hierarchy depth {actual!r} exceeds threshold ' f'{threshold!r}.' ), refs={ 'max_depth': actual, 'deepest_path': deepest_path, 'actual': actual, 'threshold': threshold, }, )] def _large_composite_warnings(states, threshold) -> List[ModelDiagnostic]: diagnostics: List[ModelDiagnostic] = [] for state in states: if not state.is_composite: continue actual = len(state.substates) if actual <= threshold: continue diagnostics.append(ModelDiagnostic( code='W_LARGE_COMPOSITE', span=state.span, severity='warning', message=( f'Composite state {state.path!r} has {actual!r} direct ' f'children, exceeding threshold {threshold!r}.' ), refs={ 'composite_path': state.path, 'n_children': actual, 'actual': actual, 'threshold': threshold, }, )) return diagnostics