o
    yiy&                     @   s\  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dedefddZdedee fddZdededefddZdededefddZdeeegef dedee defddZd ed!ede	eeef eeeeeeeeeef  f  f  f fd"d#Zd eeef d$eeeeeeeeeef  f  f  de	eeef fd%d&Zd'ed(ed)edeeef fd*d+Zd ed$edeeef fd,d-Z dS ).    N)Counter)AnyCallableDictListTupleUnion)Tensortensor)rank_zero_warn   zThis is a test text)answer_starttextzThis is a test context.1zIs this a test?z
train test)answerscontextidquestiontitlesreturnc                 C   s\   dt dt fdd}dt dt fdd}dt dt fdd}dt dt f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   V/home/ubuntu/.local/lib/python3.10/site-packages/torchmetrics/functional/text/squad.pyremove_articles,      z(_normalize_text.<locals>.remove_articlesc                 S   s   d |  S )Nr   )joinsplitr   r   r   r   white_space_fix/   r   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   s    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   r0   predicted_answertarget_answerc           	      C   s   t |}t | }t|t|@ }tt| }t|dks$t|dkr,tt||kS |dkr4tdS d| tt| }d| tt| }d| | ||  }|S )z#Compute F1 Score for two sentences.r           g      ?   )r0   r   r
   sumvalueslenint)	r1   r2   target_tokenspredicted_tokenscommonnum_same	precisionrecallf1r   r   r   _compute_f1_scoreA   s   r@   
predictionground_truthc                 C   s   t tt| t|kS )z&Compute Exact Match for two sentences.)r
   r8   r/   )rA   rB   r   r   r   _compute_exact_match_scoreR   s   rC   	metric_fnground_truthsc                    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rD   rA   r   r   r(   [   s    z1_metric_max_over_ground_truths.<locals>.<genexpr>)max)rD   rA   rE   r   rG   r   _metric_max_over_ground_truthsW   s   rI   predstargetsc                    s   t | tr| g} t |tr|g}| D ]}| }d|vs d|vr$tdq|D ]&}| }d|vs5d|vr<tdt |d }d| vrMtdt q'd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   rL   r   )r$   rA   r   r   r   
<dictcomp>   s    z&_squad_input_check.<locals>.<dictcomp>c                 S   s"   t dd | d d D | d dS )Nc                 S   s   g | ]}t |d qS )r   dict)r$   txtr   r   r   
<listcomp>   s    z8_squad_input_check.<locals>.<lambda>.<locals>.<listcomp>r   r   r   )r   r   rN   )tgtr   r   r   <lambda>   s   " z$_squad_input_check.<locals>.<lambda>
paragraphsqasc                    s   g | ]} |qS r   r   )r$   target
_fn_answerr   r   rQ      s    z&_squad_input_check.<locals>.<listcomp>)
isinstancer   keysKeyErrorSQuAD_FORMAT)rJ   rK   predrZ   rV   r   
preds_dicttargets_dictr   rW   r   _squad_input_check^   s>   

r`   rV   c           
      C   s   t d}t d}t d}|D ]F}|d D ]?}|d D ]8}|d7 }|d | vr1td|d  d qttd	d
 |d }| |d  }	|tt|	|7 }|tt|	|7 }qqq|||fS )at  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))
    r3   r   rT   rU   r   r   zUnanswered question z will receive score 0.c                 S   s   | d S )Nr   r   )xr   r   r   rS      s    z_squad_update.<locals>.<lambda>r   )r
   r   listmaprI   rC   r@   )
rJ   rV   r?   exact_matchtotalarticle	paragraphqarE   r]   r   r   r   _squad_update   s"   
ri   r?   rd   re   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@)rd   r?   r   )r?   rd   re   r   r   r   _squad_compute   s   
rj   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`_ .
    )r`   ri   rj   )rJ   rV   r^   target_dictr?   rd   re   r   r   r   squad   s   6rl   )!r   r*   collectionsr   typingr   r   r   r   r   r   torchr	   r
   torchmetrics.utilitiesr   r.   SINGLE_PRED_TYPE
PREDS_TYPEr8   SINGLE_TARGET_TYPETARGETS_TYPEUPDATE_METHOD_SINGLE_PRED_TYPEr\   r/   r0   r@   rC   rI   r`   ri   rj   rl   r   r   r   r   <module>   s\    ,:	
6
,
&
"."