
    wfh)#                    j    d dl mZ d dlZd dlmZmZ d dlmZ d dlmZ  G d dej                        Z
y)    )annotationsN)Tensornn)CrossEncoder)fullnamec                  t     e Zd Z ej                         df	 	 	 	 	 	 	 d fdZddZddZed	d       Z	 xZ
S )
ListNetLossNc                N   t         |           || _        |xs t        j                         | _        || _        t        j                         | _        | j                  j                  dk7  r9t        | j                  j                   d| j                  j                   d      y)a  
        ListNet loss for learning to rank. This loss function implements the ListNet ranking algorithm
        which uses a list-wise approach to learn ranking models. It minimizes the cross entropy
        between the predicted ranking distribution and the ground truth ranking distribution.
        The implementation is optimized to handle padded documents efficiently by only processing
        valid documents during model inference.

        .. note::

            The number of documents per query can vary between samples with the ``ListNetLoss``.

        Args:
            model (CrossEncoder): CrossEncoder model to be trained
            activation_fn (:class:`~torch.nn.Module`): Activation function applied to the logits before computing the
                loss. Defaults to :class:`~torch.nn.Identity`.
            mini_batch_size (int, optional): Number of samples to process in each forward pass. This has a significant
                impact on the memory consumption and speed of the training process. Three cases are possible:

                - If ``mini_batch_size`` is None, the ``mini_batch_size`` is set to the batch size.
                - If ``mini_batch_size`` is greater than 0, the batch is split into mini-batches of size ``mini_batch_size``.
                - If ``mini_batch_size`` is <= 0, the entire batch is processed at once.

                Defaults to None.

        References:
            - Learning to Rank: From Pairwise Approach to Listwise Approach: https://www.microsoft.com/en-us/research/publication/learning-to-rank-from-pairwise-approach-to-listwise-approach/
            - Context-Aware Learning to Rank with Self-Attention: https://arxiv.org/abs/2005.10084
            - `Cross Encoder > Training Examples > MS MARCO <../../../examples/cross_encoder/training/ms_marco/README.html>`_

        Requirements:
            1. Query with multiple documents (listwise approach)
            2. Documents must have relevance scores/labels. Both binary and continuous labels are supported.

        Inputs:
            +----------------------------------------+--------------------------------+-------------------------------+
            | Texts                                  | Labels                         | Number of Model Output Labels |
            +========================================+================================+===============================+
            | (query, [doc1, doc2, ..., docN])       | [score1, score2, ..., scoreN]  | 1                             |
            +----------------------------------------+--------------------------------+-------------------------------+

        Recommendations:
            - Use :class:`~sentence_transformers.util.mine_hard_negatives` with ``output_format="labeled-list"``
              to convert question-answer pairs to the required input format with hard negatives.

        Relations:
            - :class:`~sentence_transformers.cross_encoder.losses.LambdaLoss` takes the same inputs, and generally
              outperforms this loss.

        Example:
            ::

                from sentence_transformers.cross_encoder import CrossEncoder, CrossEncoderTrainer, losses
                from datasets import Dataset

                model = CrossEncoder("microsoft/mpnet-base")
                train_dataset = Dataset.from_dict({
                    "query": ["What are pandas?", "What is the capital of France?"],
                    "docs": [
                        ["Pandas are a kind of bear.", "Pandas are kind of like fish."],
                        ["The capital of France is Paris.", "Paris is the capital of France.", "Paris is quite large."],
                    ],
                    "labels": [[1, 0], [1, 1, 0]],
                })
                loss = losses.ListNetLoss(model)

                trainer = CrossEncoderTrainer(
                    model=model,
                    train_dataset=train_dataset,
                    loss=loss,
                )
                trainer.train()
           z< supports a model with 1 output label, but got a model with z output labels.N)super__init__modelr   Identityactivation_fnmini_batch_sizeCrossEntropyLosscross_entropy_loss
