
    .ɷi2W                        d dl mZ d dlZd dlmZmZ d dlmZmZ d dl	Z	d dl	m
Z
mZ d dlmZ d dlmZ d dlmZ  ej$                  e      Z G d	 d
ej*                        Zy)    )annotationsN)CallableIterable)AnyLiteral)Tensornn)util)SentenceTransformer)all_gather_with_gradc                       e Zd Zdej                  dddddf	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d fdZddZdd	Zdd
Ze	dd       Z
e	dd       Z xZS )MultipleNegativesRankingLossg      4@F)query_to_docjointN        c	                l   t         |           || _        || _        |dk  rt	        d      || _        || _        h d}	|st	        d      t        |      j                  |	      st	        dt        |      |	z
   d|	       d|vrt	        d      t        |      | _
        |d	vrt	        d
|       |dk(  rt        |      ddhz  rt	        d      || _        h d}
||
vrt	        d|
 d|      || _        |dk  rt	        d      || _        | |dk(  rt        j                  d|d       yyy)u'  
        Given a dataset of (anchor, positive) pairs, (anchor, positive, negative) triplets, or (anchor, positive, negative_1, ..., negative_n)
        n-tuples, this loss implements a contrastive learning objective that encourages the model to produce similar
        embeddings for the anchor and positive samples, while producing dissimilar embeddings for the negative samples.

        In plain terms, the loss works as follows:

        1. For each anchor (often a query) in the batch, we want the similarity to its matched positive
           (often a document) to be higher than the similarity to all other documents in the batch (including
           optional hard negatives). This is the standard forward MultipleNegativesRankingLoss / InfoNCE term,
           denoted with "query_to_doc".
        2. Optionally, we can also require the opposite: for each document, its matched query should have higher
           similarity than all other queries in the batch. This is the symmetric backward term, denoted with
           "doc_to_query".
        3. Optionally, we can further require that for each query, its similarity to all other queries in the batch
           is lower than to its matched document. This is the "query_to_query" term.
        4. Optionally, we can also require that for each document, its similarity to all other documents in the batch
           is lower than to its matched query. This excludes documents that belong to the same query in the case of
           hard negatives (i.e. columns beyond the first two in the input). This is the "doc_to_doc" term.

        All of these are implemented via different choices of interaction directions and how we normalize
        the scores, but they all share the same core idea: the correct pair (query, positive) should have
        the highest similarity compared to all in-batch alternatives.

        All of these are expressed via the same underlying formulation by choosing different
        ``directions`` and ``partition_mode`` values. Optional negatives in the input are treated as
        additional hard-negative documents for the corresponding query.

        The default configuration is also known as the InfoNCE loss, SimCSE loss, cross-entropy loss with in-batch
        negatives, or simply in-batch negatives loss.

        Args:
            model: SentenceTransformer model
            scale: Output of similarity function is multiplied by scale value. In some literature, the scaling parameter
                is referred to as temperature, which is the inverse of the scale. In short: ``scale = 1 / temperature``, so
                ``scale=20.0`` is equivalent to ``temperature=0.05``. A higher scale (lower temperature) puts more emphasis
                on the positive example, and values between 10 and 100 are common.
            similarity_fct: similarity function between sentence embeddings. By default, cos_sim. Can also be set to
                dot product (and then set scale to 1)
            gather_across_devices: If True, gather the embeddings across all devices before computing the loss.
                Recommended when training on multiple GPUs, as it allows for larger batch sizes, but it may slow down
                training due to communication overhead, and can potentially lead to out-of-memory errors.
            directions: Which similarity interaction terms to include in the loss. Options:

                - "query_to_doc": query -> all documents (always included as it covers the paired positive).
                - "query_to_query": query -> all other queries in the batch.
                - "doc_to_query": document -> all queries (symmetric term).
                - "doc_to_doc": document -> all other documents in the batch, excluding those belonging to the same query.

                The default ("query_to_doc",) matches the standard MultipleNegativesRankingLoss / InfoNCE behavior.
            partition_mode: How to normalize the scores (the softmax denominator):
                - "joint": One joint softmax over all selected directions.
                - "per_direction": One softmax per direction. A loss is computed for each direction and then averaged.
                  Not compatible with ``"query_to_query"`` or ``"doc_to_doc"`` directions.
            hardness_mode: Strategy for applying hardness weighting. ``None`` (default) disables hardness
                weighting entirely. Options:

                - ``"in_batch_negatives"``: Adds ``hardness_strength * stop_grad(cos_sim)`` to every in-batch negative
                  logit inside the softmax (`Lan et al. 2025 <https://huggingface.co/papers/2503.04812>`_, Eq. 5). The
                  in-batch negatives are all positives and hard negatives from other samples in the batch.
                  Works with all data formats including pairs-only.
                - ``"hard_negatives"``: Applies ``hardness_strength * stop_grad(cos_sim)`` only to the logits of
                  explicit hard negatives, leaving in-batch negatives unpenalized. Only active when explicit
                  negatives are provided. As used in
                  `Schechter Vera et al. 2025 <https://huggingface.co/papers/2509.20354>`_ (EmbeddingGemma).
                - ``"all_negatives"``: Applies ``hardness_strength * stop_grad(cos_sim)`` to every negative logit,
                  both in-batch negatives and explicit hard negatives, leaving only the positive unpenalized.
                  Combines the effect of ``"in_batch_negatives"`` and ``"hard_negatives"``.

            hardness_strength: Strength of the hardness weighting. The meaning depends on ``hardness_mode``:

                - For ``"in_batch_negatives"``: acts as ``alpha`` in the hardness penalty, `Lan et al. 2025 <https://huggingface.co/papers/2503.04812>`_ uses 9.
                - For ``"hard_negatives"``: acts as ``alpha`` in the hardness penalty, `Schechter Vera et al. 2025 <https://huggingface.co/papers/2509.20354>`_ uses 5.

                Must be non-negative. Ignored when ``hardness_mode`` is ``None``.

        Requirements:
            1. (anchor, positive) pairs, (anchor, positive, negative) triplets, or (anchor, positive, negative_1, ..., negative_n) n-tuples

        Inputs:
            +-------------------------------------------------+--------+
            | Texts                                           | Labels |
            +=================================================+========+
            | (anchor, positive) pairs                        | none   |
            +-------------------------------------------------+--------+
            | (anchor, positive, negative) triplets           | none   |
            +-------------------------------------------------+--------+
            | (anchor, positive, negative_1, ..., negative_n) | none   |
            +-------------------------------------------------+--------+

        Recommendations:
            - Use ``BatchSamplers.NO_DUPLICATES`` (:class:`docs <sentence_transformers.training_args.BatchSamplers>`) to
              ensure that no in-batch negatives are duplicates of the anchor or positive samples.

        Relations:
            - :class:`CachedMultipleNegativesRankingLoss` is equivalent to this loss, but it uses caching that allows for
              much higher batch sizes (and thus better performance) without extra memory usage. However, it is slightly
              slower.
            - :class:`GISTEmbedLoss` is equivalent to this loss, but uses a guide model to guide the in-batch negative
              sample selection. `GISTEmbedLoss` yields a stronger training signal at the cost of some training overhead.

        Loss variants from the literature:
            - Standard InfoNCE / classic MultipleNegativesRankingLoss (query -> doc only), e.g. as in `van den Oord et al. 2018 <https://arxiv.org/abs/1807.03748>`_::

                loss = MultipleNegativesRankingLoss(
                    model,
                    directions=("query_to_doc",),  # default
                    partition_mode="joint",  # default
                )

              This variant is recommended if you are training with (anchor, positive, negative_1, ..., negative_n) n-tuples.

            - Symmetric InfoNCE (query -> doc and doc -> query), e.g. as in `Günther et al. 2024 <https://arxiv.org/abs/2310.19923>`_::

                loss = MultipleNegativesRankingLoss(
                    model,
                    directions=("query_to_doc", "doc_to_query"),
                    partition_mode="per_direction",  # forward/backward computed separately and averaged
                )

              This variant may outperform the standard variant in some scenarios.

            - GTE improved contrastive loss (query/doc + same-type negatives), e.g. as in `Li et al. 2023 <https://arxiv.org/abs/2308.03281>`_::

                loss = MultipleNegativesRankingLoss(
                    model,
                    directions=("query_to_doc", "query_to_query", "doc_to_query", "doc_to_doc"),
                    partition_mode="joint",  # single softmax over all selected interaction terms
                )

              This variant is recommended if you are training with only (anchor, positive) pairs or (anchor, positive, negative)
              triplets, as it may provide a stronger training signal.

        Example:
            ::

                from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses
                from datasets import Dataset

                model = SentenceTransformer("microsoft/mpnet-base")
                train_dataset = Dataset.from_dict({
                    "anchor": ["It's nice weather outside today.", "He drove to work."],
                    "positive": ["It's so sunny.", "He took the car to the office."],
                })
                loss = losses.MultipleNegativesRankingLoss(model)

                trainer = SentenceTransformerTrainer(
                    model=model,
                    train_dataset=train_dataset,
                    loss=loss,
                )
                trainer.train()
        r   zScale must be a positive value.>   
