o
    .wi)                     @   s   d dl Z d dlmZ d dl mZmZmZ d dlmZ d dlm	Z	 d dl
mZ d dlmZmZ d dlmZ e jr=d d	lmZ esBd
gZG dd deZdS )    N)Sequence)AnyOptionalUnion)Tensor)Metric)_MATPLOTLIB_AVAILABLE)_AX_TYPE_PLOT_OUT_TYPE)WrapperMetric)ModuleClasswiseWrapper.plotc                       sd  e Zd ZU dZeed< eee  ed< 			d&dedeee  dee dee ddf
 fdd	Z	e
dee fd
dZdedeeef fddZdedeeef fddZdededefddZdededdfddZdeeef fddZd'ddZ	d(deeeee f  dee defddZdedeed f f fd!d"Zded#eddf fd$d%Z  ZS ))ClasswiseWrappera  Wrapper metric for altering the output of classification metrics.

    This metric works together with classification metrics that returns multiple values (one value per class) such that
    label information can be automatically included in the output.

    Args:
        metric: base metric that should be wrapped. It is assumed that the metric outputs a single
            tensor that is split along the first dimension.
        labels: list of strings indicating the different classes.
        prefix: string that is prepended to the metric names.
        postfix: string that is appended to the metric names.

    Example::
        Basic example where the output of a metric is unwrapped into a dictionary with the class index as keys:

        >>> from torch import randint, randn
        >>> from torchmetrics.wrappers import ClasswiseWrapper
        >>> from torchmetrics.classification import MulticlassAccuracy
        >>> metric = ClasswiseWrapper(MulticlassAccuracy(num_classes=3, average=None))
        >>> preds = randn(10, 3).softmax(dim=-1)
        >>> target = randint(3, (10,))
        >>> metric(preds, target)  # doctest: +NORMALIZE_WHITESPACE
        {'multiclassaccuracy_0': tensor(0.5000),
         'multiclassaccuracy_1': tensor(0.7500),
         'multiclassaccuracy_2': tensor(0.)}

    Example::
        Using custom name via prefix and postfix:

        >>> from torch import randint, randn
        >>> from torchmetrics.wrappers import ClasswiseWrapper
        >>> from torchmetrics.classification import MulticlassAccuracy
        >>> metric_pre = ClasswiseWrapper(MulticlassAccuracy(num_classes=3, average=None), prefix="acc-")
        >>> metric_post = ClasswiseWrapper(MulticlassAccuracy(num_classes=3, average=None), postfix="-acc")
        >>> preds = randn(10, 3).softmax(dim=-1)
        >>> target = randint(3, (10,))
        >>> metric_pre(preds, target)  # doctest: +NORMALIZE_WHITESPACE
        {'acc-0': tensor(0.3333), 'acc-1': tensor(0.6667), 'acc-2': tensor(0.)}
        >>> metric_post(preds, target)  # doctest: +NORMALIZE_WHITESPACE
        {'0-acc': tensor(0.3333), '1-acc': tensor(0.6667), '2-acc': tensor(0.)}

    Example::
        Providing labels as a list of strings:

        >>> from torch import randint, randn
        >>> from torchmetrics.wrappers import ClasswiseWrapper
        >>> from torchmetrics.classification import MulticlassAccuracy
        >>> metric = ClasswiseWrapper(
        ...    MulticlassAccuracy(num_classes=3, average=None),
        ...    labels=["horse", "fish", "dog"]
        ... )
        >>> preds = randn(10, 3).softmax(dim=-1)
        >>> target = randint(3, (10,))
        >>> metric(preds, target)  # doctest: +NORMALIZE_WHITESPACE
        {'multiclassaccuracy_horse': tensor(0.),
         'multiclassaccuracy_fish': tensor(0.3333),
         'multiclassaccuracy_dog': tensor(0.4000)}

    Example::
        Classwise can also be used in combination with :class:`~torchmetrics.MetricCollection`. In this case, everything
        will be flattened into a single dictionary:

        >>> from torch import randint, randn
        >>> from torchmetrics import MetricCollection
        >>> from torchmetrics.wrappers import ClasswiseWrapper
        >>> from torchmetrics.classification import MulticlassAccuracy, MulticlassRecall
        >>> labels = ["horse", "fish", "dog"]
        >>> metric = MetricCollection(
        ...     {'multiclassaccuracy': ClasswiseWrapper(MulticlassAccuracy(num_classes=3, average=None), labels),
        ...     'multiclassrecall': ClasswiseWrapper(MulticlassRecall(num_classes=3, average=None), labels)}
        ... )
        >>> preds = randn(10, 3).softmax(dim=-1)
        >>> target = randint(3, (10,))
        >>> metric(preds, target)  # doctest: +NORMALIZE_WHITESPACE
        {'multiclassaccuracy_horse': tensor(0.6667),
         'multiclassaccuracy_fish': tensor(0.3333),
         'multiclassaccuracy_dog': tensor(0.5000),
         'multiclassrecall_horse': tensor(0.6667),
         'multiclassrecall_fish': tensor(0.3333),
         'multiclassrecall_dog': tensor(0.5000)}

    metriclabelsNprefixpostfixreturnc                    s   t    t|tstd| || _|d ur-t|tr&tdd |D s-td| || _|d ur@t|t	s@td| || _
