use crate::error::Error;
use crate::math_helper;
use crate::structs;
use icu_provider::DataPayload;
use ndarray::{Array1, Array2, ArrayBase, Dim, ViewRepr};
use std::str;
use unicode_segmentation::UnicodeSegmentation;
pub struct Lstm {
data: DataPayload<structs::LstmDataMarker>,
}
impl Lstm {
pub fn try_new(data: DataPayload<structs::LstmDataMarker>) -> Result<Self, Error> {
if data.get().dic.len() > std::i16::MAX as usize {
return Err(Error::Limit);
}
if !data.get().model.contains("_codepoints_") && !data.get().model.contains("_graphclust_")
{
return Err(Error::Syntax);
}
let embedd_dim = data.get().mat1.shape()[1];
let hunits = data.get().mat3.shape()[0];
if data.get().mat2.shape() != [embedd_dim, 4 * hunits]
|| data.get().mat3.shape() != [hunits, 4 * hunits]
|| data.get().mat4.shape() != [4 * hunits]
|| data.get().mat5.shape() != [embedd_dim, 4 * hunits]
|| data.get().mat6.shape() != [hunits, 4 * hunits]
|| data.get().mat7.shape() != [4 * hunits]
|| data.get().mat8.shape() != [2 * hunits, 4]
|| data.get().mat9.shape() != [4]
{
return Err(Error::DimensionMismatch);
}
Ok(Self { data })
}
pub fn get_model_name(&self) -> &str {
&self.data.get().model
}
fn compute_bies(&self, arr: Array1<f32>) -> Result<char, Error> {
let ind = math_helper::max_arr1(arr.view());
match ind {
0 => Ok('b'),
1 => Ok('i'),
2 => Ok('e'),
3 => Ok('s'),
_ => Err(Error::Syntax),
}
}
fn return_id(&self, g: &str) -> i16 {
*self
.data
.get()
.dic
.get(g)
.unwrap_or(&(self.data.get().dic.len() as i16))
}
fn compute_hc(
&self,
x_t: ArrayBase<ViewRepr<&f32>, Dim<[usize; 1]>>,
h_tm1: &Array1<f32>,
c_tm1: &Array1<f32>,
warr: ArrayBase<ViewRepr<&f32>, Dim<[usize; 2]>>,
uarr: ArrayBase<ViewRepr<&f32>, Dim<[usize; 2]>>,
barr: ArrayBase<ViewRepr<&f32>, Dim<[usize; 1]>>,
) -> (Array1<f32>, Array1<f32>) {
let s_t = x_t.dot(&warr) + h_tm1.dot(&uarr) + barr;
let hunits = uarr.shape()[0];
let i = math_helper::sigmoid_arr1(s_t.slice(ndarray::s![..hunits]));
let f = math_helper::sigmoid_arr1(s_t.slice(ndarray::s![hunits..2 * hunits]));
let _c = math_helper::tanh_arr1(s_t.slice(ndarray::s![2 * hunits..3 * hunits]));
let o = math_helper::sigmoid_arr1(s_t.slice(ndarray::s![3 * hunits..]));
let c_t = i * _c + f * c_tm1;
let h_t = o * math_helper::tanh_arr1(c_t.view());
(h_t, c_t)
}
pub fn word_segmenter(&self, input: &str) -> String {
let input_seq: Vec<i16> = if self.data.get().model.contains("_codepoints_") {
input
.chars()
.map(|c| self.return_id(&c.to_string()))
.collect()
} else {
UnicodeSegmentation::graphemes(input, true)
.map(|s| self.return_id(s))
.collect()
};
let input_seq_len = input_seq.len();
let hunits = self.data.get().mat3.shape()[0];
let mut c_fw = Array1::<f32>::zeros(hunits);
let mut h_fw = Array1::<f32>::zeros(hunits);
let mut all_h_fw = Array2::<f32>::zeros((input_seq_len, hunits));
for (i, g_id) in input_seq.iter().enumerate() {
let x_t = self.data.get().mat1.slice(ndarray::s![*g_id as isize, ..]);
let (new_h, new_c) = self.compute_hc(
x_t,
&h_fw,
&c_fw,
self.data.get().mat2.view(),
self.data.get().mat3.view(),
self.data.get().mat4.view(),
);
h_fw = new_h;
c_fw = new_c;
all_h_fw = math_helper::change_row(all_h_fw, i, &h_fw);
}
let mut c_bw = Array1::<f32>::zeros(hunits);
let mut h_bw = Array1::<f32>::zeros(hunits);
let mut all_h_bw = Array2::<f32>::zeros((input_seq_len, hunits));
for (i, g_id) in input_seq.iter().rev().enumerate() {
let x_t = self.data.get().mat1.slice(ndarray::s![*g_id as isize, ..]);
let (new_h, new_c) = self.compute_hc(
x_t,
&h_bw,
&c_bw,
self.data.get().mat5.view(),
self.data.get().mat6.view(),
self.data.get().mat7.view(),
);
h_bw = new_h;
c_bw = new_c;
all_h_bw = math_helper::change_row(all_h_bw, input_seq_len - 1 - i, &h_bw);
}
let timew = self.data.get().mat8.view();
let timeb = self.data.get().mat9.view();
let mut bies = String::from("");
for i in 0..input_seq_len {
let curr_fw = all_h_fw.slice(ndarray::s![i, ..]);
let curr_bw = all_h_bw.slice(ndarray::s![i, ..]);
let concat_lstm = math_helper::concatenate_arr1(curr_fw, curr_bw);
let curr_est = concat_lstm.dot(&timew) + timeb;
let probs = math_helper::softmax(curr_est);
bies.push(self.compute_bies(probs).unwrap());
}
bies
}
}