LSTM 
=================
一种常用的 循环神经网络（RNN） 模块，用于处理具有时序依赖特征的数据（如语音、文本、时间序列等）。每个时间步的公式化描述如下。

.. math::

    \begin{aligned}
    i_t &= \sigma(W_{ii} x_t + W_{hi} h_{t-1} + b_i) && \text{(输入门)} \\[6pt]
    f_t &= \sigma(W_{if} x_t + W_{hf} h_{t-1} + b_f) && \text{(遗忘门)} \\[6pt]
    g_t &= \tanh(W_{ig} x_t + W_{hg} h_{t-1} + b_g) && \text{(候选状态)} \\[6pt]
    o_t &= \sigma(W_{io} x_t + W_{ho} h_{t-1} + b_o) && \text{(输出门)} \\[6pt]
    c_t &= f_t \odot c_{t-1} + i_t \odot g_t && \text{(细胞状态更新)} \\[6pt]
    h_t &= o_t \odot \tanh(c_t) && \text{(隐藏状态更新)}
    \end{aligned}
    
- :math:`x_t` : 当前时间步输入向量
- :math:`h_{t-1}` : 上一时间步的隐藏状态
- :math:`c_{t-1}` : 上一时间步的细胞状态
- :math:`i_t, f_t, g_t, o_t` : 四个门（输入门、遗忘门、候选门、输出门）
- :math:`W_*` : 对应的权重矩阵
- :math:`b_*` : 偏置项
- :math:`\sigma(\cdot)` : Sigmoid 函数
- :math:`\odot` : 元素乘



输入：
    - **input** - 输入序列数据，形状为  :math:`(seq\_len, batch, input\_size)`，即每个时间步的输入特征。
    - **params** - 静态参数数组，包含 LSTM 网络配置、权重、状态指针等。
        - **weight_i** - 输入到各门 :math:`(input, forget, cell, output)` 的权重矩阵，大小为 4 * hidden_size * input_size。
        - **weight_h** - 上一隐藏状态到各门的权重矩阵，大小为  :math:`4 * hidden\_size * hidden\_size`
        - **input_bias** - 输入部分的偏置项，对应 4 个门的偏置。
        - **state_bias** - 隐藏状态部分的偏置项（也是  :math:`4 * hidden\_size`），与 input_bias 一起求和形成总偏置。
        - **hidden_state** - 当前批次初始隐藏状态输入（  :math:`h_0` ），执行后更新为最后时刻的隐藏状态输出（  :math:`h_t`）
        - **cell_state** - 当前批次初始细胞状态输入（  :math:`c_0`），执行后更新为最后时刻的细胞状态输出（  :math:`c_t`）。
        - **buffer** - 临时工作区指针数组（中间计算缓存，如门值、激活结果、临时矩阵等，用于优化性能）。
        - **LstmParameter** - LSTM 配置参数结构体，包含输入大小、隐藏层维度、序列长度、是否双向等信息。
    - **core_mask** - 核掩码（仅适用于共享存储版本）。

**LstmParameter定义：**

.. code-block:: c
    :linenos:

    typedef struct LstmParameter {
    int input_size_;//每个时间步输入向量的维度（输入特征数）。
    int hidden_size_;//LSTM 隐藏状态的维度（每个门的内部计算大小）。
    int project_size_;//投影层输出维度（用于 LSTMP，有则在输出前线性压缩隐藏状态）。
    int output_size_;//实际输出维度，等于 hidden_size_ 或 project_size_（取决于是否使用投影层）。
    int seq_len_;//输入序列的时间步数（序列长度）。
    int batch_;//批次大小（一次处理的样本数量）。
    // other parameter
    int output_step_;//指定输出第几个时间步的结果（通常为最后一步或每步）。
    bool bidirectional_;//是否为双向 LSTM（true 表示前向和后向各一层）。
    float zoneout_cell_;//单元状态的 Zoneout 比例（防止过拟合的正则化参数）。
    float zoneout_hidden_;//隐藏状态的 Zoneout 比例（防止过拟合）。
    int input_row_align_;//输入张量的行对齐参数（用于 DMA 或 SIMD 加速的内存对齐）。
    int input_col_align_;//输入张量的列对齐参数。
    int state_row_align_;//状态张量（hidden/cell）的行对齐参数。
    int state_col_align_;//状态张量的列对齐参数。
    int proj_col_align_;//投影层矩阵的列对齐参数。
    bool has_bias_;//是否包含偏置项（true 表示使用 bias）。
    } LstmParameter;

