
    g5                     `    d Z ddlZddlmZ ddlmZmZ ddZddZd Z	ddZ
d	 Zdd
ZddZy)z RIBES score implementation     N)islice)choosengramsc                    d}| D ]}  }t        ||      }t        |      }t        dt        j                  dt        |      t        |      z  z
              }t        |      t        |      z  }	||	|z  z  ||z  z  }
|
|kD  s||
} |S )a  
    The RIBES (Rank-based Intuitive Bilingual Evaluation Score) from
    Hideki Isozaki, Tsutomu Hirao, Kevin Duh, Katsuhito Sudoh and
    Hajime Tsukada. 2010. "Automatic Evaluation of Translation Quality for
    Distant Language Pairs". In Proceedings of EMNLP.
    https://www.aclweb.org/anthology/D/D10/D10-1092.pdf

    The generic RIBES scores used in shared task, e.g. Workshop for
    Asian Translation (WAT) uses the following RIBES calculations:

        RIBES = kendall_tau * (alpha**p1) * (beta**bp)

    Please note that this re-implementation differs from the official
    RIBES implementation and though it emulates the results as describe
    in the original paper, there are further optimization implemented
    in the official RIBES script.

    Users are encouraged to use the official RIBES script instead of this
    implementation when evaluating your machine translation system. Refer
    to https://www.kecl.ntt.co.jp/icl/lirg/ribes/ for the official script.

    :param references: a list of reference sentences
    :type references: list(list(str))
    :param hypothesis: a hypothesis sentence
    :type hypothesis: list(str)
    :param alpha: hyperparameter used as a prior for the unigram precision.
    :type alpha: float
    :param beta: hyperparameter used as a prior for the brevity penalty.
    :type beta: float
    :return: The best ribes score from one of the references.
    :rtype: float
    g      g      ?)word_rank_alignmentkendall_tauminmathexplen)
references
hypothesisalphabeta
best_ribes	referencewordernktbpp1_ribess              O/var/www/openai/venv/lib/python3.12/site-packages/nltk/translate/ribes_score.pysentence_ribesr      s    B J	$Y
;&! dhhsS^c*o%EEFG [3z?*E	"b$h/JJ        c                 l    d}t        | |      D ]  \  }}|t        ||||      z  } |t        |      z  S )a  
    This function "calculates RIBES for a system output (hypothesis) with
    multiple references, and returns "best" score among multi-references and
    individual scores. The scores are corpus-wise, i.e., averaged by the number
    of sentences." (c.f. RIBES version 1.03.1 code).

    Different from BLEU's micro-average precision, RIBES calculates the
    macro-average precision by averaging the best RIBES score for each pair of
    hypothesis and its corresponding references

    >>> hyp1 = ['It', 'is', 'a', 'guide', 'to', 'action', 'which',
    ...         'ensures', 'that', 'the', 'military', 'always',
    ...         'obeys', 'the', 'commands', 'of', 'the', 'party']
    >>> ref1a = ['It', 'is', 'a', 'guide', 'to', 'action', 'that',
    ...          'ensures', 'that', 'the', 'military', 'will', 'forever',
    ...          'heed', 'Party', 'commands']
    >>> ref1b = ['It', 'is', 'the', 'guiding', 'principle', 'which',
    ...          'guarantees', 'the', 'military', 'forces', 'always',
    ...          'being', 'under', 'the', 'command', 'of', 'the', 'Party']
    >>> ref1c = ['It', 'is', 'the', 'practical', 'guide', 'for', 'the',
    ...          'army', 'always', 'to', 'heed', 'the', 'directions',
    ...          'of', 'the', 'party']

    >>> hyp2 = ['he', 'read', 'the', 'book', 'because', 'he', 'was',
    ...         'interested', 'in', 'world', 'history']
    >>> ref2a = ['he', 'was', 'interested', 'in', 'world', 'history',
    ...          'because', 'he', 'read', 'the', 'book']

    >>> list_of_references = [[ref1a, ref1b, ref1c], [ref2a]]
    >>> hypotheses = [hyp1, hyp2]
    >>> round(corpus_ribes(list_of_references, hypotheses),4)
    0.3597

    :param references: a corpus of lists of reference sentences, w.r.t. hypotheses
    :type references: list(list(list(str)))
    :param hypotheses: a list of hypothesis sentences
    :type hypotheses: list(list(str))
    :param alpha: hyperparameter used as a prior for the unigram precision.
    :type alpha: float
    :param beta: hyperparameter used as a prior for the brevity penalty.
    :type beta: float
    :return: The best ribes score from one of the references.
    :rtype: float
    g        )zipr   r   )list_of_references
hypothesesr   r   corpus_best_ribesr   r   s          r   corpus_ribesr    F   sH    Z "%&8*"E
J^J
E4PP #Fs:..r   c                 b    t        t        |t        |                   D ]  \  }}| |k(  s|c S  y)a  
    This function returns the position of the first instance of the ngram
    appearing in a sentence.

    Note that one could also use string as follows but the code is a little
    convoluted with type casting back and forth:

        char_pos = ' '.join(sent)[:' '.join(sent).index(' '.join(ngram))]
        word_pos = char_pos.count(' ')

    Another way to conceive this is:

        return next(i for i, ng in enumerate(ngrams(sentence, len(ngram)))
                    if ng == ngram)

    :param ngram: The ngram that needs to be searched
    :type ngram: tuple
    :param sentence: The list of tokens to search from.
    :type sentence: list(str)
    N)	enumerater   r   )ngramsentenceisublists       r   position_of_ngramr'   z   s/    ,  xU <=
7GH >r   c           
         g }t        |      }g }g }t        dt        |       dz         D ]F  }t        | |      D ]  }|j                  |        t        ||      D ]  }|j                  |        H t	        |      D ]o  \  }	}
|
| vr|j                  |
      | j                  |
      cxk(  rdk(  r$n n!|j                  | j                  |
             Yt        |	||	z
  dz         }t        d|      D ]  }|	|z   |k  rkt        t        ||	|	|z   dz               }|j                  |      }|j                  |      }||cxk(  rdk(  r"n nt        ||       }|j                  |        ||	k  s|t        t        ||	|z
  |	dz               }|j                  |      }|j                  |      }||cxk(  rdk(  sn t        ||       }|j                  |t        |      z   dz
          o r |S )a
  
    This is the word rank alignment algorithm described in the paper to produce
    the *worder* list, i.e. a list of word indices of the hypothesis word orders
    w.r.t. the list of reference words.

    Below is (H0, R0) example from the Isozaki et al. 2010 paper,
    note the examples are indexed from 1 but the results here are indexed from 0:

        >>> ref = str('he was interested in world history because he '
        ... 'read the book').split()
        >>> hyp = str('he read the book because he was interested in world '
        ... 'history').split()
        >>> word_rank_alignment(ref, hyp)
        [7, 8, 9, 10, 6, 0, 1, 2, 3, 4, 5]

    The (H1, R1) example from the paper, note the 0th index:

        >>> ref = 'John hit Bob yesterday'.split()
        >>> hyp = 'Bob hit John yesterday'.split()
        >>> word_rank_alignment(ref, hyp)
        [2, 1, 0, 3]

    Here is the (H2, R2) example from the paper, note the 0th index here too:

        >>> ref = 'the boy read the book'.split()
        >>> hyp = 'the book was read by the boy'.split()
        >>> word_rank_alignment(ref, hyp)
        [3, 4, 2, 0, 1]

    :param reference: a reference sentence
    :type reference: list(str)
    :param hypothesis: a hypothesis sentence
    :type hypothesis: list(str)
       )r   ranger   appendr"   countindexmaxtupler   r'   )r   r   character_basedr   hyp_len
ref_ngrams
hyp_ngramsnngr%   h_wordmax_window_sizewindowright_context_ngramnum_times_in_refnum_times_in_hypposleft_context_ngrams                     r   r   r      s   F F*oG JJ1c)nq()A&Bb! 'Q'Bb! ( *
 z*	6" f%)@EAEMM)//&12!!Wq[1_5O?3v:'*/z1a&jSTn0U*V''1'7'78K'L$'1'7'78K'L$'+;@q@/0CYOc*Q;).vj!f*aRSe/T)U&'1'7'78J'K$'1'7'78J'K$'+;@q@/0BINcC0B,C&Ca&GH- 4 +B Mr   c              #      K   t        |       }dt        |d      }}|g}|L|t        |d      }}||dz   |k(  r|j                  |       nt        |      dkD  rt	        |       |g}|Kyyw)aG  
    Given the *worder* list, this function groups monotonic +1 sequences.

        >>> worder = [7, 8, 9, 10, 6, 0, 1, 2, 3, 4, 5]
        >>> list(find_increasing_sequences(worder))
        [(7, 8, 9, 10), (0, 1, 2, 3, 4, 5)]

    :param worder: The worder list output from word_rank_alignment
    :param type: list(int)
    Nr)   )iternextr+   r   r/   )r   itemsabresults        r   find_increasing_sequencesrE      s{      LEeT"qASF
-$ud#1=QUaZMM!6{QFm#SF -s   A)A.,A.c                     t        |       }|dk  rd}n4t        |       }t        d |D              }t        |d      }d|z  |z  dz
  }|r|dz   dz  S |S )a  
    Calculates the Kendall's Tau correlation coefficient given the *worder*
    list of word alignments from word_rank_alignment(), using the formula:

        tau = 2 * num_increasing_pairs / num_possible_pairs -1

    Note that the no. of increasing pairs can be discontinuous in the *worder*
    list and each each increasing sequence can be tabulated as choose(len(seq), 2)
    no. of increasing pairs, e.g.

        >>> worder = [7, 8, 9, 10, 6, 0, 1, 2, 3, 4, 5]
        >>> number_possible_pairs = choose(len(worder), 2)
        >>> round(kendall_tau(worder, normalize=False),3)
        -0.236
        >>> round(kendall_tau(worder),3)
        0.382

    :param worder: The worder list output from word_rank_alignment
    :type worder: list(int)
    :param normalize: Flag to indicate normalization to between 0.0 and 1.0.
    :type normalize: boolean
    :return: The Kendall's Tau correlation coefficient.
    :rtype: float
       c              3   F   K   | ]  }t        t        |      d         ywrG   N)r   r   ).0seqs     r   	<genexpr>zkendall_tau.<locals>.<genexpr>#  s     "WBV36#c(A#6BVs   !r)   )r   rE   sumr   )r   	normalize
worder_lentauincreasing_sequencesnum_increasing_pairsnum_possible_pairss          r   r   r      ss    2 VJ A~  9@""WBV"WW#J2&&);;a?a1}
r   c           	          t        |       }t        d t        | t        |            D              }d|t	        |dz   d      z  z
  }|r|dz   dz  S |S )a  
    Calculates the Spearman's Rho correlation coefficient given the *worder*
    list of word alignment from word_rank_alignment(), using the formula:

        rho = 1 - sum(d**2) / choose(len(worder)+1, 3)

    Given that d is the sum of difference between the *worder* list of indices
    and the original word indices from the reference sentence.

    Using the (H0,R0) and (H5, R5) example from the paper

        >>> worder =  [7, 8, 9, 10, 6, 0, 1, 2, 3, 4, 5]
        >>> round(spearman_rho(worder, normalize=False), 3)
        -0.591
        >>> round(spearman_rho(worder), 3)
        0.205

    :param worder: The worder list output from word_rank_alignment
    :param type: list(int)
    c              3   2   K   | ]  \  }}||z
  d z    ywrJ    )rK   wir%   s      r   rM   zspearman_rho.<locals>.<genexpr>D  s     Q2PQQ1}2Ps   r)      rG   )r   rN   r   r*   r   )r   rO   rP   sum_d_squarerhos        r   spearman_rhor\   .  sY    * VJQ#feJ>O2PQQL
lVJNA66
6Ca1}
r   )g      ?g?)F)T)__doc__r
   	itertoolsr   	nltk.utilr   r   r   r    r'   r   rE   r   r\   rW   r   r   <module>r`      s;    #   $3l1/h8Od0+\r   