num_labels
ValueError	__class____name__)selfr   r   r   r   s       t/home/chris/cleankitchens-env/lib/python3.12/site-packages/sentence_transformers/cross_encoder/losses/ListNetLoss.pyr   zListNetLoss.__init__   s    \ 	
*;bkkm."$"5"5"7::  A%>>**+ ,((,

(=(='>oO  &    c                   t        |t              rt        d      t        |      dk7  rt        dt        |       d      |\  }}|D cg c]  }t        |       }}t	        |      }t        |      }||D cg c]  }t        |       c}k7  r)t        d| dD cg c]  }t        |       c} d      t        ||      D 	
cg c]  \  }	}|D ]  }
|	|
f  }}}	}
|s,t        j                  d| j                  j                  d	
      S | j                  xs |}|dk  rt        |      }g }t        dt        |      |      D ]  }||||z    }| j                  j                  |d	d	d      }|j                  | j                  j                        } | j                  di |d   j                  d      }|j                  |        t        j                   |d      }| j#                  |      }t        j$                  ||fd| j                  j                        }t        j                   |D cg c]   }t        j&                  t        |            " c}d      }t        j(                  t        j&                  |      t        j                  |            }||||f<   t        j*                  |t-        d            }t        j                   d      j-                         |||f<   |j                  | j                  j                        }| j/                  ||j1                  d            }|S c c}w c c}w c c}w c c}
}}	w c c}w )a3  
        Compute ListNet loss for a batch of queries and their documents.

        Args:
            inputs: List of (queries, documents_list)
            labels: Ground truth relevance scores, shape (batch_size, num_documents)

        Returns:
            Tensor: Mean ListNet loss over the batch
        z]ListNetLoss expects a list of labels for each sample, but got a single value for each sample.   zBListNetLoss expects two inputs (queries, documents_list), but got z inputs.z)Number of documents per query in inputs (z-) does not match number of labels per query (z).g        T)devicerequires_gradr   pt)padding
truncationreturn_tensors)dimg 7yA)r   z-infr    )
isinstancer   r   lenmaxziptorchtensorr   r   r   range	tokenizertoviewappendcatr   fullarangerepeat_interleave	full_likefloatr   softmax)r   inputslabelsqueries	docs_listdocsdocs_per_querymax_docs
batch_sizequerydocumentpairsr   logits_listimini_batch_pairstokenslogitslogits_matrixdoc_indicesbatch_indiceslabels_matrixlosss                          r   forwardzListNetLoss.forwarde   sO    ff%o  v;!TUXY_U`Taaij  $09:#d)::~&\
?fc&k??;N;KKx  U[  z\  KQz}  E  {F  z\  y]  ]_  `  7:'96Mcc{ud^bcRZ%"c"cc<<DJJ,=,=TRR..<*a!%jOq#e*o6 	'A$Q_)<=ZZ)) #	 * F YYtzz001FTZZ)&)!,11"5Fv&	' ;A.##F+ 

J#95IZIZ[ iiY OTc$i!8 OUVW//Z0H%,,WeJfg4:m[01 uV}E49IIf!4L4R4R4Tm[01%(():):; &&}m6K6KPQ6K6RSk ; @ z\ d@ !Ps   L;:M M
M
8%Mc                F    t        | j                        | j                  dS )z
        Get configuration parameters for this loss function.

        Returns:
            Dictionary containing the configuration parameters
        )r   r   )r   r   r   r   s    r   get_config_dictzListNetLoss.get_config_dict   s!     "*$*<*<!=RVRfRfggr   c                     y)Na@  
@inproceedings{cao2007learning,
    title={Learning to Rank: From Pairwise Approach to Listwise Approach},
    author={Cao, Zhe and Qin, Tao and Liu, Tie-Yan and Tsai, Ming-Feng and Li, Hang},
    booktitle={Proceedings of the 24th international conference on Machine learning},
    pages={129--136},
    year={2007}
}
r%   rO   s    r   citationzListNetLoss.citation   s    r   )r   r   r   znn.Module | Noner   z
int | NonereturnNone)r8   z list[list[str], list[list[str]]]r9   zlist[Tensor]rS   r   )rS   zdict[str, float])rS   str)r   
__module____qualname__r   r   r   rM   rP   propertyrR   __classcell__)r   s   @r   r	   r	   
   se     +6"++-&*	XX (X $	X
 
XtKZh 	 	r   r	   )
__future__r   r*   r   r   #sentence_transformers.cross_encoderr   sentence_transformers.utilr   Moduler	   r%   r   r   <module>r^      s&    "   < /{")) {r   