# -*- coding: utf-8 -*-
# @Time    : 2025/2/28 23:05
# @Author  : ljc
# @FileName: model_err.py
# @Software: PyCharm


# 1.简介
"""
目的：
    使用中心差分法与误差传递公式, 计算每组待测光谱所推断的 5 维参数 (变量) 误差.
函数:
    1) Jacobian_matrix
    2) Jacobian_parameter_err
    3) Hessian_matrix
    4) Hessian_parameter_err
    5) parameter_err
解释:
    1) Jacobian_matrix 函数: 计算每组待测光谱流量残差关于 5 维参数 (变量) 的雅克比矩阵.
    2) Jacobian_parameter_err 函数: 使用雅克比矩阵, 计算待测参数误差.
    3) Hessian_matrix 函数: 计算每组待测光谱流量残差平方和关于 5 维参数 (变量) 的黑塞矩阵.
    4) Hessian_parameter_err 函数: 使用黑塞矩阵, 计算待测参数误差.
    5) parameter_err 函数: 根据用户设置, 选择使用雅克比矩阵或黑塞矩阵计算待测参数误差 (默认使用雅克比矩阵计算待测参数误差).
注意:
    1) 默认使用雅克比矩阵计算待测参数误差, 也可设置 Hessian=True, 使用黑塞矩阵计算待测参数误差.
    2) 使用中心差分法计算雅克比矩阵和黑塞矩阵时, 步长 h 的默认值为 0.05.
    3) 无论使用雅克比矩阵还是黑塞矩阵计算待测参数误差, 均需使用误差传递公式 (Manfred Drosg et al. 2009) 将不同量纲误差转为恒星参数误差.
"""


# 2. 调包
from config.config import default_set, set_all_seeds
import torch
import numpy as np
# 2.1 设置随机种子
set_all_seeds()
# 2.2 调用 GPU、指定数据类型
dtype, device = default_set()
# 2.3 默认数据类型
torch.set_default_dtype(dtype)


