use candle_core::{Device, IndexOp, Tensor};
use candle_nn::rnn::{GRU, GRUConfig, GRUState, RNN, gru};
use candle_nn::{Embedding, Linear, Module, VarBuilder, embedding, linear};
pub const MAX_COUNT: usize = 20;
const HIDDEN: usize = 768;
pub struct CountLstmFixed {
gru: GRU,
pos_embedding: Embedding,
projector_0: Linear,
projector_2: Linear,
device: Device,
}
impl CountLstmFixed {
pub fn from_var_builder(vb: &VarBuilder, device: &Device) -> candle_core::Result<Self> {
let gru = gru(HIDDEN, HIDDEN, GRUConfig::default(), vb.pp("gru"))?;
let pos_embedding = embedding(MAX_COUNT, HIDDEN, vb.pp("pos_embedding"))?;
let projector_0 = linear(1536, 3072, vb.pp("projector.0"))?;
let projector_2 = linear(3072, HIDDEN, vb.pp("projector.2"))?;
Ok(Self {
gru,
pos_embedding,
projector_0,
projector_2,
device: device.clone(),
})
}
pub fn forward(&self, field_embs: &Tensor, pred_count: usize, device: &Device) -> candle_core::Result<Tensor> {
let _ = device;
let (f, h) = field_embs.dims2()?;
debug_assert_eq!(h, HIDDEN);
let l = pred_count.min(MAX_COUNT);
if l == 0 {
return Tensor::zeros((0, f, HIDDEN), field_embs.dtype(), &self.device);
}
let pos_ids = Tensor::arange(0u32, l as u32, &self.device)?;
let pos_seq = self.pos_embedding.forward(&pos_ids)?;
let mut state = GRUState {
h: field_embs.contiguous()?,
};
let mut hidden_states: Vec<Tensor> = Vec::with_capacity(l);
for t in 0..l {
let pos_t = pos_seq.i(t)?;
let input_t = pos_t.broadcast_as((f, HIDDEN))?.contiguous()?;
state = self.gru.step(&input_t, &state)?;
hidden_states.push(state.h.clone());
}
let out_h = Tensor::stack(&hidden_states, 0)?;
let field_b = field_embs.unsqueeze(0)?.broadcast_as((l, f, HIDDEN))?.contiguous()?;
let cat = Tensor::cat(&[&out_h, &field_b], 2)?;
let h2 = self.projector_0.forward(&cat)?.relu()?;
let struct_proj = self.projector_2.forward(&h2)?;
Ok(struct_proj)
}
}