本文讨论如何从 InfoSphere Streams 应用程序内部执行 MATLAB 函数。MATLAB 是一种科学计算语言和平台,为矩阵操作和大量数学建模库提供了强大支持,是实现各种分析资产的最优选择。用其他语言重新实现这些分析资产通常很麻烦,因为要需要寻找一些外部库来提供已经内置于 MATLAB 中的功能。正因如此,开发人员特别希望将用 MATLAB 编写的函数集成到 InfoSphere Streams 应用程序中,这样就不必以 C++ 或 Java™ 重写函数逻辑,这两种语言是 Streams 支持的一级语言。
MATLAB 支持将用 MATLAB 编程语言编写的函数(在 .m 文件中)编译为 C++ 共享库。它还提供一些 API 来编写与生成的库交互的 C++ 代码,比如提供矩阵数据类型的工具类。另外,MATLAB 还提供一个运行库来链接到使用编译为 C++ 的 MATLAB 例程的应用程序。将 MATLAB 函数集成到 Streams 的总方法如下:
- 开发您的 MATLAB 函数。
- 使用 MATLAB 编译器创建一个 C++ 共享库。
- 在 SPL 中创建一个本机函数来封装 C++ 共享库。
- 编写一个 SPL 应用程序来使用这些本机函数。
上述步骤将通过下面这个示例来演示。
您将编写一个简单的 MATLAB 函数来执行矩阵求逆(matrix inversion)运算。
创建一个名为 Matlab_inv 的目录,在其中放入一个名为 ml_inv.m 的文件,该文件包含以下内容:
function out = ml_inv(in) % ML_INV Matrix inverse % Invert a given matrix out = inv(in); |
您的目标是能够在 SPL 代码中轻松使用这个矩阵求逆函数。下面是一个样例 SPL 函数,它利用了包装来自 MATLAB 的 ml_inv 函数的矩阵求逆函数 ml::inv。本文余下部分将介绍创建这个 ml::inv 函数所需的步骤。
void foo()
{ // Sample SPL code that uses the matrix inverse
list<list<float64>> inM = [[1.0,3.0],[2.0,4.0]];
mutable list<list<float64>> outM = [];
ml::inv(outM, inM); // ml::inv is the wrapper for ml_inv
}
|
您将使用 MATLAB 编译器创建一个 C++ 共享库,该库将包含矩阵求逆函数。
- 在 MATLAB 命令行中,键入
deploytool。 - 跟随图形界面在
Matlab_inv目录下创建一个名为libMatlab_inv的部署项目。 - 单击 Add File 按钮将
ml_inv.m文件添加到项目。 - 单击 Build 按钮创建共享库。
下面的结果文件将生成于 Matlab_inv/libMatlab_inv/distrib 目录下。
-
libMatlab_inv.h:这是一个接口文件,用于声明从 MATLAB 代码生成的 C++ 函数。 -
libMatlab_inv.so:这是共享库,包含从 MATLAB 代码生成的 C++ 函数实现。 -
libMatlab_inv.exports:这是一个文本文件,列出了库导出的函数的名称。库操作不需要这个文件。 -
libMatlab_inv.ctf和libMatlab_inv_mcr:前者是一个归档文件,MATLAB 编译器从该文件创建后面的目录。它们包含额外的支持库,应该与.so共享库位于相同的位置。
在 Streams 应用程序中使用共享库之前,先在一个独立 C++ 应用程序中试用它。
创建一个名为 sample_c++ 目录,与 Matlab_inv 位于同一级别。在这个目录中,创建一个名为 sample.cpp 的文件并填充以下内容:
#include "libMatlab_inv.h"
int main()
{
libMatlab_invInitialize();
double data[] = {1.0, 2.0, 3.0, 4.0};
mwArray in(2,2,mxDOUBLE_CLASS);
mwArray out(2,2,mxDOUBLE_CLASS);
in.SetData(data, 4);
ml_inv(1, out, in);
std::cerr << "[" << out(1,1) << ", " << out(1,2) << "; "
<< out(2,1) << ", " << out(2,2) << "]\n";
libMatlab_invTerminate();
return 0;
} |
在上面的代码中,您调用 libMatlab_invInitialize 函数来初始化 MATLAB 运行库。这是 MATLAB 编译器为您生成的函数之一。接下来,您创建了以下两个矩阵:in 和 out。mwArray 是 MATLAB
C++ APIs 提供的一个 C++ 类,用于处理矩阵。您可以使用 ml_inv 函数对 in 矩阵进行求逆运算,并将结果赋予 out 矩阵。这是 MATLAB 编译器根据 ml_inv.m 文件中的同名 MATLAB 函数为您生成的核心函数。最后,您可以调用 libMatlab_invTerminate 函数来终止 MATLAB 运行库。同样,这也是 MATLAB 编译器为您生成的函数。
要编译这个程序,请按以下方式创建一个 Makefile:
.PHONY: all clean
MATLAB_LIBRARY_LOCATION := /nfs/hny/apps01/matlab/bin/glnxa64/
MATLAB_INCLUDE_LOCATION := /nfs/hny/apps01/matlab/extern/include/
all:
g++ -o sample sample.cpp \
-I ../Matlab_inv/libMatlab_inv/distrib \
-I $(MATLAB_INCLUDE_LOCATION) \
-L ../Matlab_inv/libMatlab_inv/distrib \
-Wl,-rpath,../Matlab_inv/libMatlab_inv/distrib \
-L $(MATLAB_LIBRARY_LOCATION) \
-Wl,-rpath,/nfs/hny/apps01/matlab/bin/glnxa64/ \
-lMatlab_inv -lmwmclmcrrt
clean:
rm sample |
应该注意以下几点:
- 路径
-I ../Matlab_inv/libMatlab_inv/distrib用于指定 MATLAB 编译器生成的接口文件(libMatlab_inv.h)的位置。 - 路径
-I $(MATLAB_INCLUDE_LOCATION)用于指定 MATLAB C++ APIs 的接口文件的位置。这个位置特定于您的环境,取决于 MATLAB 安装的位置。应该对变量$(MATLAB_INCLUDE_LOCATION)进行相应的定义。 - 库路径
-L ../Matlab_inv/libMatlab_inv/distrib用于指定 MATLAB 编译器生成的库(libMatlab_inv.so)的位置。 - 库路径
-L $(MATLAB_LIBRARY_LOCATION)用于指定 MATLAB 运行库的位置。这个位置特定于您的环境,取决于 MATLAB 安装的位置和您的系统架构。变量$(MATLAB_LIBRARY_LOCATION)应该被相应定义。 - 库
-lMatlab_inv用于指定 MATLAB 编译器生成的库的名称(libMatlab_inv.so)。可以使用相同路径指定 RPATH,以便获得该库的运行时位置。 - 库
-lmwmclmcrrt用于指定 MATLAB 运行库的名称(libmwmclmcrrt.so)。
键入 make 构建库并键入 ./sample 运行它。输出应该如下所示:
[-2, 1.5000; 1,
-0.5000]。
您将在 SPL 中创建一些本机函数,以便利用 MATLAB 编译器生成的函数。方法有两种:创建一个工具包来封装所有本机函数;或者将本机函数直接包含到应用程序中。如果有多个应用程序要利用这些函数,则第一种方法更适合。为简便起见,我们在这里将使用第二种方法。
- 创建一个名为
sample_spl的目录,它与Matlab_inv和sample_c++位于同一级别。该目录将是您的应用程序目录。 - 创建一个名为
ml的子目录。这将作为您的名称空间目录。 - 在
ml下创建一个名为native.function的子目录,用于存放函数模型。 - 在此目录下,添加名为
function.xml的函数模型文件,该文件包含以下内容:
<functionModel
xmlns="http://www.ibm.com/xmlns/prod/streams/spl/function"
xmlns:cmn="http://www.ibm.com/xmlns/prod/streams/spl/common"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ibm.com/xmlns/prod/streams/spl/function
functionModel.xsd">
<functionSet>
<headerFileName>spl_ml_inv.h</headerFileName>
<functions>
<function>
<description>Take inverse of a matrix</description>
<prototype><![CDATA[ public void inv(mutable list<list<float64>> r,
list<list<float64>> s) ]]></prototype>
</function>
<function>
<description>Initialize Matlab runtime</description>
<prototype><![CDATA[ public void initialize() ]]></prototype>
</function>
<function>
<description>Finalize Matlab runtime</description>
<prototype><![CDATA[ public void terminate() ]]></prototype>
</function>
</functions>
<dependencies>
<library>
<cmn:description>Matrix inverse</cmn:description>
<cmn:managedLibrary>
<cmn:lib>Matlab_inv</cmn:lib>
<cmn:lib>mwmclmcrrt</cmn:lib>
<cmn:libPath>../../impl/lib</cmn:libPath>
<cmn:libPath>/nfs/hny/apps01/matlab/bin/glnxa64/</cmn:libPath>
<cmn:includePath>../../impl/include</cmn:includePath>
<cmn:includePath>/nfs/hny/apps01/matlab/extern/include/</cmn:includePath>
</cmn:managedLibrary>
</library>
</dependencies>
</functionSet>
</functionModel> |
在上面的函数模型文件中,有几点需要注意:
- 文件
spl_ml_inv.h是头文件,包含将封装 MATLAB 生成的函数的 C++ 函数。 - 三个 SPL 本机函数
inv、initialize和terminate分别对应 MATLAB 编译器生成的三个函数ml_inv、libMatlab_invInitialize和libMatlab_invTerminate。请注意,inv函数使用 SPL 嵌套列表来表示矩阵,因为 SPL 中没有矩阵类型。您将看到,inv函数还将执行 SPL C++ 类型和 MATLAB C++ 类型之间的转换。 - 库
Matlab_inv和mwmclmcrrt被指定为依赖项,它包含这些库的库路径和包含路径 (include path)。请注意,Matlab_inv库的包含路径和库路径是相对于模型文件进行指定的。
现在,您将相关文件复制到应用程序目录 sample_spl 下的 impl 子目录中,使应用程序成为自含应用程序。
- 在应用程序目录
sample_spl下创建目录impl。 - 在
impl目录下创建两个子目录:include和lib。将libMatlab_inv.h文件从Matlab_inv/libMatlab_inv/distrib目录复制到include目录中。 - 现在,将
libMatlab_inv.so和libMatlab_inv.ctf文件以及libMatlab_inv_mcr目录复制到lib目录中。 - 最后,在
include目录下创建封装器spl_ml_inv.h头部文件,文件内容如下:
#include "libMatlab_inv.h"
namespace ml {
void initialize() {
libMatlab_invInitialize();
}
void terminate() {
libMatlab_invTerminate();
}
void inv(SPL::list<SPL::list<SPL::float64> > & lhs,
SPL::list<SPL::list<SPL::float64> > const & rhs)
{
size_t nr = rhs.size(), nc = rhs[0].size();
mwArray in(nr, nc, mxDOUBLE_CLASS);
mwArray out(nr, nc, mxDOUBLE_CLASS);
for(size_t r=0; r<nr; ++r)
for(size_t c=0; c<nc; ++c)
in(r+1,c+1) = rhs[r][c];
ml_inv(1, out, in);
lhs.resize(nr);
for(size_t r=0; r<nr; ++r) {
lhs[r].resize(nc);
for(size_t c=0; c<nc; ++c)
lhs[r][c] = out(r+1,c+1);
}
}
} |
在上面的代码中,inv 函数执行 SPL list 类型和 MATLAB mwArray 类型之间的转换,以便封装 MATLAB 编译器生成的 ml_inv 函数。注意,所有的封装器函数都放置到名称空间 ml 中,因为 native.function 目录位于名称空间目录 ml 下。
您将编写一个使用本机函数的 SPL 应用程序,这些本机函数封装 MATLAB 编译器生成的函数。
在 sample_spl 目录下创建一个名为 Main.spl 的文件,文件内容如下:
composite Main {
graph
stream<int8 dummy> Beat = Beacon() {
param iterations : 1u;
}
() as Sink = Custom(Beat) {
logic
onTuple Beat: {
ml::initialize();
list<list<float64>> inM = [[1.0,3.0],[2.0,4.0]];
mutable list<list<float64>> outM = [];
ml::inv(outM, inM);
println(outM);
ml::terminate();
}
}
} |
该样例 SPL 应用程序将初始化 MATLAB 运行库,通过 ml::inv 函数执行一个矩阵求逆运算,然后终止 MATLAB 运行库。
- 在
sample_spl目录中键入sc -m -M Main,为该应用程序创建一个 Makefile。 - 然后键入
make standalone构建它。 - 您将看到一些警告。忽略这些多余的警告(Streams 的未来版本可能会包含抑制这些警告的支持),键入
./output/bin/standalone来运行该应用程序。您应该能看到以下结果:[[-2,1.5],[1,-0.5]]。
本文介绍了如何将 MATLAB 函数集成到 Streams 应用程序中,而无需以另一种语言重写函数逻辑。这种方法依靠 MATLAB 编译器将 MATLAB 代码转换为 C++ 共享库,并编写一个封装这个共享库的 C++ 本机函数,然后使用一个函数模型将封装器函数导入 SPL 中。本文还提供了我们讨论的示例的样例源代码。
| 描述 | 名字 | 大小 | 下载方法 |
|---|---|---|---|
| 本文样例代码 | MatlabStreamsIntegration.zip | 322KB | HTTP |
学习
- 进一步了解 “IBM InfoSphere Streams”(2011 年 5 月),这是 Streams 的产品页。
- 进一步了解 “MathWorks MATLAB Compiler”(2011 年 5 月),这是 MATLAB Compiler 的产品页。
- 在 developerWorks 中国网站 Information Management 专区 了解关于信息管理的更多信息,从那里可以获取技术文档、how-to 文章、培训、下载、产品信息以及其他资源。
- 观看 developerWorks 演示中心,包括面向初学者的产品安装和设置演示,以及为经验丰富的开发人员提供的高级功能。
获得产品和技术
- 使用可从 developerWorks 直接下载的 IBM 产品评估试用版软件 构建您的下一个开发项目,或者在 IBM SOA 人员沙箱 中花费几个小时,学习如何高效实现 Service Oriented Architecture。
讨论
- 参与论坛讨论。
- 查看 developerWorks 社区中的 Stream Processing 小组,这个小组主要关注 InfoSphere Streams。
- 查看 developerWorks 博客 并加入 developerWorks 中文社区,developerWorks 社区是一个面向全球 IT 专业人员,可以提供博客、书签、wiki、群组、联系、共享和协作等社区功能的专业社交网络社区。
