Mulgrad
=================


计算逐元素乘法 (Mul) 操作的梯度。该算子是 Mul 算子的反向传播（backward pass）部分。梯度的计算遵循链式法则。

.. math::

    \text{dx0}_i = \text{dy}_i \times \text{Input1}_i

    \text{dx1}_i = \text{dy}_i \times \text{Input0}_i

其中 dx0 和 dx1 分别是损失函数对前向输入 Input0 和 Input1 的梯度。

Gradmul1L版本专门用于 `x1` 张量维度大于或等于 `x2` 张量的广播场景。Gradmul2l版本专门用于 `x2` 张量维度大于或等于 `x1` 张量的广播场景。

输入：
    - **dy** - 来自后一层的上游梯度张量。
    - **x1** - 前向传播时的第一个输入张量（被除数）。
    - **x2** - 前向传播时的第二个输入张量（除数）。
    - **params** - 参数打包成结构体Parameter：
        - **tile_data0** - 临时工作空间地址。
        - **tile_data1** - 临时工作空间地址。
        - **large_shape** - `x1` 和 `x2` 中维度较大的张量的形状。
        - **small_shape** - `x1` 和 `x2` 中维度较小的张量的形状。
        - **out_shape** - 输出张量 `dx1` 和 `dx2` 的形状。
        - **ndims** - 张量的维度数。
        - **dy_size** - dy元素个数，需要初始化。
        - **x1_size** - x1元素个数，需要初始化。
        - **x2_size** - x2元素个数，需要初始化。
        - **large_strides** - 维度较大张量的步长信息。
        - **small_strides** - 维度较小张量的步长信息。
        - **out_strides** - 输出张量的步长信息。
        - **large_multiples** - 维度较大张量的广播倍数。
        - **small_multiples** - 维度较小张量的广播倍数。
        - **indices** - 用于广播计算的临时索引空间地址。
        - **x1_shape** - x1的维度信息地址。
        - **x2_shape** - x2的维度信息地址。
    - **core_mask** - 核掩码。

输出：
    - **dx1** - 写入计算出的对 `x1` 的梯度。
    - **dx2** - 写入计算出的对 `x2` 的梯度。

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

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

**共享存储版本:**

.. c:function:: void fp_mul_grad_s(float* dy, float *dx1, float *dx2, float* x1, float* x2, Parameter *params, int core_mask)
.. c:function:: void hp_mul_grad_s(half* dy, half *dx1, half *dx2, half* x1, half* x2, Parameter *params, int core_mask)


**C调用示例：**

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

    //FT78NE示例
    #include <stdio.h>
    #include <divgrad.h>
    int main(int argc, char* argv[]) {
        float *dy = (float *)0x81000000;//输入，初始化
        float *dx1 = (float *)0x82000000;//输出，需要初始化
        float *dx2 = (float *)0x83000000;//输出，需要初始化
        float *x1_data = (float *)0x84000000;//输入，需要初始化
        float *x2_data = (float *)0x85000000;//输入，需要初始化
        float *tile_data0 = (float *)0x86000000;//中间结果，不需要初始化
        float *tile_data1 = (float *)0x87000000;//中间结果，不需要初始化

        long long ndims = 4;
        long long dy_size;
        long long x1_size;
        long long x2_size;

        int *large_strides = (int *)0x91000000;//不需要初始化
        int *small_strides = (int *)0x91001000; //不需要初始化
        int *out_strides = (int *)0x91002000; //不需要初始化
        int *large_multiples = (int *)0x91003000; //不需要初始化
        int *small_multiples = (int *)0x91004000; //不需要初始化
        int *indices = (int *)0x91005000;
        float *check_dx1 = (float *)0x91006000;
        float *check_dx2 = (float *)0x1007000;

        int i = 0;
        srand(seed++);
        
        //初始化
        int x1_shape[4] = {8, 1, 8, 8};
        int x2_shape[4] = {8, 8, 8, 8};

        int* large_shape = (int*) x2_shape;
        int* small_shape = (int*) x1_shape;
        int *out_shape = (int*) x2_shape;

        dy_size = out_shape[0] * out_shape[1] * out_shape[2] * out_shape[3];
        x1_size = x1_shape[0] * x1_shape[1] * x1_shape[2] * x1_shape[3];
        x2_size = x2_shape[0] * x2_shape[1] * x2_shape[2] * x2_shape[3];
        
        for(i = 0; i < dy_size; ++i) {
            dy[i] = (float)(rand()%20)/2;
        }
        
        for(i = 0; i < x1_size; ++i) {
            x1_data[i] = (float)(rand()%20)/2;
        }
        
        for(i = 0; i < x2_size; ++i) {
            x2_data[i] = (float)(rand()%20)/2;
        }

        memset(indices, 0, ndims*sizeof(int));

        Parameter params;
        params.tile_data0 = tile_data0;
        params.tile_data1 = tile_data1;
        params.large_shape = large_shape;
        params.small_shape = small_shape;
        params.out_shape = out_shape;
        params.ndims = ndims;
        params.dy_size = dy_size;
        params.x1_size = x1_size;
        params.x2_size = x2_size;
        params.large_strides = large_strides;
        params.small_strides = small_strides;
        params.out_strides = out_strides;
        params.large_multiples = large_multiples;
        params.small_multiples = small_multiples;
        params.indices = indices;
        params.x1_shape = x1_shape;
        params.x2_shape = x2_shape;
        
        int core_mask = 0b1111;
        /*性能统计*/    
        fp_mul_grad_s(dy, dx1, dx2, x1_data, x2_data, &params, core_mask);
        return 0;
    }


