candding 0.1.0

Pure candle embeddings for Rust with no ONNX: dense, sparse, multi-vector and rerank models today, image models on the roadmap
Documentation
use candle_core::DType;
use candle_core::Module;
use candle_core::Tensor;
use candle_nn::Embedding;
use candle_nn::VarBuilder;
use candle_nn::embedding;

use super::config::MPNetConfig;
use super::config::PADDING_IDX;
use crate::error::Result;
use crate::layers::layer_norm::LayerNorm;
use crate::models::xlm_roberta::embeddings::position_ids;

/// No token-type table: `EncoderInput.token_type_ids` is ignored, as for Qwen3 and Gemma3.
pub struct MPNetEmbeddings {
  word: Embedding,
  position: Embedding,
  layer_norm: LayerNorm,
}

impl MPNetEmbeddings {
  pub fn load(config: &MPNetConfig, vb: VarBuilder) -> Result<Self> {
    Ok(Self {
      word: embedding(
        config.vocab_size,
        config.hidden_size,
        vb.pp("word_embeddings"),
      )?,
      position: embedding(
        config.max_position_embeddings,
        config.hidden_size,
        vb.pp("position_embeddings"),
      )?,
      layer_norm: LayerNorm::load(
        vb.pp("LayerNorm"),
        config.hidden_size,
        config.layer_norm_eps,
      )?,
    })
  }

  pub(super) fn weights_dtype(&self) -> DType {
    self.word.embeddings().dtype()
  }

  /// `LayerNorm(word + position)`, with RoBERTa-style position ids from MPNet's literal padding index.
  pub fn forward(&self, input_ids: &Tensor) -> Result<Tensor> {
    let positions = position_ids(input_ids, PADDING_IDX)?;
    let summed = (self.word.forward(input_ids)? + self.position.forward(&positions)?)?;
    Ok(self.layer_norm.forward(&summed)?)
  }
}