
    Qd$P                     x    d Z ddlZddlmZ ddlmZ  G d d          Z G d d          Z G d	 d
          ZdS )a&  
A decoder that uses stacks to implement phrase-based translation.

In phrase-based translation, the source sentence is segmented into
phrases of one or more words, and translations for those phrases are
used to build the target sentence.

Hypothesis data structures are used to keep track of the source words
translated so far and the partial output. A hypothesis can be expanded
by selecting an untranslated phrase, looking up its translation in a
phrase table, and appending that translation to the partial output.
Translation is complete when a hypothesis covers all source words.

The search space is huge because the source sentence can be segmented
in different ways, the source phrases can be selected in any order,
and there could be multiple translations for the same source phrase in
the phrase table. To make decoding tractable, stacks are used to limit
the number of candidate hypotheses by doing histogram and/or threshold
pruning.

Hypotheses with the same number of words translated are placed in the
same stack. In histogram pruning, each stack has a size limit, and
the hypothesis with the lowest score is removed when the stack is full.
In threshold pruning, hypotheses that score below a certain threshold
of the best hypothesis in that stack are removed.

Hypothesis scoring can include various factors such as phrase
translation probability, language model probability, length of
translation, cost of remaining words to be translated, and so on.


References:
Philipp Koehn. 2010. Statistical Machine Translation.
Cambridge University Press, New York.
    Ndefaultdict)logc                       e Zd ZdZd Zed             Zej        d             Zd Zd Z	d Z
d Zd	 Zd
 Zd Zed             ZdS )StackDecodera  
    Phrase-based stack decoder for machine translation

    >>> from nltk.translate import PhraseTable
    >>> phrase_table = PhraseTable()
    >>> phrase_table.add(('niemand',), ('nobody',), log(0.8))
    >>> phrase_table.add(('niemand',), ('no', 'one'), log(0.2))
    >>> phrase_table.add(('erwartet',), ('expects',), log(0.8))
    >>> phrase_table.add(('erwartet',), ('expecting',), log(0.2))
    >>> phrase_table.add(('niemand', 'erwartet'), ('one', 'does', 'not', 'expect'), log(0.1))
    >>> phrase_table.add(('die', 'spanische', 'inquisition'), ('the', 'spanish', 'inquisition'), log(0.8))
    >>> phrase_table.add(('!',), ('!',), log(0.8))

    >>> #  nltk.model should be used here once it is implemented
    >>> from collections import defaultdict
    >>> language_prob = defaultdict(lambda: -999.0)
    >>> language_prob[('nobody',)] = log(0.5)
    >>> language_prob[('expects',)] = log(0.4)
    >>> language_prob[('the', 'spanish', 'inquisition')] = log(0.2)
    >>> language_prob[('!',)] = log(0.1)
    >>> language_model = type('',(object,),{'probability_change': lambda self, context, phrase: language_prob[phrase], 'probability': lambda self, phrase: language_prob[phrase]})()

    >>> stack_decoder = StackDecoder(phrase_table, language_model)

    >>> stack_decoder.translate(['niemand', 'erwartet', 'die', 'spanische', 'inquisition', '!'])
    ['nobody', 'expects', 'the', 'spanish', 'inquisition', '!']

    c                     || _         || _        d| _        	 d| _        	 d| _        	 d| _        |                                  dS )aG  
        :param phrase_table: Table of translations for source language
            phrases and the log probabilities for those translations.
        :type phrase_table: PhraseTable

        :param language_model: Target language model. Must define a
            ``probability_change`` method that calculates the change in
            log probability of a sentence, if a given string is appended
            to it.
            This interface is experimental and will likely be replaced
            with nltk.model once it is implemented.
        :type language_model: object
                d   g      ?N)phrase_tablelanguage_modelword_penaltybeam_threshold