# 3. 定义每组待测光谱流量残差关于 5 个参数的雅克比矩阵
def Jacobian_matrix(loss_reduced, best_params, specs, spec_coef, borders_, NewBorders_, flat, leg_array_, group_size, n_params, h, goodPixels_final, Jacobian, Hessian) -> torch.Tensor:

    """
       计算每组待测光谱流量残差关于 5 维参数 (变量) 的雅克比矩阵.

       输入参数：
              -----------
              loss_reduced:
                          每组待测光谱流量残差函数, 输出形状: (specs.shape[0], specs.shape[1]).
              best_params:
                         使用 Adam 获取的最佳参数 (注意: 还没有转化为恒星大气物理参数, 需使用误差传递公式转为恒星参数误差).
              specs:
                   待测光谱数据.
              spec_coef:
                    TGM 系数.
              borders_、NewBorders_、flat:
                                         待测光谱插值到 TGM 光谱的波长设置.
              leg_array_:
                        勒让德多项式值.
              group_size:
                        每组待测光谱有多少样本.
              n_params:
                       n_params=5, 即每条待测光谱的 5 个待测参数.
              h:
                中心差分法计算导数时的默认步长.
              goodPixels_final=False
                    默认迭代优化时, 不剔除异常流量点.
              Jacobian:
                       默认为 Jacobian=True, Hessian=False, 即使用雅克比矩阵计算参数误差.
                       原理: cov(恒星参数)=(J^{T}J)^{-1}*(sigma**2)
                       由于 fits 文件提供的流量不确定性与重采样光谱流量无法一一对应, 因此不确定
                       性使用无偏估计计算, 即 sigma**2=chi^{2}/(N-P), 其中 chi^{2} 为实测光
                       谱与光谱模拟器的差异, N-P 为自由度 (N 为光谱维度、P 是待测参数数量).
              Hessian:
                      使用黑塞矩阵计算参数误差 (若使用黑塞矩阵计算参数误差, 需设置 Jacobian=False, Hessian=True).

        输出参数:
               -----------
               J:
                 每组待测光谱流量残差关于 5 个变量组成的雅克比矩阵, 形状: (specs.shape[0], specs.shape[1], n_params)
    """

    # 3.1 使用 torch.no_grad(), 避免计算梯度、减少内存占用并加速计算
    with torch.no_grad():
        # 3.2 初始化雅克比矩阵, 形状为 [specs.shape[0], specs.shape[1]-2, n_params], 数据类型和设备与 best_params 相同
        J = torch.zeros((group_size, specs.shape[1]-2, n_params), dtype=best_params.dtype, device=best_params.device)

        # 3.3 中心差分法计算待测参数的一阶导数
        for j in range(n_params):
            # 3.3.1 复制参数用于正向扰动
            params_plus = best_params.clone()
            # 3.3.2 复制参数用于负向扰动
            params_minus = best_params.clone()
            # 3.3.3 在第 j 个参数上增加步长 h
            params_plus[:, j] += h
            # 3.3.4 在第 j 个参数上减少步长 h
            params_minus[:, j] -= h
            # 3.3.5 计算 f(xj+h)-f(xj-h)/(2*h), 即中心差分法计算导数
            derivatives = (loss_reduced(params_plus,      # 正向扰动后的参数
                                        specs,            # LAMOST 光谱的流量
                                        spec_coef,        # TGM 系数
                                        borders_,         # LAMOST 光谱的波长区间范围
                                        NewBorders_,      # TGM 模型光谱的波长区间范围
                                        flat,             # 重采样流量校正因子
                                        leg_array_,       # 勒让德多项式值
                                        goodPixels_final, # 是否剔除异常流量点
                                        Jacobian,         # 是否使用雅克比矩阵计算参数误差
                                        Hessian           # 是否使用黑塞矩阵计算参数误差
                                        )
                           -
                           loss_reduced(params_minus,     # 负向扰动后的参数
                                        specs,            # LAMOST 光谱的流量
                                        spec_coef,        # TGM 系数
                                        borders_,         # LAMOST 光谱的波长区间范围
                                        NewBorders_,      # TGM 模型光谱的波长区间范围
                                        flat,             # 重采样流量校正因子
                                        leg_array_,       # 勒让德多项式值
                                        goodPixels_final, # 是否剔除异常流量点
                                        Jacobian,         # 是否使用雅克比矩阵计算参数误差
                                        Hessian           # 是否使用黑塞矩阵计算参数误差
                                        )
                           ) / (2 * h)
            # 3.3.6 确保维度匹配, 将计算的偏导数添加到雅克比矩阵中
            J[:, :, j:j+1] = derivatives.unsqueeze(-1)

    # 3.4 返回计算完成的雅克比矩阵
    return J