**私有存储版本:**

.. c:function:: void fp_mul_grad_p(float* dy, float *dx1, float *dx2, float* x1, float* x2, Parameter *params)
.. c:function:: void hp_mul_grad_p(half* dy, half *dx1, half *dx2, half* x1, half* x2, Parameter *params)


**C调用示例：**

.. code-block:: c
    :linenos:
    :emphasize-lines: 36

    //FT78NE示例
    #include <stdio.h>
    #include <mulgrad.h>
    int main(int argc, char* argv[]) {
        float *dy = (float *)0x10010000;//输入，初始化
        float *dx1 = (float *)0x10016000;//输出，需要初始化
        float *dx2 = (float *)0x10020000;//输出，需要初始化
        float *x1_data = (float *)0x10026000;//输入，需要初始化
        float *x2_data = (float *)0x10030000;//输入，需要初始化
        float *tile_data0 = (float *)0x10036000;//中间结果，不需要初始化
        float *tile_data1 = (float *)0x10040000;//中间结果，不需要初始化

        long long ndims = 4;
        long long dy_size;
        long long x1_size;
        long long x2_size;

        int *large_strides = (int *)0x10050000;//不需要初始化
        int *small_strides = (int *)0x10051000; //不需要初始化
        int *out_strides = (int *)0x10052000; //不需要初始化
        int *large_multiples = (int *)0x10053000; //不需要初始化
        int *small_multiples = (int *)0x10054000; //不需要初始化
        int *indices = (int *)0x10055000;
        float *check_dx1 = (float *)0x10060000;
        float *check_dx2 = (float *)0x10070000;

        int i = 0;
        srand(seed++);
        
        /*
        1024时 large_shape = {4, 8, 4, 8}, small_shape = {4, 8, 4, 8}
        4096时 large_shape = {8, 8, 8, 8}, small_shape = {8, 8, 8, 8}
        */
        //初始化
        int x1_shape[4] = {8, 1, 8, 8};
        int x2_shape[4] = {8, 8, 8, 8};

        int* large_shape = (int*) x2_shape;
        int* small_shape = (int*) x1_shape;
        int *out_shape = (int*) x2_shape;

        dy_size = out_shape[0] * out_shape[1] * out_shape[2] * out_shape[3];
        x1_size = x1_shape[0] * x1_shape[1] * x1_shape[2] * x1_shape[3];
        x2_size = x2_shape[0] * x2_shape[1] * x2_shape[2] * x2_shape[3];
        
        for(i = 0; i < dy_size; ++i) {
            dy[i] = (float)(rand()%20)/2;
        }
        
        for(i = 0; i < x1_size; ++i) {
            x1_data[i] = (float)(rand()%20)/2;
        }
        
        for(i = 0; i < x2_size; ++i) {
            x2_data[i] = (float)(rand()%20)/2;
        }

        memset(indices, 0, ndims*sizeof(int));

        Parameter params;
       
        params.tile_data0 = tile_data0;
        params.tile_data1 = tile_data1;
        params.large_shape = large_shape;
        params.small_shape = small_shape;
        params.out_shape = out_shape;
        params.ndims = ndims;
        params.dy_size = dy_size;
        params.x1_size = x1_size;
        params.x2_size = x2_size;
        params.large_strides = large_strides;
        params.small_strides = small_strides;
        params.out_strides = out_strides;
        params.large_multiples = large_multiples;
        params.small_multiples = small_multiples;
        params.indices = indices;
        params.x1_shape = x1_shape;
        params.x2_shape = x2_shape;
    
        fp_mul_grad_p(dy, dx1, dx2, x1_data, x2_data, &params);
        return 0;
    }

   