M兼容工具:兼蓄经典,实现模型代码资产高效复用(上篇)

一、引言

M兼容工具:兼蓄经典,实现模型代码资产高效复用(上篇)科学计算与系统建模仿真是装备数字化的重要落地支撑,是避免受制于人的关键核心技术。近年来,同元软控围绕行业应用求和装备数字化需求,持续迭代完善MWORKS,今年发布的MWORKS 2024是全球第四个完整的科学计算与系统建模仿真一体化平台,为世界提供科学计算与系统建模仿真平台的中国选项。

依托多年的编译技术沉淀、复杂装备系统的软件研发与工程实践经验、自主开发的高质量系列模型库及函数库,MWORKS 2024全新推出M语言兼容工具和Simulink模型导入工具,无需安装MATLAB,即可实现M语言脚本的直接运行和Simulink模型的导入与转换,实现对用户MATLAB模型代码资产的高效复用。兼包并蓄,不止于替代。

M兼容工具分为上下两篇,上篇主要介绍M语言兼容工具,下篇主要介绍Simulink模型导入工具。

二、M语言兼容工具

2.1 概述

M语言兼容工具(即TyMLang),是同元软控新推出的一款实用工具,无需安装 MATLAB,原生支持MATLAB代码文件的解释与运行,并内置提供1260+ 常用M函数,覆盖基础、数学、图形、控制、信号等领域,帮您实现历史代码资产的高效复用。它运行在MWORKS.Syslab 上,并支持M语言与Julia语言的相互调用,无缝衔接MATLAB生态与Julia科学计算生态。

M语言兼容工具主要由三部分组成:一是IDE,支持M语言的编辑、运行与调试;二是M语言执行环境,系统性的原生支持MATLAB/M语言的核心语法、程序行为和特殊机制;三是M函数库,包括内置常用函数、外部语言函数(MEX机制)、以及MATLAB专业函数。

其中,M语言执行环境是整个M兼容工具的核心,即使是其中较为简单的解析器,其难度、工作量也不容小觑。MATLAB语法没有公开规范,并在40年的发展中积累大量边缘语法、上下文相关语法。因此, M语言解析器开发与一般编译解析的小步快速迭代过程不同,M语言解析器不仅要快速迭代,还需要随时推翻之前总结的规范重新来过。

在这种风险极大、重构极频繁的解析器工作中,同元软控充分研究了前沿的解析器维护技术,综合了解析器生成器 + 语法制导 + BNF静态类型检查 + 语法树领域建模,在底层保证每次推翻规范、重构代码后,可通过生成器快速生成成品,可通过静态检查和领域建模以确定性手段排查旧代码中的错误规范/假设,从而确保M语言解析器的高可用。实现M语言执行环境所面临的困难与挑战,从其冰山一角的M语言解析器上可见一斑。

2.2 亮点功能

1)无需安装MATLAB,直接在Syslab上运行

MWORKS.Syslab是多语言科学计算环境,以Julia为主语言,同时支持Python、M等编程语言。Syslab内置提供了M扩展插件(TyMLangIDE)、M编译器(mc.exe)和M函数库,因此无需安装MATLAB,M语言脚本即可直接在Syslab上打开、编辑和运行。

2)M语言及程序行为兼容度超过90%

MATLAB语法没有公开规范,多年来积累形成了大量边缘语法、上下文相关语法。通过大量资料的查阅与推理,以及在工程实践中反复锤炼,MWORKS.Syslab对M语言及程序行为兼容度超过90%。

3)支持代码调试

M语言兼容工具,支持M语言脚本的代码调试,包括设置断点、启动调试、单步调试、断点调试、查看调试变量等。

4)支持MEX机制

MEX是MATLAB内的一种扩展,用于在M语言中调用C/C++编写的函数。MEX 文件的源代码用C编写,并通过一组MEX API,提供了C与M语言的数组之间交互的功能。遵循以下的入口函数规范,将对应C文件编译成动态库后可直接由M语言调用。

#include "mex.h"
void mexFunction ( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] )
{
    // ...
}

其中nlhs表示M语言在调用该函数时的实际参数个数,plhs表示指向参数列表的指针,nrhs 表示调用该函数时的返回值个数,prhs 表示指向返回值列表的指针。

M语言兼容工具支持将符合MEX接口规范的C文件编译成动态库,并能加载和调用该动态库。例如,创建一个c_cumsum.c文件,完整代码如下:

#include "mex.h"
#include "assert.h"
#include "stdio.h"
#include "math.h"