# 4. 使用雅克比矩阵, 计算待测参数误差
def Jacobian_parameter_err(loss_reduced, best_params, specs, spec_coef, borders_, NewBorders_, flat, leg_array_, group_size, n_params, dof, h, goodPixels_final, Jacobian, Hessian) -> np.ndarray:

    """
       使用雅克比矩阵与误差传递公式, 批量计算每组待测参数的误差.

       输入参数：
              -----------
              loss_reduced:
                          每组待测光谱流量残差函数, 输出形状: (specs.shape[0], specs.shape[1]).
              best_params:
                         使用 Adam 获取的最佳参数(注意: 还没有转化为恒星大气物理参数).
              specs:
                   待测光谱数据.
              spec_coef:
                    TGM 系数.
              borders_、NewBorders_、flat:
                                        待测光谱插值到 TGM 光谱的波长设置.
              leg_array_:
                        勒让德多项式值.
              group_size:
                        每组待测光谱有多少样本.
              n_params:
                       n_params=5, 即每条待测光谱的 5 个待测参数.
              dof:
                计算每条光谱的待测参数时的自由度, dof=光谱特征维度-待测参数数量.
              h:
                中心差分法计算导数时的默认步长.
              goodPixels_final=False
                                    默认迭代优化时, 不剔除异常流量点.
              Jacobian:
                       默认为 Jacobian=True, Hessian=False, 即使用雅克比矩阵计算参数误差.
              Hessian:  
                      使用黑塞矩阵计算参数误差 (若使用黑塞矩阵计算参数误差, 需设置 Jacobian=False, Hessian=True).

        输出参数:
               -----------
               parameter_std:
                             每组待测变量误差, 形状: (specs.shape[0], n_params).
    """

    # 4.1 计算每组待测光谱流量残差关于 5 个参数的雅克比矩阵
    J = Jacobian_matrix(loss_reduced,       # 每组待测光谱流量残差函数   
                        best_params,        # 最佳参数
                        specs,              # 待测光谱数据
                        spec_coef,          # TGM 系数
                        borders_,           # LAMOST 光谱的波长区间范围
                        NewBorders_,        # TGM 模型光谱的波长区间范围
                        flat,               # 重采样流量校正因子
                        leg_array_,         # 勒让德多项式值
                        group_size,         # 每组待测光谱有多少样本
                        n_params,           # 每组待测光谱的 5 个待测参数
                        h,                  # 中心差分法计算导数时的默认步长
                        goodPixels_final,   # 是否剔除异常流量点
                        Jacobian,           # 是否使用雅克比矩阵计算参数误差
                        Hessian             # 是否使用黑塞矩阵计算参数误差
                        )
    
    # 4.2 估计流量的误差方差 sigma^{2}: 计算流量残差平方和(即损失函数在最佳参数点的值的平方和), 除以自由度估计误差方差, 作为流量误差方差的无偏估计
    ssigma_sq = torch.sum(loss_reduced(best_params,        # 最佳参数
                                       specs,              # 待测光谱数据
                                       spec_coef,          # TGM 系数
                                       borders_,           # LAMOST 光谱的波长区间范围
                                       NewBorders_,        # TGM 模型光谱的波长区间范围
                                       flat,               # 重采样流量校正因子
                                       leg_array_,         # 勒让德多项式值
                                       goodPixels_final,   # 是否剔除异常流量点
                                       Jacobian,           # 是否使用雅克比矩阵计算参数误差
                                       Hessian             # 是否使用黑塞矩阵计算参数误差
                                       ) ** 2,
                                       dim=1).unsqueeze(-1).unsqueeze(-1) / dof
    
    # 4.3 计算参数协方差矩阵
    covariance = torch.linalg.pinv(torch.bmm(J.transpose(1, 2), J) / ssigma_sq)
    
    # 4.4 提取对角线元素、并取平方根得到参数误差
    parameter_std = torch.sqrt(torch.diagonal(covariance, dim1=1, dim2=2)).reshape(group_size, 5)
    
    # 4.5 使用误差传递公式(Manfred Drosg et al. 2009), 将不同量纲误差转为恒星参数误差
    # 4.5.1 将最佳参数转为 numpy 数组处理
    best_params = best_params.detach().cpu().numpy()
    # 4.5.2 将标准差转为 numpy 数组处理
    parameter_std = parameter_std.detach().cpu().numpy()
    # 4.5.3 对第 1 个参数应用误差传播公式转为 Teff 误差: σ_Teff = parameter_std[:, 0] * 10^(parameter_std[:, 0]+3.7617) * ln(10)
    parameter_std[:, 0] = parameter_std[:, 0] * np.power(10, best_params[:, 0] + 3.7617) * np.log(10)
    # 4.5.4 对第 4 个参数转为 Rv 误差: 光速约为 299792.458 km/s, 对数波长空间中每个像素的大小(velScale)为 0.0002302585092994046
    parameter_std[:, 3] = parameter_std[:, 3] * (299792.458 * 0.0002302585092994046)

    # 4.6 返回转换后的参数误差
    return parameter_std


