# 快速使用自定义的线性求解算法
线性代数系统的通用形式为:
其中
接下来,我们将通过一个快速示例来演示如何运行您的第一个线性求解算法。
# 拷贝文件
若您已经在快速使用自定义的积分算法中运行了自定义积分算法MyEuler,那么mws_user_alg.c文件中实现的线性求解算法userLinearAlg也已经生效,无需再次拷贝该文件。否则,您需要按照快速使用自定义的积分算法中的相关步骤拷贝mws_user_alg.c文件。
# 创建模型
打开 Sysplorer 界面,在建模标签页中选择快速新建模型,如图所示:
在弹出的新建模型窗口中单击确定,然后切换到文本视图,复制以下模型代码:
model Model4
Real x;
Real y;
Real z;
parameter Real p1 = 2;
parameter Real p2 = 3;
parameter Real p3 = 1;
equation
p1 * x + p2 * y + p1 * z = 3 * sin(4 * time);
p3 * x - p1 * y = cos(5 * time);
-p1 * x + 8 * y + p2*z = time;
end Model4;
上述示例中的模型名为Model4,您可以根据需要自定义模型名称。
提示
只有当线性方程系统撕裂后的变量数大于1时,仿真时才会调用线性系统求解算法。如图所示,在上述示例模型翻译后,您可以从输出窗口的建模标签页中看到线性方程系统撕裂后的变量数为2。如果您的翻译结果显示线性方程系统撕裂后的变量数为0,请检查仿真设置中模型翻译标签页的参数估值功能是否已开启,如果开启,请将其关闭。

