a
    d&                     @   s8  d dl Z d dlZd dlmZ d dlmZmZmZmZm	Z	m
Z
 d dlmZmZ d dlmZ eeef Ze
eee f Zeee
eeee
ee ee f f f f Ze
eee f Ze
eeee
eef f  eeee
ee ee f f f Zdgdgdd	d
dddZeedddZeee dddZeeedddZeeedddZeeegef eee edddZeee	eeef eeeeeeeeeef  f  f  f dddZeeef eeeeeeeeeef  f  f  e	eeef dd d!Zeeeeeef d"d#d$Zeeeeef dd%d&Z dS )'    N)Counter)AnyCallableDictListTupleUnion)Tensortensor)rank_zero_warn   zThis is a test text)Zanswer_starttextzThis is a test context.1zIs this a test?z
train test)answerscontextidZquestiontitle)sreturnc                 C   sT   t t ddd}t t ddd}t t ddd}t t ddd	}||||| S )
zALower text and remove punctuation, articles and extra whitespace.)r   r   c                 S   s   t dd| S )Nz\b(a|an|the)\b )resubr    r   k/var/www/html/stable-diffusion-webui/venv/lib/python3.9/site-packages/torchmetrics/functional/text/squad.pyremove_articles,   s    z(_normalize_text.<locals>.remove_articlesc                 S   s   d |  S )Nr   )joinsplitr   r   r   r   white_space_fix/   s    z(_normalize_text.<locals>.white_space_fixc                    s"   t tj d fdd| D S )N c                 3   s   | ]}| vr|V  qd S Nr   ).0chexcluder   r   	<genexpr>4       z7_normalize_text.<locals>.remove_punc.<locals>.<genexpr>)setstringpunctuationr   r   r   r#   r   remove_punc2   s    
z$_normalize_text.<locals>.remove_puncc                 S   s   |   S r    )lowerr   r   r   r   r+   6   s    z_normalize_text.<locals>.lower)str)r   r   r   r*   r+   r   r   r   _normalize_text)   s
    r-   c                 C   s   | sg S t |  S )z&Split a sentence into separate tokens.)r-   r   )r   r   r   r   _get_tokens<   s    r.   )predicted_answertarget_answerr   c                 C   s   t |}t | }t|t|@ }tt| }t|dksHt|dkrXtt||kS |dkrhtdS d| tt| }d| tt| }d| | ||  S )z#Compute F1 Score for two sentences.r           g      ?   )r.   r   r
   sumvalueslenint)r/   r0   Ztarget_tokensZpredicted_tokenscommonZnum_sameZ	precisionZrecallr   r   r   _compute_f1_scoreA   s    r8   )
predictionground_truthr   c                 C   s   t tt| t|kS )z&Compute Exact Match for two sentences.)r
   r6   r-   )r9   r:   r   r   r   _compute_exact_match_scoreQ   s    r;   )	metric_fnr9   ground_truthsr   c                    s   t  fdd|D S )zJCalculate maximum score for a predicted answer with all reference answers.c                 3   s   | ]} |V  qd S r    r   )r!   truthr<   r9   r   r   r%   Z   r&   z1_metric_max_over_ground_truths.<locals>.<genexpr>)max)r<   r9   r=   r   r?   r   _metric_max_over_ground_truthsV   s    rA   )predstargetsr   c           	         s   t | tr| g} t |tr |g}| D ]$}| }d|vs@d|vr$tdq$|D ]L}| }d|vsjd|vrxtdt |d }d| vrNtdt qNdd	 | D }d
d  dd fdd|D igig}||fS )zOCheck for types and convert the input to necessary format to compute the input.prediction_textr   zExpected keys in a single prediction are 'prediction_text' and 'id'.Please make sure that 'prediction_text' maps to the answer string and 'id' maps to the key string.r   zExpected keys in a single target are 'answers' and 'id'.Please make sure that 'answers' maps to a `SQuAD` format dictionary and 'id' maps to the key string.
SQuAD Format: r   zzExpected keys in a 'answers' are 'text'.Please make sure that 'answer' maps to a `SQuAD` format dictionary.
SQuAD Format: c                 S   s   i | ]}|d  |d qS )r   rD   r   )r!   r9   r   r   r   
<dictcomp>   r&   z&_squad_input_check.<locals>.<dictcomp>c                 S   s    dd | d d D | d dS )Nc                 S   s   g | ]}d |iqS r   r   )r!   txtr   r   r   
<listcomp>   r&   z8_squad_input_check.<locals>.<lambda>.<locals>.<listcomp>r   r   r   )r   r   r   )Ztgtr   r   r   <lambda>   r&   z$_squad_input_check.<locals>.<lambda>
paragraphsqasc                    s   g | ]} |qS r   r   )r!   targetZ
_fn_answerr   r   rG      r&   z&_squad_input_check.<locals>.<listcomp>)
isinstancer   keysKeyErrorSQuAD_FORMAT)	rB   rC   predZ	pred_keysrK   target_keysr   
preds_dictZtargets_dictr   rL   r   _squad_input_check]   s:    