# 5. 计算每组待测光谱流量残差平方和关于 5 个参数的黑塞矩阵
def Hessian_matrix(loss_reduced, best_params, specs, spec_coef, borders_, NewBorders_, flat, leg_array_, group_size, n_params, h, goodPixels_final, Jacobian, Hessian) -> torch.Tensor:

    """
        批量计算每组待测光谱流量残差平方和关于 5 个参数的黑塞矩阵.

        输入参数:
              -----------
              loss_reduced:
                          每组待测光谱流量残差函数, 输出形状: (specs.shape[0], specs.shape[1]).
              best_params:
                         使用 Adam 获取的最佳参数(注意: 还没有转化为恒星大气物理参数).
              specs:
                   待测光谱数据.
              spec_coef:
                    TGM 系数.
              borders_、NewBorders_、flat:
                                        待测光谱插值到 TGM 光谱的波长设置.
              leg_array_:
                        勒让德多项式值.
              group_size:
                        每组待测光谱有多少样本.
              n_params:
                       n_params=5, 即每条待测光谱的 5 个待测参数.
              h:
                中心差分法计算导数时的默认步长.
              goodPixels_final=False
                                    默认迭代优化时, 不剔除异常流量点.
              Jacobian:
                       默认为 Jacobian=True, Hessian=False, 即使用雅克比矩阵计算参数误差.
              Hessian:
                      使用黑塞矩阵计算参数误差(若使用黑塞矩阵计算参数误差, 需设置 Jacobian=False, Hessian=True).

        输出参数:
               -----------
               H:
                 每组待测光谱流量残差平方和关于 5 个参数组成的黑塞矩阵, 形状: (specs.shape[0], n_params, n_params).
    """

    # 5.1 使用 torch.no_grad(), 避免计算梯度、减少内存占用并加速计算
    with torch.no_grad():
        # 5.2 初始化黑塞矩阵, 形状为 [specs.shape[0], n_params, n_params], 数据类型和设备与 best_params 相同
        hessians = torch.zeros((group_size, n_params, n_params), dtype=best_params.dtype, device=best_params.device)

        # 5.3 计算对角线元素(二阶偏导数)
        for j in range(n_params):
            # 5.3.1 创建需要的参数点
            params_plus = best_params.clone()
            params_minus = best_params.clone()
            params_original = best_params.clone()

            # 5.3.2 对第 j 个参数加减 h
            params_plus[:, j] += h
            params_minus[:, j] -= h

            # 5.3.3 计算 f(xj+h) - 2f(xj) + f(xj-h), 即中心差分法计算二阶偏导数
            hessians[:, j, j] = (loss_reduced(params_plus,        # 正向扰动后的参数
                                              specs,              # 待测光谱数据
                                              spec_coef,          # TGM 系数
                                              borders_,           # LAMOST 光谱的波长区间范围
                                              NewBorders_,        # TGM 模型光谱的波长区间范围
                                              flat,               # 重采样流量校正因子
                                              leg_array_,         # 勒让德多项式值
                                              goodPixels_final,   # 是否剔除异常流量点
                                              Jacobian,           # 是否使用雅克比矩阵计算参数误差
                                              Hessian             # 是否使用黑塞矩阵计算参数误差
                                              )
                                 - 2 *
                                 loss_reduced(params_original,    # 原始参数
                                              specs,              # 待测光谱数据
                                              spec_coef,          # TGM 系数
                                              borders_,           # LAMOST 光谱的波长区间范围
                                              NewBorders_,        # TGM 模型光谱的波长区间范围
                                              flat,               # 重采样流量校正因子
                                              leg_array_,         # 勒让德多项式值
                                              goodPixels_final,   # 是否剔除异常流量点
                                              Jacobian,           # 是否使用雅克比矩阵计算参数误差
                                              Hessian             # 是否使用黑塞矩阵计算参数误差
                                              )
                                 +
                                 loss_reduced(params_minus,       # 负向扰动后的参数
                                              specs,              # 待测光谱数据  
                                              spec_coef,          # TGM 系数
                                              borders_,           # LAMOST 光谱的波长区间范围
                                              NewBorders_,        # TGM 模型光谱的波长区间范围
                                              flat,               # 重采样流量校正因子
                                              leg_array_,         # 勒让德多项式值
                                              goodPixels_final,   # 是否剔除异常流量点
                                              Jacobian,           # 是否使用雅克比矩阵计算参数误差
                                              Hessian             # 是否使用黑塞矩阵计算参数误差
                                              )
                                 ) / (h * h)

        # 5.4 计算非对角线元素(混合偏导数)
        for j in range(n_params):
            # 5.4.1 只计算上三角部分
            for k in range(j + 1, n_params):
                # 5.4.2 创建四个批量点来计算中心差分
                params_pp = best_params.clone()
                params_pm = best_params.clone()
                params_mp = best_params.clone()
                params_mm = best_params.clone()
                # 5.4.3 对参数 j 和 k 分别加减 h
                params_pp[:, j] += h
                params_pp[:, k] += h
                params_pm[:, j] += h
                params_pm[:, k] -= h
                params_mp[:, j] -= h
                params_mp[:, k] += h
                params_mm[:, j] -= h
                params_mm[:, k] -= h

                # 5.4.4 计算混合偏导数, 即中心差分法计算二阶偏导数
                hessians[:, j, k] = (loss_reduced(params_pp,          # 第 j 个参数正向扰动 h, 第 k 个参数正向扰动 h 后的参数
                                                  specs,              # 待测光谱数据
                                                  spec_coef,          # TGM 系数
                                                  borders_,           # LAMOST 光谱的波长区间范围
                                                  NewBorders_,        # TGM 模型光谱的波长区间范围
                                                  flat,               # 重采样流量校正因子
                                                  leg_array_,         # 勒让德多项式值
                                                  goodPixels_final,   # 是否剔除异常流量点
                                                  Jacobian,           # 是否使用雅克比矩阵计算参数误差
                                                  Hessian             # 是否使用黑塞矩阵计算参数误差
                                                  )
                                     -
                                     loss_reduced(params_pm,          # 第 j 个参数正向扰动 h, 第 k 个参数负向扰动 h 后的参数
                                                  specs,              # 待测光谱数据
                                                  spec_coef,          # TGM 系数
                                                  borders_,           # LAMOST 光谱的波长区间范围
                                                  NewBorders_,        # TGM 模型光谱的波长区间范围
                                                  flat,               # 重采样流量校正因子
                                                  leg_array_,         # 勒让德多项式值
                                                  goodPixels_final,   # 是否剔除异常流量点
                                                  Jacobian,           # 是否使用雅克比矩阵计算参数误差
                                                  Hessian             # 是否使用黑塞矩阵计算参数误差
                                                  )
                                     -
                                     loss_reduced(params_mp,          # 第 j 个参数负向扰动 h, 第 k 个参数正向扰动 h 后的参数
                                                  specs,              # 待测光谱数据
                                                  spec_coef,          # TGM 系数
                                                  borders_,           # LAMOST 光谱的波长区间范围
                                                  NewBorders_,        # TGM 模型光谱的波长区间范围
                                                  flat,               # 重采样流量校正因子
                                                  leg_array_,         # 勒让德多项式值
                                                  goodPixels_final,   # 是否剔除异常流量点
                                                  Jacobian,           # 是否使用雅克比矩阵计算参数误差
                                                  Hessian             # 是否使用黑塞矩阵计算参数误差
                                                  )
                                     +
                                     loss_reduced(params_mm,          # 第 j 个参数负向扰动 h, 第 k 个参数负向扰动 h 后的参数
                                                  specs,              # 待测光谱数据
                                                  spec_coef,          # TGM 系数
                                                  borders_,           # LAMOST 光谱的波长区间范围
                                                  NewBorders_,        # TGM 模型光谱的波长区间范围
                                                  flat,               # 重采样流量校正因子
                                                  leg_array_,         # 勒让德多项式值
                                                  goodPixels_final,   # 是否剔除异常流量点
                                                  Jacobian,           # 是否使用雅克比矩阵计算参数误差
                                                  Hessian             # 是否使用黑塞矩阵计算参数误差
                                                  )
                                     ) / (4 * h * h)

                # 5.4.5 利用对称性填充下三角部分
                hessians[:, k, j] = hessians[:, j, k]

    # 5.5 返回计算完成的黑塞矩阵
    return hessians


