def from_pimo_result(
        cls: type["AUPIMOResult"],
        pimo_result: PIMOResult,
        fpr_bounds: tuple[float, float],
        num_thresholds_auc: int,
        aupimos: torch.Tensor,
    ) -> "AUPIMOResult":
        """Create AUPIMO result from PIMO result.

        Args:
            pimo_result: Source PIMO result object.
            fpr_bounds: Tuple of (lower, upper) bounds for FPR range.
            num_thresholds_auc: Number of thresholds for AUPIMO computation.
                Note this differs from PIMO curve thresholds.
            aupimos: AUPIMO scores, one per image.

        Returns:
            New AUPIMO result object.

        Raises:
            TypeError: If inputs are invalid or inconsistent.

        Example:
            >>> pimo_result = PIMOResult(...)  # doctest: +SKIP
            >>> aupimos = torch.rand(5)  # 5 images
            >>> result = AUPIMOResult.from_pimo_result(  # doctest: +SKIP
            ...     pimo_result=pimo_result,
            ...     fpr_bounds=(0.0, 0.3),
            ...     num_thresholds_auc=100,
            ...     aupimos=aupimos
            ... )
        """
        if pimo_result.per_image_tprs.shape[0] != aupimos.shape[0]:
            msg = (
                f"Invalid {cls.__name__} object. "
                f"Attributes have inconsistent shapes: "
                f"there are {pimo_result.per_image_tprs.shape[0]} PIMO curves "
                f"but {aupimos.shape[0]} AUPIMO scores."
            )
            raise TypeError(msg)

        if not torch.isnan(aupimos[pimo_result.image_classes == 0]).all():
            msg = "Expected all normal images to have NaN AUPIMOs, but some have non-NaN values."
            raise TypeError(msg)

        if torch.isnan(aupimos[pimo_result.image_classes == 1]).any():
            msg = "Expected all anomalous images to have valid AUPIMOs (not nan), but some have NaN values."
            raise TypeError(msg)

        fpr_lower_bound, fpr_upper_bound = fpr_bounds
        # recall: fpr upper/lower bounds are same as thresh lower/upper bounds
        _, thresh_lower_bound, __ = pimo_result.thresh_at(fpr_upper_bound)
        _, thresh_upper_bound, __ = pimo_result.thresh_at(fpr_lower_bound)
        # `_` is threshold's index, `__` is actual fpr value
        return cls(
            fpr_lower_bound=fpr_lower_bound,
            fpr_upper_bound=fpr_upper_bound,
            num_thresholds=num_thresholds_auc,
            thresh_lower_bound=float(thresh_lower_bound),
            thresh_upper_bound=float(thresh_upper_bound),
            aupimos=aupimos,
        )
Create AUPIMO result from PIMO result.

        Args:
            pimo_result: Source PIMO result object.
            fpr_bounds: Tuple of (lower, upper) bounds for FPR range.
            num_thresholds_auc: Number of thresholds for AUPIMO computation.
                Note this differs from PIMO curve thresholds.
            aupimos: AUPIMO scores, one per image.

        Returns:
            New AUPIMO result object.

        Raises:
            TypeError: If inputs are invalid or inconsistent.

        Example:
            >>> pimo_result = PIMOResult(...)  # doctest: +SKIP
            >>> aupimos = torch.rand(5)  # 5 images
            >>> result = AUPIMOResult.from_pimo_result(  # doctest: +SKIP
            ...     pimo_result=pimo_result,
            ...     fpr_bounds=(0.0, 0.3),
            ...     num_thresholds_auc=100,
            ...     aupimos=aupimos
            ... )
        
src/anomalib/metrics/pimo/dataclasses.py