DynamicQuant
=================

将 FP32 输入张量按给定的 scale 和 zero point（zp）动态量化为 INT8 输出张量。
支持按整张量量化或按指定轴分段量化（per-axis dynamic quantization）。

.. math::

    q_i = \mathrm{clip}\left( \mathrm{round}\left( \frac{x_i}{scale} + zp \right), q_{min}, q_{max} \right)

其中：

- :math:`x_i` 为输入浮点值
- :math:`scale` 为量化比例因子
- :math:`zp` 为零点（zero point）
- :math:`q_{min} = -128`
- :math:`q_{max} = 127`

当输入为 :math:`+\infty` 或 :math:`-\infty` 时，输出分别饱和到最大或最小量化值。

---

输入：
    - **real_values** - 输入 FP32 数据地址
    - **element_num** - 输入元素总数
    - **scale** - 量化 scale 数组地址
    - **zp** - 量化 zero point 数组地址
    - **segment_num** - 分段数量（用于按轴量化）
    - **axis_num** - 量化轴标识  
        - ``0``：整张量量化（per-tensor）
        - ``!=0``：按轴分段量化（per-axis）

输出：
    - **quant_values** - 输出 INT8 量化结果地址

---

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

---

.. note::
    - 当前算子仅支持 **FP32\Fp16 → INT8** 的动态量化
    - 当 ``axis_num = 0`` 时，仅使用 ``scale[0]`` 与 ``zp[0]`` 进行整张量量化
    - 当 ``axis_num != 0`` 时，输入按 ``segment_num`` 进行分段，每段使用对应的 ``scale[i]`` 与 ``zp[i]``
    - 分段大小通过 ``UP_DIV(element_num, segment_num)`` 计算，最后一段自动处理剩余元素
    - 量化结果会被限制在 ``[-128, 127]`` 范围内

---

**私有存储版本：**

.. c:function:: void fp_QuantData_p(float* real_values, int8_t* quant_values, int element_num, float* scale, int* zp, int segment_num, int axis_num)
.. c:function:: void hp_QuantData_p(half* real_values, half* quant_values, int element_num, half* scale, int* zp, int segment_num, int axis_num)

---

**C调用示例：**

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

        #include <stdio.h>
        #include <quant.h>

        int main(int argc, char* argv[]) {
            float *input = (float *)0x10000000;      // FP32 输入
            int8_t *output = (int8_t *)0x10004000;   // INT8 输出
            float scale[4] = {0.1f, 0.12f, 0.11f, 0.09f};
            int zp[4] = {0, 0, 0, 0};
            int element_num = 1024;
            int segment_num = 4;
            int axis_num = 1;

            fp_QuantData_p(input, output, element_num,
                      scale, zp, segment_num, axis_num);

            return 0;
        }

---

**共享存储版本：**

.. c:function:: void fp_QuantData_s(float* real_values, int8_t* quant_values, int element_num, float* scale, int* zp, int segment_num, int axis_num, int core_mask)
.. c:function:: void hp_QuantData_s(half* real_values, half* quant_values, int element_num, half* scale, int* zp, int segment_num, int axis_num, int core_mask)

---

**C调用示例：**

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

        #include <stdio.h>
        #include <quant.h>

        int main(int argc, char* argv[]) {
            float *input = (float *)0x10000000;      // FP32 输入
            int8_t *output = (int8_t *)0x10004000;   // INT8 输出
            float scale[4] = {0.1f, 0.12f, 0.11f, 0.09f};
            int zp[4] = {0, 0, 0, 0};
            int element_num = 1024;
            int segment_num = 4;
            int axis_num = 1, core_mask = 0xff;

            fp_QuantData_s(input, output, element_num,
                      scale, zp, segment_num, axis_num, core_mask);

            return 0;
        }

---

**实现说明：**

- 当 ``axis_num == 0`` 时：
  
  - 对整个输入数组执行一次统一量化
  - 等价于 per-tensor quantization

- 当 ``axis_num != 0`` 时：

  - 输入数据按 ``segment_num`` 均分为多个分段
  - 每个分段使用独立的 ``scale[i]`` 与 ``zp[i]``
  - 等价于 per-axis dynamic quantization

- 核心量化过程由 ``DoQuantizeFp32ToInt8`` 完成，包括：
  
  - 反 scale 计算
  - 四舍五入
  - 饱和裁剪
  - INF 特殊值处理