# 6. 使用黑塞矩阵, 计算待测参数误差
def Hessian_parameter_err(loss_reduced, best_params, specs, spec_coef, borders_, NewBorders_, flat, leg_array_, group_size, n_params, dof, h, goodPixels_final, Jacobian, Hessian) -> np.ndarray:
   
    """
       使用黑塞矩阵与误差传递公式, 批量计算每组待测参数的误差.

       输入参数：
              -----------
              loss_reduced:
                          每组待测光谱流量残差函数, 输出形状: (specs.shape[0], specs.shape[1]).
              best_params:
                         使用 Adam 获取的最佳参数(注意: 还没有转化为恒星大气物理参数).
              specs:
                   待测光谱数据.
              spec_coef:
                    TGM 系数.
              borders_、NewBorders_、flat:
                                        待测光谱插值到 TGM 光谱的波长设置.
              leg_array_:
                        勒让德多项式值.
              group_size:
                        每组待测光谱有多少样本.
              n_params:
                       n_params=5, 即每条待测光谱的 5 个待测参数.
              dof:
                计算每条光谱的待测参数时的自由度, dof=光谱特征维度-待测参数数量.
              h:
                中心差分法计算导数时的默认步长.
              goodPixels_final=False
                                    默认迭代优化时, 不剔除异常流量点.
              Jacobian:
                       默认为 Jacobian=True, Hessian=False, 即使用雅克比矩阵计算参数误差.
              Hessian:
                      使用黑塞矩阵计算参数误差(若使用黑塞矩阵计算参数误差, 需设置 Jacobian=False, Hessian=True).

        输出参数:
               -----------
               parameter_std:
                            每组待测变量误差, 形状: (specs.shape[0], n_params).
    """
   
    # 6.1 计算黑塞矩阵
    H = Hessian_matrix(loss_reduced,       # 每组待测光谱流量残差函数
                       best_params,        # 最佳参数
                       specs,              # 待测光谱数据
                       spec_coef,          # TGM 系数
                       borders_,           # LAMOST 光谱的波长区间范围
                       NewBorders_,        # TGM 模型光谱的波长区间范围
                       flat,               # 重采样流量校正因子
                       leg_array_,         # 勒让德多项式值
                       group_size,         # 每组待测光谱有多少样本
                       n_params,           # 每组待测光谱的 5 个待测参数
                       h,                  # 中心差分法计算导数时的默认步长
                       goodPixels_final,   # 是否剔除异常流量点
                       Jacobian,           # 是否使用雅克比矩阵计算参数误差
                       Hessian             # 是否使用黑塞矩阵计算参数误差
                       )
    ssigma_sq = loss_reduced(best_params,        # 最佳参数
                             specs,              # 待测光谱数据
                             spec_coef,          # TGM 系数
                             borders_,           # LAMOST 光谱的波长区间范围
                             NewBorders_,        # TGM 模型光谱的波长区间范围
                             flat,               # 重采样流量校正因子
                             leg_array_,         # 勒让德多项式值
                             goodPixels_final,   # 是否剔除异常流量点
                             Jacobian,           # 是否使用雅克比矩阵计算参数误差
                             Hessian             # 是否使用黑塞矩阵计算参数误差
                             ).unsqueeze(-1).unsqueeze(-1) / dof
    covariance = 2.0 * torch.linalg.pinv(H) * ssigma_sq
    
    # 6.2 提取对角线元素、并取平方根得到参数误差
    parameter_std = torch.sqrt(torch.diagonal(covariance, dim1=1, dim2=2)).reshape(group_size, 5)
    
    # 6.3 使用误差传递公式(Manfred Drosg et al. 2009), 将不同量纲误差转为恒星参数误差
    # 6.3.1 将最佳参数转为 numpy 数组处理
    best_params = best_params.detach().cpu().numpy()
    # 6.3.2 将标准差转为 numpy 数组处理
    parameter_std = parameter_std.detach().cpu().numpy()
    # 6.3.3 对第 1 个参数应用误差传播公式转为 Teff 误差: σ_Teff = parameter_std[:, 0] * 10^(parameter_std[:, 0]+3.7617) * ln(10)
    parameter_std[:, 0] = parameter_std[:, 0] * np.power(10, best_params[:, 0] + 3.7617) * np.log(10)
    # 6.3.4 对第 4 个参数转为 Rv 误差: 光速约为 299792.458 km/s, 对数波长空间中每个像素的大小(velScale)为 0.0002302585092994046
    parameter_std[:, 3] = parameter_std[:, 3] * (299792.458 * 0.0002302585092994046)

    # 6.4 返回转换后的参数误差
    return parameter_std