void mexFunction(int nlhs, mxArray *plhs[],
                 int nrhs, const mxArray *prhs[]) {
    // 检查输入
    if (nlhs == 0) {
        return;
    }
    if (nlhs > 1) {
        mexErrMsgTxt("Too many output arguments.");
    }
    if (nrhs != 1) {
        mexErrMsgTxt("one input required.");
    }
    if (!mxIsDouble(prhs[0])) {
        mexErrMsgTxt("Input must be of type double.");
    }

    // 提取指向输入数组的数据的指针
    double *input1 = mxGetPr(prhs[0]);
    int N = mxGetNumberOfElements(prhs[0]);

    // 创建和输入相同大小的矩阵作为输出
    int s1 = mxGetM(prhs[0]);
    int s2 = mxGetN(prhs[0]);
    mxArray *B = mxCreateDoubleMatrix(s1, s2, mxREAL);
    double *out = mxGetPr(B);
    double tmp = 0;

    // cumsum,求累加和
    for (int i = 0; i < N; ++i) {
        tmp += input1[i];
        out[i] = tmp;
    }

    // 将结果赋给 lhs
    plhs[0] = B;
}

启动M语言兼容工具,输入以下命令,即可将c_cumsum.c文件编译成动态链接库c_cumsum.mexdll,然后加载动态库并调用其算法函数。

5)支持搜索路径

搜索路径是文件系统中所有文件夹的子集,使用搜索路径来高效地定位用于产品的文件。搜索路径上的文件夹顺序十分重要。当在搜索路径上的多个文件夹中出现同名文件时,将使用搜索路径中最靠前的文件夹中的文件。

M语言兼容工具支持查看或更改搜索路径,并提供设置路径对话框,方便用户使用。

6)支持Julia与M语言的互调用

M语言兼容工具,支持Julia与M的语言互调用,能够调用同元软控Julia高性能科学计算函数库,无缝衔接MATLAB生态与Julia生态。

①M语言调用Julia函数M语言兼容工具提供了一套API,支持完备的Julia调用,包括:

⦁获取Julia对象的原始引用

⦁Julia对象与M对象的类型转换

⦁调用Julia函数

⦁Julia对象操作

M调用Julia的API 说明
jv = Julia(o)
jv = Julia(o, jtype)
将M类型对象转为Julia类型对象
o = fromJulia(jv)
o = fromJulia(jv, mtype)
将Julia类型对象转为M类型对象
jv = jeval(s) 执行Julia代码,返回Julia对象
jv = jcall(expr, jv1, ..., jvn)

jv = jcall(__, '-kwargs', kws)
jv = jcall('-return', mtype, __)
jv = jcall('-vector', __)
jv = jcall('-noreturn', __)
调用Julia函数,返回Julia对象
jv = jinterp(expr, opts) jinterp比jeval开销更少
jvi = jindex(jv, {i1, ..., in})
jvi = jindex(jv1, {i1, ..., in}, jv2)
获取Julia对象的索引,返回Julia对象
jisa(jv, jtype) 判断类型,返回logical
jfunc = jcallable(mfun, {rettypes}, {argtypes}) 返回一个Julia的callable对象

不妨以中值滤波的噪声抑制为例,考虑到MWORKS.Syslab信号处理函数库已提供一维中值滤波函数medfilt1,此时用户M代码若需要调用该函数,可以基于上述API来实现,完整代码如下:

fs = 100;
t = 0:1/fs:1;
x = sin(2*pi*t*3)+0.25*sin(2*pi*t*40);

% 调用同元信号处理函数库的一维中值滤波medfilt1函数
jv_x = Julia(x);
jv_y = jcall("TySignalProcessing.medfilt1", jv_x, Julia(10, "Int"));
y = fromJulia(jv_y, "double")

plot(t,x,t,y)
legend('Original','Filtered')

在MWORKS.Syslab上直接运行上述M脚本,将得到如下运行结果:

②Julia语言调用M函数M语言兼容工具,除了支持M语言调用Julia函数,同样也支持Julia语言调用M函数,包括在Julia执行M代码、操作M语言的对象、类型转换以及调用M语言的函数。

Julia调用M的API 说明
eval_mcode!(code) 在 Julia 中直接运行 M 代码,返回值为 nothing
to_mlang(x) 将一个 Julia 对象转换为 MxArray 类型
from_mlang(pm::MxArray)
from_mlang(T::Type, pm::MxArray)
MxArray 类型转换对应的 Julia 类型
mexCallMLang(funName::String, nargout::Integer, args::Vector{MxArray}) funName 为调用的 M 函数,nargout 为 M 函数的返回参数个数,返回值为 Vector{MxArray}
mexGetVar(varName::String) 获取 MLang REPL 中的变量,返回值为 MxArray
mxget_cell(pm::MxArray, i) 获取元胞数组 pm 的第 i 个元素,返回值为 MxArray 类型
mxset_cell(pm::MxArray, i, value::MxArray) 将元胞数组 pm 的第 i 个元素设置为 value
mxget_field(pm::MxArray, i, fieldname) 获取结构体 pm 的第 i 个元素的 fieldname 字段,返回值为 MxArray 类型
mxset_field(pm::MxArray, i, fieldname, value::MxArray) 将结构体 pm 的第 i 个元素的 fieldname 字段设置为 value
mxndims(pm::MxArray) 获取 pm 的维度
nrows(pm::MxArray) 获取 pm 的行数
ncols(pm::MxArray) 获取 pm 的列数
nelems(pm::MxArray) 获取 pm 的元素个数
size_vec(pm::MxArray) 获取 pm 的 shape, 返回值为 Vector{Int}
is_class(pm::MxArray, classname) 判断 pm 是否为 classname 类的实例
is_empty(pm::MxArray) 判断 pm 是否为空数组
is_scalar(pm::MxArray) 判断 pm 是否为标量
is_numeric(pm::MxArray) 判断 pm 是否为数值类型
is_char(pm::MxArray) 判断 pm 是否为字符数组类型
is_logical(pm::MxArray) 判断 pm 是否为 logical 类型
is_double(pm::MxArray) 判断 pm 是否为 double 类型
is_complex(pm::MxArray) 判断 pm 是否为 complex
is_struct(pm::MxArray) 判断 pm 是否为 MLang 结构体数组
is_cell(pm::MxArray) 判断 pm 是否为 MLang 元胞数组