输出：

- **output** - 计算结果地址，存放 LSTM 每个时间步输出结果的缓冲区，维度通常为 :math:`(seq\_len, batch, output\_size)`

支持平台：
    ``FT78NE``
    ``MT7004``

.. note::
    - FT78NE 支持fp32
    - MT7004 支持fp32、fp16

**共享存储版本:**

.. c:function:: void fp_lstm_s(float *output, const float *input, unsigned long long * params, int core_mask);
.. c:function:: void hp_lstm_s(half *output, const half *input, unsigned long long * params, int core_mask);

**C调用示例：**

.. code-block:: c
    :linenos:
    :emphasize-lines: 40-42

    //FT78NE示例
    #include <stdio.h>
    #include <lstm.h>
    
    int main(int argc, char* argv[]) {
        LstmParameter *lstm_param = (LstmParameter *)0x90000000;
        lstm_param->seq_len_ = 4;
        lstm_param->batch_ = 1;
        lstm_param->input_size_ = 2;
        lstm_param->hidden_size_ = 3;
        lstm_param->bidirectional_ = false;
        float * input = (float *)0xA0000000;   
        float * weight_i = (float *)0xA0001000;
        float * weight_h = (float *)0xA0003000;
        float * bias = (float *)0xA0005000;
        float *hidden_state = (float *)0xA0006000;
        float *cell_state = (float *)0xA0007000;
        float *buffer[9];
        float * packed_input_ = (float *)0xB0000000;
        buffer[0] = packed_input_;
        float * gate = (float *)0xB0100000;
        buffer[1] = gate;
        float * packed_state = (float *)0xB0200000;
        buffer[2] = packed_state;
        float * state_gate = (float *)0xB0300000;
        buffer[3] = state_gate;
        float * cell_buffer = (float *)0xB0400000;
        buffer[4] = cell_buffer;
        float * hidden_buffer = (float *)0xB0500000;
        buffer[5] = hidden_buffer;
        float * packed_output = (float *)0xB0600000;
        buffer[6] = packed_output;
        float * left_matrix = (float *)0xB0700000;
        buffer[7] = left_matrix;
        float * packed_ptr = (float *)0xB0800000;
        buffer[8] = packed_ptr;
        lstm_param->hidden_size_ = 3;
        lstm_param->output_size_ = 3;

        lstm_param->output_step_ = lstm_param->bidirectional_ ?  2 * lstm_param->batch_ * lstm_param->output_size_
            : lstm_param->batch_ * lstm_param->output_size_;
        int weight_segment_num_ = lstm_param->bidirectional_ ? 2 * 4 : 4;
        int row_tile_ = 12;
        int col_tile_ = 8;
        lstm_param->input_row_align_ = UP_ROUND(lstm_param->seq_len_ * lstm_param->batch_, row_tile_);
        lstm_param->input_col_align_ = UP_ROUND(lstm_param->hidden_size_, col_tile_);
        int state_row_tile_ = row_tile_;
        int state_col_tile_ = col_tile_;
        lstm_param->state_row_align_ = lstm_param->batch_ == 1 ? 1 : UP_ROUND(lstm_param->batch_, state_row_tile_);
        lstm_param->state_col_align_ =
                lstm_param->batch_ == 1 ? lstm_param->hidden_size_ : UP_ROUND(lstm_param->hidden_size_, state_col_tile_);
        lstm_param->proj_col_align_ =
                lstm_param->batch_ == 1 ? lstm_param->output_size_ : UP_ROUND(lstm_param->output_size_, state_col_tile_);
        unsigned long long params[9];
        params[0] = (unsigned long long)weight_i;
        params[1] = (unsigned long long)weight_h;
        params[2] = (unsigned long long)input_bias_;//ok
        params[3] = (unsigned long long)state_bias;
        params[4] = (unsigned long long)hidden_state;
        params[5] = (unsigned long long)cell_state;
        params[6] = (unsigned long long)buffer;
        params[7] = (unsigned long long)lstm_param;
        int core_mask = 0x11;

        fp_lstm_s(output, input, params, core_mask);
            return 0;
    }