# 7. 根据用户设置, 选择使用雅克比矩阵或黑塞矩阵计算待测参数误差
def parameter_err(loss_reduced, best_params, specs, spec_coef, borders_, NewBorders_, flat, leg_array_, group_size, n_params, dof, h, goodPixels_final, Jacobian=True, Hessian=False) -> np.ndarray:

    """
        根据用户设置, 选择使用雅克比矩阵或黑塞矩阵计算待测参数误差.

        输入参数:
              -----------
              loss_reduced:
                          每组待测光谱流量残差函数, 输出形状: (specs.shape[0], specs.shape[1]).
              best_params:
                         使用 Adam 获取的最佳参数(注意: 还没有转化为恒星大气物理参数).
              specs:
                   待测光谱数据.
              spec_coef:
                    TGM 系数.
              borders_、NewBorders_、flat:
                                        待测光谱插值到 TGM 光谱的波长设置.
              leg_array_:
                        勒让德多项式值.
              group_size:
                        每组待测光谱有多少样本.
              n_params:
                       n_params=5, 即每条待测光谱的 5 个待测参数.
              dof:
                计算每条光谱的待测参数时的自由度, dof=光谱特征维度-待测参数数量.
              h:
                中心差分法计算导数时的默认步长.
              goodPixels_final=False
                                    默认迭代优化时, 不剔除异常流量点.
              Jacobian:
                       默认为 Jacobian=True, Hessian=False, 即使用雅克比矩阵计算参数误差.
              Hessian:
                      使用黑塞矩阵计算参数误差(若使用黑塞矩阵计算参数误差, 需设置 Jacobian=False, Hessian=True).

        输出参数:
              -----------
              parameter_std:
                            每组待测参数误差, 形状: (specs.shape[0], n_params).
    """

    # 7.1 根据用户设置, 选择使用雅克比矩阵或黑塞矩阵计算待测参数误差
    if Jacobian is True:
        return Jacobian_parameter_err(loss_reduced,       # 每组待测光谱流量残差函数
                                      best_params,        # 最佳参数
                                      specs,              # 待测光谱数据
                                      spec_coef,          # TGM 系数
                                      borders_,           # LAMOST 光谱的波长区间范围
                                      NewBorders_,        # TGM 模型光谱的波长区间范围
                                      flat,               # 重采样流量校正因子
                                      leg_array_,         # 勒让德多项式值
                                      group_size,         # 每组待测光谱有多少样本
                                      n_params,           # 每组待测光谱的 5 个待测参数
                                      dof,                # 计算每条光谱的待测参数时的自由度, dof=光谱特征维度-待测参数数量
                                      h,                  # 中心差分法计算导数时的默认步长
                                      goodPixels_final,   # 是否剔除异常流量点
                                      Jacobian,           # 是否使用雅克比矩阵计算参数误差
                                      Hessian             # 是否使用黑塞矩阵计算参数误差
                                      )
    if Hessian is True:
        return Hessian_parameter_err(loss_reduced,       # 每组待测光谱流量残差函数
                                     best_params,        # 最佳参数
                                     specs,              # 待测光谱数据
                                     spec_coef,          # TGM 系数
                                     borders_,           # LAMOST 光谱的波长区间范围
                                     NewBorders_,        # TGM 模型光谱的波长区间范围
                                     flat,               # 重采样流量校正因子
                                     leg_array_,         # 勒让德多项式值
                                     group_size,         # 每组待测光谱有多少样本
                                     n_params,           # 每组待测光谱的 5 个待测参数
                                     dof,                # 计算每条光谱的待测参数时的自由度, dof=光谱特征维度-待测参数数量
                                     h,                  # 中心差分法计算导数时的默认步长
                                     goodPixels_final,   # 是否剔除异常流量点
                                     Jacobian,           # 是否使用雅克比矩阵计算参数误差
                                     Hessian             # 是否使用黑塞矩阵计算参数误差
                                     )