Spaces:
Running on Zero
Running on Zero
| """Redundancy removal for validated GCMD classification records.""" | |
| from __future__ import annotations | |
| from pydantic import BaseModel, ConfigDict, Field | |
| from gcmd_classifier.models import ClassificationRecord, OutputWarning | |
| from gcmd_classifier.vocabulary.index import VocabularyIndex | |
| _STAGE = "redundancy_removal" | |
| class RedundancyResult(BaseModel): | |
| """Non-redundant classifications plus structured redundancy diagnostics.""" | |
| model_config = ConfigDict(extra="forbid", frozen=True) | |
| classifications: tuple[ClassificationRecord, ...] = Field(default_factory=tuple) | |
| removed: tuple[ClassificationRecord, ...] = Field(default_factory=tuple) | |
| warnings: tuple[OutputWarning, ...] = Field(default_factory=tuple) | |
| def retained_count(self) -> int: | |
| """Number of retained classifications.""" | |
| return len(self.classifications) | |
| def removed_count(self) -> int: | |
| """Number of removed redundant classifications.""" | |
| return len(self.removed) | |
| def remove_redundant_classifications( | |
| records: tuple[ClassificationRecord, ...] | list[ClassificationRecord], | |
| vocabulary: VocabularyIndex, | |
| ) -> RedundancyResult: | |
| """Remove exact duplicates and branch-provenance ancestor redundancy.""" | |
| retained: list[ClassificationRecord] = [] | |
| removed: list[ClassificationRecord] = [] | |
| warnings: list[OutputWarning] = [] | |
| seen_uuids: dict[str, ClassificationRecord] = {} | |
| seen_paths: dict[str, ClassificationRecord] = {} | |
| for record in records: | |
| if record.UUID in seen_uuids: | |
| removed.append(record) | |
| warnings.append( | |
| _warning( | |
| "DUPLICATE_UUID_REMOVED", | |
| "Duplicate UUID classification was removed.", | |
| record=record, | |
| details={"duplicate_of_branch_id": seen_uuids[record.UUID].branch_id}, | |
| ) | |
| ) | |
| continue | |
| if record.canonical_path in seen_paths: | |
| removed.append(record) | |
| warnings.append( | |
| _warning( | |
| "DUPLICATE_CANONICAL_PATH_REMOVED", | |
| "Duplicate canonical-path classification was removed.", | |
| record=record, | |
| details={"duplicate_of_branch_id": seen_paths[record.canonical_path].branch_id}, | |
| ) | |
| ) | |
| continue | |
| seen_uuids[record.UUID] = record | |
| seen_paths[record.canonical_path] = record | |
| retained.append(record) | |
| retained, ancestry_removed, ancestry_warnings = _remove_branch_ancestors(retained, vocabulary) | |
| removed.extend(ancestry_removed) | |
| warnings.extend(ancestry_warnings) | |
| return RedundancyResult( | |
| classifications=tuple(retained), | |
| removed=tuple(removed), | |
| warnings=tuple(warnings), | |
| ) | |
| def _remove_branch_ancestors( | |
| records: list[ClassificationRecord], | |
| vocabulary: VocabularyIndex, | |
| ) -> tuple[list[ClassificationRecord], list[ClassificationRecord], list[OutputWarning]]: | |
| to_remove: set[int] = set() | |
| removed: list[ClassificationRecord] = [] | |
| warnings: list[OutputWarning] = [] | |
| for ancestor_index, ancestor in enumerate(records): | |
| for descendant_index, descendant in enumerate(records): | |
| if ancestor_index == descendant_index: | |
| continue | |
| if not vocabulary.is_ancestor(ancestor.UUID, descendant.UUID): | |
| continue | |
| relation = _branch_relation(ancestor.branch_id, descendant.branch_id) | |
| if relation == "same_lineage": | |
| if ancestor_index not in to_remove: | |
| to_remove.add(ancestor_index) | |
| removed.append(ancestor) | |
| warnings.append( | |
| _warning( | |
| "ANCESTOR_REMOVED_SAME_BRANCH", | |
| ( | |
| "Ancestor classification was removed because a deeper descendant " | |
| "exists in the same branch lineage." | |
| ), | |
| record=ancestor, | |
| details={ | |
| "descendant_uuid": descendant.UUID, | |
| "descendant_branch_id": descendant.branch_id, | |
| }, | |
| ) | |
| ) | |
| elif relation == "independent": | |
| warnings.append( | |
| _warning( | |
| "ANCESTOR_PRESERVED_INDEPENDENT_BRANCH", | |
| ( | |
| "Ancestor classification was preserved because branch provenance " | |
| "indicates an independent classification decision." | |
| ), | |
| record=ancestor, | |
| details={ | |
| "descendant_uuid": descendant.UUID, | |
| "descendant_branch_id": descendant.branch_id, | |
| }, | |
| ) | |
| ) | |
| else: | |
| warnings.append( | |
| _warning( | |
| "ANCESTOR_PRESERVED_INSUFFICIENT_PROVENANCE", | |
| ( | |
| "Ancestor classification was preserved because branch provenance " | |
| "was insufficient to prove redundancy." | |
| ), | |
| record=ancestor, | |
| details={ | |
| "descendant_uuid": descendant.UUID, | |
| "descendant_branch_id": descendant.branch_id, | |
| }, | |
| ) | |
| ) | |
| retained = [record for index, record in enumerate(records) if index not in to_remove] | |
| return retained, removed, warnings | |
| def _branch_relation(ancestor_branch_id: str | None, descendant_branch_id: str | None) -> str: | |
| if not ancestor_branch_id or not descendant_branch_id: | |
| return "insufficient" | |
| if ancestor_branch_id == descendant_branch_id: | |
| return "same_lineage" | |
| if descendant_branch_id.startswith(f"{ancestor_branch_id}/"): | |
| return "same_lineage" | |
| if ancestor_branch_id.startswith(f"{descendant_branch_id}/"): | |
| return "same_lineage" | |
| return "independent" | |
| def _warning( | |
| code: str, | |
| message: str, | |
| *, | |
| record: ClassificationRecord, | |
| details: dict | None = None, | |
| ) -> OutputWarning: | |
| warning_details = { | |
| "UUID": record.UUID, | |
| "canonical_path": record.canonical_path, | |
| "branch_id": record.branch_id, | |
| } | |
| if details: | |
| warning_details.update(details) | |
| return OutputWarning( | |
| code=code, | |
| message=message, | |
| stage=_STAGE, | |
| details=warning_details, | |
| ) | |