rT   )rB   rK   r   c           
      C   s   t d}t d}t d}|D ]}|d D ]z}|d D ]l}|d7 }|d | vrbtd|d  d q4d	d
 |d D }| |d  }	|tt|	|7 }|tt|	|7 }q4q(q|||fS )au  Compute F1 Score and Exact Match for a collection of predictions and references.

    Args:
        preds: A dictionary mapping an `id` to the predicted `answer`.
        target:
            A list of dictionary mapping `paragraphs` to list of dictionary mapping `qas` to a list of dictionary
            containing `id` and list of all possible `answers`.

    Return:
        Tuple containing F1 score, Exact match score and total number of examples.

    Example:
        >>> from torchmetrics.functional.text.squad import _squad_update
        >>> preds = [{"prediction_text": "1976", "id": "56e10a3be3433e1400422b22"}]
        >>> target = [{"answers": {"answer_start": [97], "text": ["1976"]}, "id": "56e10a3be3433e1400422b22"}]
        >>> preds_dict = {pred["id"]: pred["prediction_text"] for pred in preds}
        >>> targets_dict = [
        ...     dict(paragraphs=[dict(qas=[dict(answers=[
        ...         {"text": txt} for txt in tgt["answers"]["text"]], id=tgt["id"]) for tgt in target
        ...     ])])
        ... ]
        >>> _squad_update(preds_dict, targets_dict)
        (tensor(1.), tensor(1.), tensor(1))

    r1   r   rI   rJ   r   r   zUnanswered question z will receive score 0.c                 S   s   g | ]}|d  qS r   r   )r!   xr   r   r   rG      r&   z!_squad_update.<locals>.<listcomp>r   )r
   r   rA   r;   r8   )
rB   rK   f1exact_matchtotalarticleZ	paragraphZqar=   rQ   r   r   r   _squad_update   s    rZ   )rV   rW   rX   r   c                 C   s"   d| | }d|  | } || dS )zAggregate the F1 Score and Exact match for the batch.

    Return:
        Dictionary containing the F1 score, Exact match score for the batch.

    g      Y@)rW   rV   r   )rV   rW   rX   r   r   r   _squad_compute   s    r[   c                 C   s*   t | |\}}t||\}}}t|||S )a  Calculate `SQuAD Metric`_ .

    Args:
        preds: A Dictionary or List of Dictionary-s that map `id` and `prediction_text` to the respective values.

            Example prediction:

            .. code-block:: python

                {"prediction_text": "TorchMetrics is awesome", "id": "123"}

        target: A Dictionary or List of Dictionary-s that contain the `answers` and `id` in the SQuAD Format.

            Example target:

            .. code-block:: python

                {
                    'answers': [{'answer_start': [1], 'text': ['This is a test answer']}],
                    'id': '1',
                }

            Reference SQuAD Format:

            .. code-block:: python

                {
                    'answers': {'answer_start': [1], 'text': ['This is a test text']},
                    'context': 'This is a test context.',
                    'id': '1',
                    'question': 'Is this a test?',
                    'title': 'train test'
                }


    Return:
        Dictionary containing the F1 score, Exact match score for the batch.

    Example:
        >>> from torchmetrics.functional.text.squad import squad
        >>> preds = [{"prediction_text": "1976", "id": "56e10a3be3433e1400422b22"}]
        >>> target = [{"answers": {"answer_start": [97], "text": ["1976"]},"id": "56e10a3be3433e1400422b22"}]
        >>> squad(preds, target)
        {'exact_match': tensor(100.), 'f1': tensor(100.)}

    Raises:
        KeyError:
            If the required keys are missing in either predictions or targets.

    References:
        [1] SQuAD: 100,000+ Questions for Machine Comprehension of Text by Pranav Rajpurkar, Jian Zhang, Konstantin
        Lopyrev, Percy Liang `SQuAD Metric`_ .

    )rT   rZ   r[   )rB   rK   rS   target_dictrV   rW   rX   r   r   r   squad   s    7r]   )!r   r(   collectionsr   typingr   r   r   r   r   r   Ztorchr	   r
   Ztorchmetrics.utilitiesr   r,   ZSINGLE_PRED_TYPEZ
PREDS_TYPEr6   ZSINGLE_TARGET_TYPEZTARGETS_TYPEZUPDATE_METHOD_SINGLE_PRED_TYPErP   r-   r.   r8   r;   rA   rT   rZ   r[   r]   r   r   r   r   <module>   s@    ,:	6,
&/