stack_size _StackDecoder__distortion_factor%_StackDecoder__compute_log_distortion)selfr   r   s      <lib/python3.11/site-packages/nltk/translate/stack_decoder.py__init__zStackDecoder.__init__O   s`     ),	 "	 	 $' %%'''''    c                     | j         S )a   
        float: Amount of reordering of source phrases.
            Lower values favour monotone translation, suitable when
            word order is similar for both source and target languages.
            Value between 0.0 and 1.0. Default 0.5.
        )r   r   s    r   distortion_factorzStackDecoder.distortion_factory   s     ''r   c                 <    || _         |                                  d S N)r   r   )r   ds     r   r   zStackDecoder.distortion_factor   s"    #$ %%'''''r   c                 z    | j         dk    rt          d          | _        d S t          | j                   | _        d S )Nr	   g&.>)r   r   $_StackDecoder__log_distortion_factorr   s    r   __compute_log_distortionz%StackDecoder.__compute_log_distortion   s?     #s**+.t99D(((+.t/G+H+HD(((r   c           
      j    t          |          }t          |          } fdt          d|dz             D             }t                      }|d                             |                                |          }                     |          }|D ]}|D ]}	t                              ||	          }
|
D ]}||d         |d                  } j	        
                    |          D ]|}                     |	||          }t          |||j        |	          }                     |||          |_        |                                }||                             |           }Ќ||         st          j        d           g S ||                                         }|                                S )z
        :param src_sentence: Sentence to be translated
        :type src_sentence: list(str)

        :return: Translated sentence
        :rtype: list(str)
        c                 D    g | ]}t          j        j                  S  )_Stackr   r   ).0_r   s     r   
<listcomp>z*StackDecoder.translate.<locals>.<listcomp>   s8     
 
 
 4?D$788
 
 
r   r      )	raw_scoresrc_phrase_span
trg_phrasepreviouszYUnable to translate all words. The source sentence contains words not in the phrase table)tuplelenrange_Hypothesispushfind_all_src_phrasescompute_future_scoresr   valid_phrasesr   translations_forexpansion_scorer)   future_scoretotal_translated_wordswarningswarnbesttranslation_so_far)r   src_sentencesentencesentence_lengthstacksempty_hypothesisall_phrasesfuture_score_tablestack
hypothesispossible_expansionsr(   
src_phrasetranslation_optionr'   new_hypothesistotal_wordsbest_hypothesiss   `                 r   	translatezStackDecoder.translate   s-    &&h--
 
 
 
1o122
 
 
 '==q	'(((//99!77AA 	A 	AE# A A
&2&@&@' '# (; A AO!)/!*<q?Q*Q!RJ.2.?.P.P"/ / A A* %)$8$8&(:O% %	 *5&/,;'9'D%/	* * * 7;6G6G*,>7 73 '5&K&K&M&M{+00@@@@!AA	A0 o& 	M#   I 1668811333r   c                     t          |          }d |D             }t          d|          D ]I}t          |dz   |dz             D ]0}|||         }|| j        v r||                             |           1J|S )a@  
        Finds all subsequences in src_sentence that have a phrase
        translation in the translation table

        :type src_sentence: tuple(str)

        :return: Subsequences that have a phrase translation,
            represented as a table of lists of end positions.
            For example, if result[2] is [5, 6, 9], then there are
            three phrases starting from position 2 in ``src_sentence``,
            ending at positions 5, 6, and 9 exclusive. The list of
            ending positions are in ascending order.
        :rtype: list(list(int))
        c                     g | ]}g S r!   r!   )r#   r$   s     r   r%   z5StackDecoder.find_all_src_phrases.<locals>.<listcomp>   s    333"333r   r   r&   )r,   r-   r   append)r   r;   r=   phrase_indicesstartendpotential_phrases          r   r0   z!StackDecoder.find_all_src_phrases   s     l++33l3331o.. 	6 	6EUQY!(;<< 6 6#/c	#: #t'888"5)005556 r   c                 "   t          d           }t          dt          |          dz             D ]}t          dt          |          |z
  dz             D ]}||z   }|||         }|| j        v rM| j                            |          d         j        }|| j                            |          z  }|||         |<   t          |dz   |          D ]<}||         |         ||         |         z   }	|	||         |         k    r|	||         |<   =|S )a  
        Determines the approximate scores for translating every
        subsequence in ``src_sentence``

        Future scores can be used a look-ahead to determine the
        difficulty of translating the remaining parts of a src_sentence.

        :type src_sentence: tuple(str)

        :return: Scores of subsequences referenced by their start and
            end positions. For example, result[2][5] is the score of the
            subsequence covering positions 2, 3, and 4.
        :rtype: dict(int: (dict(int): float))
        c                  "    t          d           S )Nc                       t          d          S )N-inf)floatr!   r   r   <lambda>zFStackDecoder.compute_future_scores.<locals>.<lambda>.<locals>.<lambda>   s    v r   r   r!   r   r   rW   z4StackDecoder.compute_future_scores.<locals>.<lambda>   s    [1F1F%G%G r   r&   r   )r   r-   r,   r   r3   log_probr   probability)
