blt-patch-view / patch_view /constraints.py
3v324v23's picture
Convert from submodule to subdirectory
d9af7ee
Raw History Blame Contribute Delete
5.45 kB
"""Constraint metadata and helper utilities for patch selection UI."""
from __future__ import annotations
from dataclasses import dataclass
from typing import Dict, Iterable, Mapping
@dataclass(frozen=True)
class MetricRange:
"""Slider bounds for a specific metric."""
default: float
minimum: float
maximum: float
step: float
label: str | None = None
description: str | None = None
@dataclass(frozen=True)
class ConstraintDefinition:
"""UI metadata + semantic description for a constraint."""
name: str
label: str
description: str
entropy: MetricRange
perplexity: MetricRange
guard: bool = False
def range_for_metric(self, metric_name: str) -> MetricRange:
"""Return the metric range for the given metric label."""
key = metric_name.lower()
if key not in ("entropy", "perplexity"):
raise ValueError(f"Unknown metric '{metric_name}'")
return getattr(self, key)
# Constraint slider definitions
CONSTRAINT_DEFINITIONS: Dict[str, ConstraintDefinition] = {
"global": ConstraintDefinition(
name="global",
label="Global Threshold",
description="H(t) > θ",
entropy=MetricRange(default=5.0, minimum=0.0, maximum=20.0, step=0.5),
perplexity=MetricRange(default=300.0, minimum=0.0, maximum=2000.0, step=5.0),
),
"additive": ConstraintDefinition(
name="additive",
label="Additive Monotonic",
description="H(t) - H(t-1) > θ",
entropy=MetricRange(default=0.5, minimum=0.0, maximum=10.0, step=0.1),
perplexity=MetricRange(default=5.0, minimum=0.0, maximum=100.0, step=1.0),
),
"multiplicative": ConstraintDefinition(
name="multiplicative",
label="Multiplicative Monotonic",
description="H(t) / H(t-1) > θ",
entropy=MetricRange(default=1.5, minimum=1.0, maximum=5.0, step=0.1),
perplexity=MetricRange(default=1.5, minimum=1.0, maximum=5.0, step=0.1),
),
"additive_min": ConstraintDefinition(
name="additive_min",
label="Additive from Min",
description="H(t) - min(H) > θ",
entropy=MetricRange(default=2.0, minimum=0.0, maximum=10.0, step=0.1),
perplexity=MetricRange(default=20.0, minimum=0.0, maximum=100.0, step=1.0),
),
"multiplicative_min": ConstraintDefinition(
name="multiplicative_min",
label="Multiplicative from Min",
description="H(t) / min(H) > θ",
entropy=MetricRange(default=2.0, minimum=1.0, maximum=10.0, step=0.1),
perplexity=MetricRange(default=3.0, minimum=1.0, maximum=10.0, step=0.1),
),
"cumulative": ConstraintDefinition(
name="cumulative",
label="Cumulative Sum (Min)",
description="Σ H(t) > θ (boundary after exceeding)",
entropy=MetricRange(default=10.0, minimum=0.0, maximum=100.0, step=1.0),
perplexity=MetricRange(default=100.0, minimum=0.0, maximum=1000.0, step=10.0),
),
"cumulative_max": ConstraintDefinition(
name="cumulative_max",
label="Cumulative Sum (Max)",
description="Σ H(t) ≤ θ (boundary before exceeding)",
entropy=MetricRange(default=50.0, minimum=0.0, maximum=500.0, step=5.0),
perplexity=MetricRange(default=500.0, minimum=0.0, maximum=10000.0, step=50.0),
),
}
MIN_GUARD_DEFINITION = ConstraintDefinition(
name="min_metric",
label="Minimum Metric for Boundary",
description="AND guard: boundaries only trigger once metric exceeds this value",
entropy=MetricRange(
default=0.0,
minimum=0.0,
maximum=20.0,
step=0.1,
label="Minimum Entropy for Boundary",
description="Guard: boundaries only trigger once entropy exceeds this value",
),
perplexity=MetricRange(
default=0.0,
minimum=0.0,
maximum=2000.0,
step=5.0,
label="Minimum Perplexity for Boundary",
description="Guard: boundaries only trigger once perplexity exceeds this value",
),
guard=True,
)
def iter_constraint_definitions() -> Iterable[ConstraintDefinition]:
"""Iterate definitions in sorted order by display label."""
return sorted(CONSTRAINT_DEFINITIONS.values(), key=lambda c: c.label)
def get_constraint_definition(name: str) -> ConstraintDefinition:
"""Return definition by name."""
if name == MIN_GUARD_DEFINITION.name:
return MIN_GUARD_DEFINITION
try:
return CONSTRAINT_DEFINITIONS[name]
except KeyError as exc:
raise KeyError(f"Unknown constraint '{name}'") from exc
def default_thresholds_for_metric(metric_name: str) -> Mapping[str, float]:
"""Return default slider values for each constraint given the metric."""
return {
definition.name: definition.range_for_metric(metric_name).default
for definition in iter_constraint_definitions()
}
def guard_range_for_metric(metric_name: str) -> MetricRange:
"""Return metric range for the guard slider."""
metric_range = MIN_GUARD_DEFINITION.range_for_metric(metric_name)
if metric_range.label and metric_range.description:
return metric_range
return MetricRange(
default=metric_range.default,
minimum=metric_range.minimum,
maximum=metric_range.maximum,
step=metric_range.step,
label=MIN_GUARD_DEFINITION.label,
description=MIN_GUARD_DEFINITION.description,
)