doc_to_docdoc_to_queryr   query_to_queryz)At least one direction must be specified.zInvalid directions: z	. Valid: r   zB'query_to_doc' direction is required (contains the positive pair).)r   per_directionz7partition_mode must be 'joint' or 'per_direction', got r   r   r   a  partition_mode='per_direction' requires every direction's candidate pool to include the positive pair. 'query_to_query' and 'doc_to_doc' only contain same-type similarities and never include the positive, making the per-direction loss ill-defined. Use partition_mode='joint' instead.>   Nall_negativeshard_negativesin_batch_negativeszhardness_mode must be one of z, got r   z'hardness_strength must be non-negative.Nzhardness_mode=z is set but hardness_strength=0.0, so hardness weighting has no effect. Set hardness_strength to a positive value to enable hardness weighting.)super__init__modelscale
ValueErrorsimilarity_fctgather_across_devicessetissubsettuple
directionspartition_modehardness_modehardness_strengthloggerwarning)selfr   r   r   r    r$   r%   r&   r'   valid_directionsvalid_hardness_modes	__class__s              l/var/www/html/venv/lib/python3.12/site-packages/sentence_transformers/losses/MultipleNegativesRankingLoss.pyr   z%MultipleNegativesRankingLoss.__init__   s   N 	

A:>??,%:"[HII:''(893C
OFV4V3WW`aq`rstt+abb
+!;;VWeVfghh_,ZDTVbCc1c a 
 -^ 44<=Q<RRXYfXijkk*s"FGG!2$):c)ANN  1 2b b *B$    c                r    |D cg c]  }| j                  |      d    }}| j                  ||      S c c}w )Nsentence_embedding)r   compute_loss_from_embeddings)r*   sentence_featureslabelssentence_feature