**私有存储版本:**

.. c:function:: void fp_lstm_p(float *output, const float *input, unsigned long long * params);
.. c:function:: void hp_lstm_p(half *output, const half *input, unsigned long long * params);
     
        
**C调用示例：**

.. code-block:: c
    :linenos:
    :emphasize-lines: 38-40

    //FT78NE示例
    #include <stdio.h>
    #include <lstm.h>
    int main(int argc, char* argv[]) {
        LstmParameter *lstm_param = (LstmParameter *)0x10810000;
        lstm_param->seq_len_ = 4;
        lstm_param->batch_ = 1;
        lstm_param->input_size_ = 2;
        lstm_param->hidden_size_ = 3;
        lstm_param->bidirectional_ = false;
        float * input = (float *)0x10811000;   
        float * weight_i = (float *)0x10812000;
        float * weight_h = (float *)0x10813000;
        float * bias = (float *)0x10814000;
        float *hidden_state = (float *)0x10814800;
        float *cell_state = (float *)0x10815000;
        float *buffer[9];
        float * packed_input_ = (float *)0x10815f00;
        buffer[0] = packed_input_;
        float * gate = (float *)0x108160000;
        buffer[1] = gate;
        float * packed_state = (float *)0x10816100;
        buffer[2] = packed_state;
        float * state_gate = (float *)0x10816200;
        buffer[3] = state_gate;
        float * cell_buffer = (float *)0x10816300;
        buffer[4] = cell_buffer;
        float * hidden_buffer = (float *)0x10816400;
        buffer[5] = hidden_buffer;
        float * packed_output = (float *)0x10816500;
        buffer[6] = packed_output;
        float * left_matrix = (float *)0x10816600;
        buffer[7] = left_matrix;
        float * packed_ptr = (float *)0x10816700;
        buffer[8] = packed_ptr;
        lstm_param->hidden_size_ = 3;
        lstm_param->output_size_ = 3;

        lstm_param->output_step_ = lstm_param->bidirectional_ ?  2 * lstm_param->batch_ * lstm_param->output_size_
            : lstm_param->batch_ * lstm_param->output_size_;
        int weight_segment_num_ = lstm_param->bidirectional_ ? 2 * 4 : 4;
        int row_tile_ = 12;
        int col_tile_ = 8;
        lstm_param->input_row_align_ = UP_ROUND(lstm_param->seq_len_ * lstm_param->batch_, row_tile_);
        lstm_param->input_col_align_ = UP_ROUND(lstm_param->hidden_size_, col_tile_);
        int state_row_tile_ = row_tile_;
        int state_col_tile_ = col_tile_;
        lstm_param->state_row_align_ = lstm_param->batch_ == 1 ? 1 : UP_ROUND(lstm_param->batch_, state_row_tile_);
        lstm_param->state_col_align_ =
                lstm_param->batch_ == 1 ? lstm_param->hidden_size_ : UP_ROUND(lstm_param->hidden_size_, state_col_tile_);
        lstm_param->proj_col_align_ =
                lstm_param->batch_ == 1 ? lstm_param->output_size_ : UP_ROUND(lstm_param->output_size_, state_col_tile_);
        unsigned long long params[9];
        params[0] = (unsigned long long)weight_i;
        params[1] = (unsigned long long)weight_h;
        params[2] = (unsigned long long)input_bias_;//ok
        params[3] = (unsigned long long)state_bias;
        params[4] = (unsigned long long)hidden_state;
        params[5] = (unsigned long long)cell_state;
        params[6] = (unsigned long long)buffer;
        params[7] = (unsigned long long)lstm_param;
        int core_mask = 0x11;

        fp_lstm_s(output, input, params, core_mask);
        return 0;
    }