不妨以求输入向量的均值和标准差为例,假设已存在M统计函数stat,若Julia语言需要调用该算法函数,可以基于上述API来实现调用,完整代码如下:

import TyMLangCore
TyMLangCore.eval_mcode!(raw"""
function [m,s] = stat(x)
    n = length(x);
    m = sum(x)/n;
    s = sqrt(sum((x-m).^2/n));
end
""")

mx = TyMLangCore.to_mlang([12.7, 45.4, 98.9, 26.6, 53.1])
mv = TyMLangCore.mexCallMLang("stat", 2, [mx])

ave = TyMLangCore.from_mlang(mv[1])
println("ave=$ave")

stdev = TyMLangCore.from_mlang(mv[2])
println("stdev=$stdev")

在MWORKS.Syslab中执行上述Julia代码,得到运行结果:

2.3 M函数列表

如果只有M语言解析器,常用的数学或专业算法函数都需要用户自己编写或提供,那么该产品将只是一个没有灵魂的空壳。为了让用户实现“开箱即用、专注业务”,M语言兼容工具目前已内置提供1260+ 常用M函数,覆盖基础、数学、图形、控制系统、信号处理、优化等领域常用函数。

打开MWORKS.Syslab帮助文档,点击左侧目录的M语言兼容工具/支持的M函数列表,可以查阅已支持的M函数列表。如果内置提供的M函数列表不满足用户使用场景,还可以基于上述的M与Julia语言互调用技术,调用同元Julia高性能科学计算函数库,支持用户自定义扩展M函数。

三、典型示例

不妨拿通信行业较为著名的Delta-Sigma开源工具箱为例。该工具箱支持Delta-Sigma模拟器的合成、模拟、实现和动态,能够用来计算Delta-Sigma 模拟器的NTF(噪声传输函数)和STF(信号传输函数),并进行调节。Delta-Sigma工具箱用到了基础、数学、图形、APP、控制系统、优化、信号处理的一些算法函数,具有一定的工程规模和代码复杂度。

得益于高兼容度的M语言解析器和丰富的M函数库,用户无需修改Delta-Sigma任何源码,在MWORKS.Syslab上可以直接打开、运行或调试,并能得到正确结果。

动图封面

四、总结

目前,M语言兼容工具在工程实践上反复迭代,某芯片产业的77+应用场景案例,8万行代码,实现100% 精准对标。M语言兼容工具达到了一定的兼容度,但与拥有四十年以上积累的MATLAB相比,仍然面临着以下挑战:

部分语言特性的差异:MATLAB为并行计算引入了 parfor、spmd 等内置语法,TyMLang正在计划逐步接入。

边缘语法的差距:虽然MATLAB的语法相对简单,但在边缘情况却有大量细节需要注意。在这种情况下,只能通过不断地测试和修复,来逐步完善TyMLang的语法兼容性,这是一个长期的过程。

⦁内置函数的差距:MATLAB内置了许多工具箱,这些工具箱包含了大量函数。函数的缺失,导致用户在使用TyMLang时可能需要自行实现这些函数,或调用同元软控Julia科学计算函数库。但是,TyMLang将会源源不断地扩充常用M函数库。

兼蓄经典,不止于替代。在长期服务于重大工程的过程中,同元软控始终致力于做好新一代科学计算与系统建模仿真的底座,提供开放的接口,依托中国科学与工程的创新发展需求、高校院所3800万师生和工业企业4000万工程师,共同创建MWORKS的应用生态,立足创新,不断实现自我突破。

最近更新

  1. TCP协议是安全的吗?

    2024-04-07 17:38:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-07 17:38:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-07 17:38:01       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-07 17:38:01       20 阅读

热门阅读

  1. c++算法学习笔记 (21) STL

    2024-04-07 17:38:01       13 阅读
  2. 搜索(DFS BFS)

    2024-04-07 17:38:01       15 阅读
  3. bash find: get directory of found file

    2024-04-07 17:38:01       13 阅读
  4. Electron 是一个流行的框架

    2024-04-07 17:38:01       14 阅读
  5. golang channel

    2024-04-07 17:38:01       14 阅读
  6. C++ 【填充书架】

    2024-04-07 17:38:01       22 阅读
  7. ChatGPT Excel 大师

    2024-04-07 17:38:01       13 阅读