embeddingss        r.   forwardz$MultipleNegativesRankingLoss.forward   s?    arsM]djj!123GHs
s00VDD ts   4c                R   t        |      dk  rt        dt        |             |d   }|dd  }|j                  d      }d}| j                  rdt	        |      }|D cg c]  }t	        |       }}t
        j                  j                         r#t
        j                  j                         }||z  }|j                  d      }	t        j                  |d      }
|d   }t        j                  |||z   |j                        }t        j                  ||j                        }||   }||   }i }| j                  ||
      |d<   d| j                  v r.| j                  ||      |d<   t
        j                   |d   ||f<   d	| j                  v r| j                  ||      j                  |d	<   d
| j                  v r| j                  |
|      j                  |d
<   t        j                   |	|j                        |   }|j#                  dt        |            j%                         }|d
   j'                  |t
        j                          i }| j(                  dv r| j*                  dkD  r| j*                  |d   j-                         z  }t        j                   |	|j                  t
        j$                        |   }|j#                  dt        |            }| j(                  dk(  r| }d|d d d |	f<   n.| j(                  dk(  r|}n| j(                  dk(  r|}d|d d |	d f<   d|<   ||d<   |D ]  }||   | j.                  z  ||<    |j1                         D ]  \  }}||   |z   ||<    |d   ||f   }| j2                  dk(  rFt        j                  t5        |j7                               d      }t        j8                  |d      }n?d}|j7                         D ]  }|t        j8                  |d      z  } |t        |      z  }||z
  j;                          }|S c c}w )N   z$Expected at least 2 embeddings, got r      )dim)devicer   r   r   r   )r   r   r   r   )r<   dtyper   Tr   r   Fr   )lenr   sizer    r   torchdistributedis_initializedget_rankcataranger<   r   r$   infTeyerepeatboolmasked_fill_r&   r'   detachr   itemsr%   listvalues	logsumexpmean)r*   r6   r4   queriesdocs