# 开始仿真
单击仿真标签页上方的仿真按钮,Sysplorer 将调用自定义的线性求解算法userLinearAlg来求解模型中的线性方程系统。至此,您的第一个自定义线性算法已经成功运行。
提示
与积分算法不同,拷贝完mws_user_alg.c文件之后,其中的自定义线性系统求解算法将自动生效,无需在界面手动选择。因此,您可能不会明显察觉到这一过程。
# 自定义线性求解算法解析
mws_user_alg.c文件内实现了一个简单的线性求解算法,名为userLinearAlg,并将其成功设置到了 Sysplorer 的求解器中。自定义线性算法一旦设置成功,则系统会自动调用该算法来求解模型中的线性代数系统。接下来,我们将深入探讨mws_user_alg.c中线性求解算法的实现细节,为您后续扩展自定义线性求解算法打下坚实的基础。
mws_user_alg.c中与自定义线性求解算法相关的内容列表如下:
| 名称 | 类型 | 必须实现 | 含义 |
|---|---|---|---|
| userLPData | 结构体 | 是 | 线性算法对象的定义 |
| UserLPSolverInstantiate | 函数 | 是 | 创建线性算法实例 |
| UserLPSolverGetFeature | 函数 | 是 | 获取线性算法的特性 |
| UserLPSolver | 函数 | 是 | 线性问题求解计算 |
| UserLPSolverSetDebugLogging | 函数 | 否 | 设置调试日志输出选项 |
| UserLPSolverFreeInstance | 函数 | 是 | 释放积分器对象 |
| MwsSetExternalLinearSolver | 函数 | 是 | 设置线性求解算法 |
提示
自定义积分算法是注册到求解器中供用户选择的,而自定义线性求解算法则是直接设置到求解器中,以替换默认的线性算法。
# 常用数据类型
Sysplorer 常用的内部数据类型在各种算法中均一致,Sysplorer 内部数据类型和宏的定义参见:%Sysplorer安装目录%\Simulator\Src\mws_common_decl.h。
# 线性求解算法对象的定义
typedef struct
{
MwsInteger n;
MwsReal* A;
MwsInteger* ipiv;
MwsLPCallbackFns cbfns;
MwsUserContext userContext;
} MwsUserLPData;
MwsUserLPData各个成员的含义如下:
| 名称 | 含义 |
|---|---|
| n | 求解变量的个数 |
| A | 线性代数系统的系数矩阵 |
| ipiv | 记录线性方程求解过程中系数矩阵进行的行交换 |
| cbfns | 线性求解算法可直接使用的回调函数集,由求解器提供 |
| userContext | 用户数据环境,调用回调函数时需要此参数 |
# 创建线性求解算法的实例
static MwsLPSolverType UserLPSolverInstantiate(MwsSize n, MwsMatrixFeature matrixFeature, const MwsLPCallbackFns* cbfns,
MwsBoolean loggingOn, MwsLocale locale, MwsUserContext userContext)
{
MwsUserLPData* userLPData = cbfns->allocateMemory(userContext, 1, sizeof(MwsUserLPData));
if (userLPData == MWSnullptr)
{
return MWSnullptr;
}
userLPData->n = n;
userLPData->cbfns = *cbfns;
userLPData->userContext = userContext;
userLPData->A = cbfns->allocateMemory(userContext, n * n, sizeof(MwsReal));
if (userLPData->A == MWSnullptr)
{
cbfns->freeMemory(userContext, userLPData);
return MWSnullptr;
}
userLPData->ipiv = cbfns->allocateMemory(userContext, n, sizeof(MwsInteger));
if (userLPData->ipiv == MWSnullptr)
{
cbfns->freeMemory(userContext, userLPData);
}
return userLPData;
}
UserLPSolverInstantiate函数用于实例化线性求解算法的“类”,即创建一个MwsUserLPData类型的结构体实例userLPData,并为其成员分配内存空间和赋值。与MyEuler算法的实例类似。
# 获取线性求解算法的特性
static MwsInteger UserLPSolverGetFeature(MwsLPSolverType inst, MwsLPSolverFeature* solverFeature)
{
MwsInteger rst = 0;
solverFeature->columnMajorMatrix = mwsTrue;
solverFeature->iterativeMethod = mwsFalse;
return rst;
}
UserLPSolverGetFeature函数用于获取线性求解算法的特性。与前述的积分算法MyEuler相比,线性求解算法的特性要简单得多,仅包含两个特性。通常情况下,columnMajorMatrix设置为mwsTrue。iterativeMethod根据算法实际情况设置。示例算法userLinearAlg使用的是直接求解,故iterativeMethod设为mwsFalse。
| 名称 | 含义 |
|---|---|
| columnMajorMatrix | 矩阵是否为列主排列 |
| iterativeMethod | 是否为迭代方法 |
# 求解线性代数系统
static MwsInteger UserLPSolve(MwsLPSolverType inst, const MwsReal A[], const MwsReal b[], const MwsChar* const xname[],
MwsReal x[], MwsBoolean newA, MwsReal tolerance, void* reserve)
{
MwsUserLPData* userLPData = (MwsUserLPData*)(inst);
MwsInteger rst = 0;
MwsInteger n = userLPData->n;
MwsInteger nrhs = 1;
MwsInteger info = 0;
memcpy(userLPData->A, A, userLPData->n * userLPData->n * sizeof(MwsReal));
memcpy(x, b, userLPData->n * sizeof(MwsReal));
rst = dgesv_(&n, &nrhs, userLPData->A, &n, userLPData->ipiv, x, &n, &info);
return rst;
}
UserLPSolve函数用于线性方程组的求解计算,是整个线性求解算法的关键。其中各个参数的意义如下:
| 参数名称 | 含义 |
|---|---|
[in] inst | 线性求解算法实例 |
[in] A | 系数矩阵,按算法特性排列(列主或行主)方式提供 |
[in] b | 常数向量 |
[in] xname | 求解变量名字向量(主要用于日志输出) |
|[in | out] x | 求解变量值(输入时为初始值,输出时为结果值) |
[in] newA | 系数矩阵相对于上一次调用是否有更新 |
[in] tolerance | 收敛精度(针对迭代法,直接方法此参数无意义) |
[in] reserve | 保留参数 |
示例算法userLinearAlg在此处直接调用了 LAPACK 中采用直接求解法的dgesv_函数。dgesv_函数的功能十分强大,可以求解如下方程:
其中
dgesv_函数的接口说明如下:
| 参数名称 | 含义 |
|---|---|
[in] n | 线性方程的个数 |
[in] nrhs | 方程组右端项的列数 |
[in | out] A | 输入时为矩阵 A,大小为 n*n;输出时为 LU 分解的结果 |
[in] lda | A 的主维度,一般为 n |
[out] ipiv | 整数数组,大小为 n,存储了 LU 分解时的主元行交换信息 |
[in | out] B | 输入时为矩阵 B,大小为 n*nrhs;输出时为矩阵 X,即解 |
[in] ldb | A 的主维度,一般为 n |
[out] info | 返回状态信息。info=0 表示成功;如果 info<0,表示对应位置的参数有非法值;如果 info>0,表示矩阵 A 为奇异矩阵,无法求解 |
# 释放算法实例
static void UserLPSolverFreeInstance(MwsLPSolverType inst)
{
MwsUserLPData* userLPData = (MwsUserLPData*)(inst);
if (userLPData != MWSnullptr)
{
userLPData->cbfns.freeMemory(userLPData->userContext, userLPData->A);
userLPData->cbfns.freeMemory(userLPData->userContext, userLPData->ipiv);
userLPData->cbfns.freeMemory(userLPData->userContext, userLPData);
userLPData = MWSnullptr;
}
}
UserLPSolverFreeInstance函数用于释放算法实例的内存,与MyEuler进行内存释放的UserIVPSolverFreeInstance函数基本一致,在此不作赘述。
# 其他函数
| 函数名称 | 含义 |
|---|---|
| UserIVPSolverSetDebugLogging | 设置调试日志输出选项 |
上述函数在线性求解算法userLinearAlg的实现中,函数体为空,直接返回0。若有使用上述函数的需求,请参考用户手册中对应章节的详细接口说明。
# 设置线性求解算法
MwsStatus MwsSetExternalLinearSolver(void* inst)
{
MwsStatus rst = mwsOK;
MwsLPSolverFcns lpSolverFcns;
MwsString solverName = "userLinearAlg";
lpSolverFcns.solverInstantiate = UserLPSolverInstantiate;
lpSolverFcns.solverGetFeature = UserLPSolverGetFeature;
lpSolverFcns.solverSolve = UserLPSolve;
lpSolverFcns.solverSetDebugLogging = UserLPSolverSetDebugLogging;
lpSolverFcns.solverFreeInstance = UserLPSolverFreeInstance;
rst = MwsSetUserLinearSolver(inst, solverName, &lpSolverFcns);
return rst;
}
MwsSetExternalLinearSolver函数用于将 Sysplorer 求解器中的线性求解算法设置为自定义的线性求解算法userLinearAlg。只有在算法成功设置后,Sysplorer 才能正确识别并执行用户自定义的算法。设置线性求解算法的主要步骤与快速使用自定义的积分算法中的注册积分算法的步骤极为相似,因此不再赘述。
有关MwsSetExternalLinearSolver函数的使用说明,请参见用户手册的设置函数的模板。