a
    dx.                     @   s   d dl mZmZmZmZmZ d dl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svdgZG d	d
 d
eZdS )    )AnyDictOptionalSequenceUnion)Tensornn)MetricCollection)Metric)_MATPLOTLIB_AVAILABLE)_AX_TYPE_PLOT_OUT_TYPE)WrapperMetricMultitaskWrapper.plotc                       s  e Zd ZdZdZeeeee	f f dd fddZ
eeeeee	f f ddddZeeef eeef dd	d
dZeeef dddZeeef eeef eeef d	ddZdd fddZdeeeee f  eee  ee dddZ  ZS )MultitaskWrappera  Wrapper class for computing different metrics on different tasks in the context of multitask learning.

    In multitask learning the different tasks requires different metrics to be evaluated. This wrapper allows
    for easy evaluation in such cases by supporting multiple predictions and targets through a dictionary.
    Note that only metrics where the signature of `update` follows the stardard `preds, target` is supported.

    Args:
        task_metrics:
            Dictionary associating each task to a Metric or a MetricCollection. The keys of the dictionary represent the
            names of the tasks, and the values represent the metrics to use for each task.

    Raises:
        TypeError:
            If argument `task_metrics` is not an dictionary
        TypeError:
            If not all values in the `task_metrics` dictionary is instances of `Metric` or `MetricCollection`

    Example (with a single metric per class):
         >>> import torch
         >>> from torchmetrics.wrappers import MultitaskWrapper
         >>> from torchmetrics.regression import MeanSquaredError
         >>> from torchmetrics.classification import BinaryAccuracy
         >>>
         >>> classification_target = torch.tensor([0, 1, 0])
         >>> regression_target = torch.tensor([2.5, 5.0, 4.0])
         >>> targets = {"Classification": classification_target, "Regression": regression_target}
         >>>
         >>> classification_preds = torch.tensor([0, 0, 1])
         >>> regression_preds = torch.tensor([3.0, 5.0, 2.5])
         >>> preds = {"Classification": classification_preds, "Regression": regression_preds}
         >>>
         >>> metrics = MultitaskWrapper({
         ...     "Classification": BinaryAccuracy(),
         ...     "Regression": MeanSquaredError()
         ... })
         >>> metrics.update(preds, targets)
         >>> metrics.compute()
         {'Classification': tensor(0.3333), 'Regression': tensor(0.8333)}

    Example (with several metrics per task):
         >>> import torch
         >>> from torchmetrics import MetricCollection
         >>> from torchmetrics.wrappers import MultitaskWrapper
         >>> from torchmetrics.regression import MeanSquaredError, MeanAbsoluteError
         >>> from torchmetrics.classification import BinaryAccuracy, BinaryF1Score
         >>>
         >>> classification_target = torch.tensor([0, 1, 0])
         >>> regression_target = torch.tensor([2.5, 5.0, 4.0])
         >>> targets = {"Classification": classification_target, "Regression": regression_target}
         >>>
         >>> classification_preds = torch.tensor([0, 0, 1])
         >>> regression_preds = torch.tensor([3.0, 5.0, 2.5])
         >>> preds = {"Classification": classification_preds, "Regression": regression_preds}
         >>>
         >>> metrics = MultitaskWrapper({
         ...     "Classification": MetricCollection(BinaryAccuracy(), BinaryF1Score()),
         ...     "Regression": MetricCollection(MeanSquaredError(), MeanAbsoluteError())
         ... })
         >>> metrics.update(preds, targets)
         >>> metrics.compute()
         {'Classification': {'BinaryAccuracy': tensor(0.3333), 'BinaryF1Score': tensor(0.)},
          'Regression': {'MeanSquaredError': tensor(0.8333), 'MeanAbsoluteError': tensor(0.6667)}}

    FN)task_metricsreturnc                    s$   |  | t   t|| _d S N)_check_task_metrics_typesuper__init__r   Z
ModuleDictr   )selfr   	__class__ h/var/www/html/stable-diffusion-webui/venv/lib/python3.9/site-packages/torchmetrics/wrappers/multitask.pyr   a   s    

zMultitaskWrapper.__init__c                 C   sJ   t | tstd|  |  D ]$}t |ttfs tdt| q d S )NzDExpected argument `task_metrics` to be a dict. Found task_metrics = zYExpected each task's metric to be a Metric or a MetricCollection. Found a metric of type )
isinstancedict	TypeErrorvaluesr
   r	   type)r   metricr   r   r   r   i   s    
z)MultitaskWrapper._check_task_metrics_type)
task_predstask_targetsr   c                 C   s   | j  |   kr"| ksLn td|  d|  d| j   | j  D ]$\}}|| }|| }||| qVdS )zUpdate each task's metric with its corresponding pred and target.

        Args:
            task_preds: Dictionary associating each task to a Tensor of pred.
            task_targets: Dictionary associating each task to a Tensor of target.

        zExpected arguments `task_preds` and `task_targets` to have the same keys as the wrapped `task_metrics`. Found task_preds.keys() = z, task_targets.keys() = z  and self.task_metrics.keys() = N)r   keys