r   r;   scores
seq_lengthrO   rP   phrasescoremidcombined_scores
             r   r1   z"StackDecoder.compute_future_scores   sJ    GGHH3|#4#4q#899 	< 	<Jq#l"3"3j"@1"DEE < <j(%eCi0T... ->>vFF  T0<<VDDDE).F5M#& !C00 < <C%+E]3%7&+c:J%JN%uc(:::-;uc*<<" r   c                 t    d}|                     |          D ]}|||d                  |d                  z  } |S )zs
        Determines the approximate score for translating the
        untranslated words in ``hypothesis``
        r	   r   r&   )untranslated_spans)r   rC   rA   r=   r]   spans         r   r5   zStackDecoder.future_score  sJ    
 11/BB 	: 	:D'Q0a99EEr   c                     |j         }||j        z  }|| j                            ||j                  z  }||                     ||          z  }|| j        t          |j                  z  z  }|S )a  
        Calculate the score of expanding ``hypothesis`` with
        ``translation_option``

        :param hypothesis: Hypothesis being expanded
        :type hypothesis: _Hypothesis

        :param translation_option: Information about the proposed expansion
        :type translation_option: PhraseTableEntry

        :param src_phrase_span: Word position span of the source phrase
        :type src_phrase_span: tuple(int, int)
        )r'   rX   r   probability_changer)   distortion_scorer   r,   )r   rC   rF   r(   r]   s        r   r4   zStackDecoder.expansion_score  s     $#,, 	$77*5
 
 	
 	&&z?CCC"S);)F%G%GGGr   c                 v    |j         sdS |d         }|j         d         }||z
  }t          |          | j        z  S )Nr	   r   r&   )r(   absr   )r   rC   next_src_phrase_spannext_src_phrase_startprev_src_phrase_enddistortion_distances         r   re   zStackDecoder.distortion_score(  sN    ) 	3 4Q 7(8;36II&''$*FFFr   c                     |                     t          |                     }g }|D ]M}|d         }|d         }||k     r5| |         D ]!}||k    r n|                    ||f           "|dz  }||k     5N|S )a  
        Extract phrases from ``all_phrases_from`` that contains words
        that have not been translated by ``hypothesis``

        :param all_phrases_from: Phrases represented by their spans, in
            the same format as the return value of
            ``find_all_src_phrases``
        :type all_phrases_from: list(list(int))

        :type hypothesis: _Hypothesis

        :return: A list of phrases, represented by their spans, that
            cover untranslated positions.
        :rtype: list(tuple(int, int))
        r   r&   )ra   r,   rM   )all_phrases_fromrC   ra   r2   available_spanrO   available_end
phrase_ends           r   r2   zStackDecoder.valid_phrases0  s    " (::3?O;P;PQQ0 	 	N"1%E*1-M-''"25"9 > >J!M11 !((%)<====
 -'' r   N)__name__
__module____qualname____doc__r   propertyr   setterr   rJ   r0   r1   r5   r4   re   staticmethodr2   r!   r   r   r   r   1   s         :(( (( ((T ( ( X( ( ( (I I I74 74 74r  0" " "H    2G G G   \  r   r   c                   H    e Zd ZdZ	 	 	 	 	 ddZd Zd Zd Zd	 Zd
 Z	d Z
dS )r.   a0  
    Partial solution to a translation.

    Records the word positions of the phrase being translated, its
    translation, raw score, and the cost of the untranslated parts of
    the sentence. When the next phrase is selected to build upon the
    partial solution, a new _Hypothesis object is created, with a back
    pointer to the previous hypothesis.

    To find out which words have been translated so far, look at the
    ``src_phrase_span`` in the hypothesis chain. Similarly, the
    translation output can be found by traversing up the chain.
    r	   r!   Nc                 L    || _         || _        || _        || _        || _        dS )a  
        :param raw_score: Likelihood of hypothesis so far.
            Higher is better. Does not account for untranslated words.
        :type raw_score: float

        :param src_phrase_span: Span of word positions covered by the
            source phrase in this hypothesis expansion. For example,
            (2, 5) means that the phrase is from the second word up to,
            but not including the fifth word in the source sentence.
        :type src_phrase_span: tuple(int)

        :param trg_phrase: Translation of the source phrase in this
            hypothesis expansion
        :type trg_phrase: tuple(str)

        :param previous: Previous hypothesis before expansion to this one
        :type previous: _Hypothesis

        :param future_score: Approximate score for translating the
            remaining words not covered by this hypothesis. Higher means
            that the remaining words are easier to translate.
        :type future_score: float
        N)r'   r(   r)   r*   r5   )r   r'   r(   r)   r*   r5   s         r   r   z_Hypothesis.__init__a  s/    > #.$ (r   c                      | j         | j        z   S )zd
        Overall score of hypothesis after accounting for local and
        global features
        )r'   r5   r   s    r   r]   z_Hypothesis.score  s    
 ~ 111r   c                     |                                  }|                                 |                    |           g }d}|D ]$}||k     r|                    ||f           |dz   }%|S )a.  
        Starting from each untranslated word, find the longest
        continuous span of untranslated positions

        :param sentence_length: Length of source sentence being
            translated by the hypothesis
        :type sentence_length: int

        :rtype: list(tuple(int, int))
        r   r&   )translated_positionssortrM   )r   r=   r|   ra   rO   rP   s         r   ra   z_Hypothesis.untranslated_spans  s      $88::!!#####O444' 	 	Cs{{"))5#,777!GEE!!r   c                     g }| }|j         D|j        }|                    t          |d         |d                              |j         }|j         D|S )z
        List of positions in the source sentence of words already
        translated. The list is not sorted.

        :rtype: list(int)
        Nr   r&   )r*   r(   extendr-   )r   r|   current_hypothesistranslated_spans       r   r|   z _Hypothesis.translated_positions  sf      "! )50@O ''oa.@/RSBT(U(UVVV!3!< !)5 $#r   c                 D    t          |                                           S r   )r,   r|   r   s    r   r6   z"_Hypothesis.total_translated_words  s    4,,..///r   c                 6    g }|                      | |           |S r   )_Hypothesis__build_translation)r   translations     r   r:   z_Hypothesis.translation_so_far  s#      {333r   c                     |j         d S |                     |j         |           |                    |j                   d S r   )r*   r   r   r)   )r   rC   outputs      r   __build_translationz_Hypothesis.__build_translation  sD    &F  !4f===j+,,,,,r   )r	   r!   r!   Nr	   )rq   rr   rs   rt   r   r]   ra   r|   r6   r:   r   r!   r   r   r.   r.   R  s           #) #) #) #)J2 2 2" " "2$ $ $0 0 0  