|d urSt|t	sStd| || _d| _d S )NzNExpected argument `metric` to be an instance of `torchmetrics.Metric` but got c                 s   s    | ]}t |tV  qd S )N)
isinstancestr).0lab r   \/home/ubuntu/sommelier/.venv/lib/python3.10/site-packages/torchmetrics/wrappers/classwise.py	<genexpr>   s    z,ClasswiseWrapper.__init__.<locals>.<genexpr>zLExpected argument `labels` to either be `None` or a list of strings but got zCExpected argument `prefix` to either be `None` or a string but got zDExpected argument `postfix` to either be `None` or a string but got    )super__init__r   r   
ValueErrorr   listallr   r   _prefix_postfix_update_count)selfr   r   r   r   	__class__r   r   r   w   s   

$
zClasswiseWrapper.__init__c                 C   s   | j jS )z*Return if the metric is higher the better.)r   higher_is_betterr$   r   r   r   r'      s   z!ClasswiseWrapper.higher_is_betterkwargsc                 K   s   | j jdi |S )zFilter kwargs for the metric.Nr   )r   _filter_kwargs)r$   r)   r   r   r   r*      s   zClasswiseWrapper._filter_kwargsxc                    sx   | j s| js| jjj  dd n
| j pd| jpd | jdu r. fddt|D S  fddt| j|D S )z1Convert output to dictionary with labels as keys._ Nc                    "   i | ]\}} |   |qS r   r   )r   ivalr   r   r   r   
<dictcomp>      " z4ClasswiseWrapper._convert_output.<locals>.<dictcomp>c                    r.   r   r   )r   r   r0   r1   r   r   r2      r3   )	r!   r"   r   r&   __name__lowerr   	enumeratezip)r$   r+   r   r1   r   _convert_output   s   


z ClasswiseWrapper._convert_outputargsc                 O   s   |  | j|i |S )z2Calculate on batch and accumulate to global state.)r8   r   r$   r9   r)   r   r   r   forward      zClasswiseWrapper.forwardc                 O   s   | j j|i | dS )zUpdate state.N)r   updater:   r   r   r   r=      r<   zClasswiseWrapper.updatec                 C   s   |  | j S )zCompute metric.)r8   r   computer(   r   r   r   r>      s   zClasswiseWrapper.computec                 C   s   | j   dS )zReset metric.N)r   resetr(   r   r   r   r?      s   zClasswiseWrapper.resetr0   axc                 C   s   |  ||S )a  Plot a single or multiple values from the metric.

        Args:
            val: Either a single result from calling `metric.forward` or `metric.compute` or a list of these results.
                If no value is provided, will automatically call `metric.compute` and plot that result.
            ax: An matplotlib axis object. If provided will add plot to that axis

        Returns:
            Figure and Axes object

        Raises:
            ModuleNotFoundError:
                If `matplotlib` is not installed

        .. plot::
            :scale: 75

            >>> # Example plotting a single value
            >>> import torch
            >>> from torchmetrics.wrappers import ClasswiseWrapper
            >>> from torchmetrics.classification import MulticlassAccuracy
            >>> metric = ClasswiseWrapper(MulticlassAccuracy(num_classes=3, average=None))
            >>> metric.update(torch.randint(3, (20,)), torch.randint(3, (20,)))
            >>> fig_, ax_ = metric.plot()

        .. plot::
            :scale: 75

            >>> # Example plotting multiple values
            >>> import torch
            >>> from torchmetrics.wrappers import ClasswiseWrapper
            >>> from torchmetrics.classification import MulticlassAccuracy
            >>> metric = ClasswiseWrapper(MulticlassAccuracy(num_classes=3, average=None))
            >>> values = [ ]
            >>> for _ in range(3):
            ...     values.append(metric(torch.randint(3, (20,)), torch.randint(3, (20,))))
            >>> fig_, ax_ = metric.plot(values)

        )_plot)r$   r0   r@   r   r   r   plot   s   *r   namer   c                    s6   |dks|| j v r|| jj vrt |S t| j|S )z%Get attribute from classwise wrapper.r   )__dict__r   r   __getattr__getattr)r$   rC   r%   r   r   rE      s   zClasswiseWrapper.__getattr__valuec                    sd   t | dr|| jjv rt| j|| dS t || |dkr0| jj| _| jj| _| jj| _dS dS )z#Set attribute to classwise wrapper.r   N)hasattrr   	_defaultssetattrr   __setattr___persistent_reductions)r$   rC   rG   r%   r   r   rK      s   

zClasswiseWrapper.__setattr__)NNN)r   N)NN)r4   
__module____qualname____doc__r   __annotations__r   r   r   r   propertyboolr'   r   dictr*   r   r8   r;   r=   r>   r?   r   r   r	   r
   rB   rE   rK   __classcell__r   r   r%   r   r       sH   
 S


,"r   )typingcollections.abcr   r   r   r   torchr   torchmetrics.metricr   torchmetrics.utilities.importsr   torchmetrics.utilities.plotr	   r
   torchmetrics.wrappers.abstractr   TYPE_CHECKINGtorch.nnr   __doctest_skip__r   r   r   r   r   <module>   s   