ValueErroritemsupdate)r   r"   r#   	task_namer!   predtargetr   r   r   r'   u   s    $zMultitaskWrapper.update)r   c                 C   s   dd | j  D S )zCompute metrics for all tasks.c                 S   s   i | ]\}}||  qS r   )compute.0r(   r!   r   r   r   
<dictcomp>       z,MultitaskWrapper.compute.<locals>.<dictcomp>r   r&   )r   r   r   r   r+      s    zMultitaskWrapper.computec                    s    fdd| j  D S )zTCall underlying forward methods for all tasks and return the result as a dictionary.c                    s$   i | ]\}}|| | | qS r   r   r,   r"   r#   r   r   r.      s   z,MultitaskWrapper.forward.<locals>.<dictcomp>r0   )r   r"   r#   r   r1   r   forward   s    zMultitaskWrapper.forwardc                    s&   | j  D ]}|  q
t   dS )zReset all underlying metrics.N)r   r   resetr   )r   r!   r   r   r   r3      s    
zMultitaskWrapper.reset)valaxesr   c           	         s,  |durpt |ts$tdt| tdd |D s>tdt|t| jkrptdt| dt| j d|dur||n|  }g }t	| j
 D ]\}\ }|dur|| nd}t |tr|j|  |d	\}}n@t |tr|j fd
d|D |d	\}}ntdt| |||f q|S )az  Plot a single or multiple values from the metric.

        All tasks' results are plotted on individual axes.

        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.
            axes: Sequence of matplotlib axis objects. If provided, will add the plots to the provided axis objects.
                If not provided, will create them.

        Returns:
            Sequence of tuples with Figure and Axes object for each task.

        .. plot::
            :scale: 75

            >>> # Example plotting a single value
            >>> import torch
            >>> from torchmetrics.wrappers import MultitaskWrapper
            >>> from torchmetrics.regression import MeanSquaredError
            >>> from torchmetrics.classification import BinaryAccuracy
            >>>
            >>> classification_target = torch.tensor([0, 1, 0])
            >>> regression_target = torch.tensor([2.5, 5.0, 4.0])
            >>> targets = {"Classification": classification_target, "Regression": regression_target}
            >>>
            >>> classification_preds = torch.tensor([0, 0, 1])
            >>> regression_preds = torch.tensor([3.0, 5.0, 2.5])
            >>> preds = {"Classification": classification_preds, "Regression": regression_preds}
            >>>
            >>> metrics = MultitaskWrapper({
            ...     "Classification": BinaryAccuracy(),
            ...     "Regression": MeanSquaredError()
            ... })
            >>> metrics.update(preds, targets)
            >>> value = metrics.compute()
            >>> fig_, ax_ = metrics.plot(value)

        .. plot::
            :scale: 75

            >>> # Example plotting multiple values
            >>> import torch
            >>> from torchmetrics.wrappers import MultitaskWrapper
            >>> from torchmetrics.regression import MeanSquaredError
            >>> from torchmetrics.classification import BinaryAccuracy
            >>>
            >>> classification_target = torch.tensor([0, 1, 0])
            >>> regression_target = torch.tensor([2.5, 5.0, 4.0])
            >>> targets = {"Classification": classification_target, "Regression": regression_target}
            >>>
            >>> classification_preds = torch.tensor([0, 0, 1])
            >>> regression_preds = torch.tensor([3.0, 5.0, 2.5])
            >>> preds = {"Classification": classification_preds, "Regression": regression_preds}
            >>>
            >>> metrics = MultitaskWrapper({
            ...     "Classification": BinaryAccuracy(),
            ...     "Regression": MeanSquaredError()
            ... })
            >>> values = []
            >>> for _ in range(10):
            ...     values.append(metrics(preds, targets))
            >>> fig_, ax_ = metrics.plot(values)

        Nz>Expected argument `axes` to be a Sequence. Found type(axes) = c                 s   s   | ]}t |tV  qd S r   )r   r   )r-   axr   r   r   	<genexpr>   r/   z(MultitaskWrapper.plot.<locals>.<genexpr>zBExpected each ax in argument `axes` to be a matplotlib axis objectzfExpected argument `axes` to be a Sequence of the same length as the number of tasks.Found len(axes) = z and z tasks)r6   c                    s   g | ]}|  qS r   r   )r-   vr(   r   r   
<listcomp>   r/   z)MultitaskWrapper.plot.<locals>.<listcomp>zWExpected argument `val` to be None or of type Dict or Sequence[Dict]. Found type(val)= )r   r   r   r    alllenr   r%   r+   	enumerater&   r   plotappend)	r   r4   r5   Zfig_axsiZtask_metricr6   far   r9   r   r>      s8    D

"r   )NN)__name__
__module____qualname____doc__Zis_differentiabler   strr   r
   r	   r   staticmethodr   r   r'   r   r+   r2   r3   r   r   r   r   r>   __classcell__r   r   r   r   r      s    A""* r   N)typingr   r   r   r   r   Ztorchr   r   Ztorchmetrics.collectionsr	   Ztorchmetrics.metricr
   Ztorchmetrics.utilities.importsr   Ztorchmetrics.utilities.plotr   r   Ztorchmetrics.wrappers.abstractr   Z__doctest_skip__r   r   r   r   r   <module>   s   