batch_sizeoffsetdocrankworld_batch_sizedocs_alldocs_poslocal_indicesrow_indiceslocal_queries
local_docssim_matricessame_query_doc_mask	penaltiespenaltyown_doc_maskpenalty_exclusion_maskkeypenpositive_scoresscoreslog_z
sim_matrixlosss                                r.   r2   z9MultipleNegativesRankingLoss.compute_loss_from_embeddings   s%   z?QCC
OCTUVVQ-!"~\\!_
%% +73G9=>#(->D>  //1((113
*"<<?99Tq)7VVj-@Xll:gnnE.m,
'+':':=('S^$t.-1-@-@PW-XL)*JO))L)*;+EFT__,+/+>+>w
+S+U+UL(4??*)-)<)<Xz)R)T)TL&"')),<W^^"TUb"c"5"<"<QD	"J"O"O"Q&334G%))T
 	"[[&&,,,|N/K/R/R/TTG !99%5gnnTYT^T^_`mnL'..q#d)<L!!%55*6&?C&q*;+;*;';<##';;)5&##6)5&?D&q*:*;';<.1G*+(/In%   	?C ,S 1DJJ >L	?!) 	8HC ,S 1C 7L	8 '~6{M7QR')YYtL$7$7$9:BFOOF2E E*113 <
;;<S&&E 5(..00q ?s   P$c                    | j                   | j                  j                  | j                  | j                  | j
                  | j                  | j                  dS )N)r   r   r    r$   r%   r&   r'   )r   r   __name__r    r$   r%   r&   r'   r*   s    r.   get_config_dictz,MultipleNegativesRankingLoss.get_config_dictS  sM    ZZ"11::%)%?%?//"11!//!%!7!7
 	
r/   c                     d| j                   z  S )Ng      ?)r   rn   s    r.   temperaturez(MultipleNegativesRankingLoss.temperature^  s    TZZr/   c                    t        | j                        h dk(  r| j                  dk(  ryt        | j                        ddhk(  r| j                  dk(  ryy)	N>   r   r   r   r   r   a  
@misc{li2023generaltextembeddingsmultistage,
      title={Towards General Text Embeddings with Multi-stage Contrastive Learning},
      author={Zehan Li and Xin Zhang and Yanzhao Zhang and Dingkun Long and Pengjun Xie and Meishan Zhang},
      year={2023},
      eprint={2308.03281},
      archivePrefix={arXiv},
      primaryClass={cs.CL},
      url={https://arxiv.org/abs/2308.03281},
}
r   r   r   u  
@misc{günther2024jinaembeddings28192token,
      title={Jina Embeddings 2: 8192-Token General-Purpose Text Embeddings for Long Documents},
      author={Michael Günther and Jackmin Ong and Isabelle Mohr and Alaeddine Abdessalem and Tanguy Abel and Mohammad Kalim Akram and Susana Guzman and Georgios Mastrapas and Saba Sturua and Bo Wang and Maximilian Werk and Nan Wang and Han Xiao},
      year={2024},
      eprint={2310.19923},
      archivePrefix={arXiv},
      primaryClass={cs.CL},
      url={https://arxiv.org/abs/2310.19923},
}
a_  
@misc{oord2019representationlearningcontrastivepredictive,
      title={Representation Learning with Contrastive Predictive Coding},
      author={Aaron van den Oord and Yazhe Li and Oriol Vinyals},
      year={2019},
      eprint={1807.03748},
      archivePrefix={arXiv},
      primaryClass={cs.LG},
      url={https://arxiv.org/abs/1807.03748},
}
)r!   r$   r%   rn   s    r.   citationz%MultipleNegativesRankingLoss.citationb  sY      $dd##w.
 tNN#CCH[H[_nHn

r/   )r   r   r   floatr   z"Callable[[Tensor, Tensor], Tensor]r    rJ   r$   zStuple[Literal['query_to_doc', 'query_to_query', 'doc_to_query', 'doc_to_doc'], ...]r%   z!Literal['joint', 'per_direction']r&   zGLiteral['in_batch_negatives', 'hard_negatives', 'all_negatives'] | Noner'   rt   returnNone)r3   zIterable[dict[str, Tensor]]r4   r   ru   r   )r6   zlist[Tensor]r4   r   ru   r   )ru   zdict[str, Any])ru   rt   )ru   str)rm   
__module____qualname__r
   cos_simr   r7   r2   ro   propertyrq   rs   __classcell__)r-   s   @r.   r   r      s     =A\\&+ <Cae#&R"R R ;	R
  $R
R :R _R !R 
RhE
fP	
     & &r/   r   )
__future__r   loggingcollections.abcr   r   typingr   r   r@   r   r	   sentence_transformersr
   )sentence_transformers.SentenceTransformerr   sentence_transformers.utilr   	getLoggerrm   r(   Moduler    r/   r.   <module>r      sD    "  .    & I ;			8	$x299 xr/   