- - - - -r   r.   c                   B    e Zd ZdZddZd Zd Zd Zd Zd	 Z	d
 Z
e
ZdS )r"   z+
    Collection of _Hypothesis objects
    r
   r	   c                     || _         g | _        |dk    rt          d          | _        dS t	          |          | _        dS )z
        :param beam_threshold: Hypotheses that score less than this
            factor of the best hypothesis are discarded from the stack.
            Value must be between 0.0 and 1.0.
        :type beam_threshold: float
        r	   rU   N)max_sizeitemsrV   _Stack__log_beam_thresholdr   )r   r   r   s      r   r   z_Stack.__init__  sG     !
S  (-fD%%%(+N(;(;D%%%r   c                 B   | j                             |           | j                             d d           t          | j                   | j        k    r6| j                                          t          | j                   | j        k    6|                                  dS )a  
        Add ``hypothesis`` to the stack.
        Removes lowest scoring hypothesis if the stack is full.
        After insertion, hypotheses that score less than
        ``beam_threshold`` times the score of the best hypothesis
        are removed.
        c                 *    |                                  S r   )r]   )hs    r   rW   z_Stack.push.<locals>.<lambda>  s    aggii r   T)keyreverseN)r   rM   r}   r,   r   popthreshold_pruner   rC   s     r   r/   z_Stack.push  s     	
*%%%
//>>>$*oo--JNN $*oo--r   c                     | j         sd S | j         d                                         | j        z   }t          | j                   D ]6}|                                |k     r| j                                          4 d S d S Nr   )r   r]   r   reversedr   )r   	thresholdrC   s      r   r   z_Stack.threshold_prune  s    z 	FJqM''))D,EE	"4:.. 	 	J!!I--
    		 	r   c                 .    | j         r| j         d         S dS )ze
        :return: Hypothesis with the highest score in the stack
        :rtype: _Hypothesis
        r   Nr   r   s    r   r9   z_Stack.best  s    
 : 	!:a= tr   c                 *    t          | j                  S r   )iterr   r   s    r   __iter__z_Stack.__iter__  s    DJr   c                     || j         v S r   r   r   s     r   __contains__z_Stack.__contains__  s    TZ''r   c                 2    t          | j                  dk    S r   )r,   r   r   s    r   __bool__z_Stack.__bool__   s    4:!##r   N)r
   r	   )rq   rr   rs   rt   r   r/   r   r9   r   r   r   __nonzero__r!   r   r   r"   r"     s         < < < <  	 	 	       ( ( ($ $ $ KKKr   r"   )	rt   r7   collectionsr   mathr   r   r.   r"   r!   r   r   <module>r      s   " "H  # # # # # #      ^ ^ ^ ^ ^ ^ ^ ^B	o- o- o- o- o- o- o- o-d? ? ? ? ? ? ? ? ? ?r   