Вызов MATLAB из Mathematica с помощью MATLink
Как можно вызывать функции MATLAB напрямую из Mathematica и организовать обмен данными и переменными между двумя системами?
Для этого существует кроссплатформенный пакет под названием MATLink. С помощью него легко организовать вызов функций MATLAB прямо из Mathematica и передавать различные данные от одной системы другой.
Приведу небольшую инструкцию:
? Установка
Сперва стоит перейти на главную страницу MATLink и выполнить указанные там инструкции. Самый простой путь — скачать архив и распаковать его в эту папку:
In[1]:=
SystemOpen@FileNameJoin[{$UserBaseDirectory, "Applications"}]
Далее следует выполнить инструкции для конкретной операционной системы в разделе «Link with MATLAB» на главной странице.
? Использование MATLink
MATLink можно загрузить, вычислив ячейку с кодом:
In[2]:=
Needs["MATLink`"]
А затем запустить MATLAB командой:
In[3]:=
OpenMATLAB []
Таким образом будет запущен новый процесс в MATLAB, с которым Mathematica сможет взаимодействовать.
Для использования произвольных команд MATLAB'а следует использовать MEvaluate. Данные будут переданы в виде строки.
Пример:
In[4]:=
MEvaluate["magic(4)"]
Out[4]=
(* ==>
ans =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
*)
Чтобы передать данные в MATLAB, следует использовать MSet:
In[5]:=
MSet["x", Range[10]];MEvaluate["x"]
Out[5]=
(* ==>
x =
1 2 3 4 5 6 7 8 9 10
*)
Чтобы вернуть данные обратно, следуйте использовать MGet:
In[6]:=
MGet["x"]
Out[6]=
(* ==> {1., 2., 3., 4., 5., 6., 7., 8., 9., 10.} *)
Поддерживается большое количество типов данных, включая разреженные массивы, структуры и ячейки.
Функции MATLAB'а могут вызываться непосредственно из Mathematica с помощью функции MFunction:
In[7]:=
eig = MFunction["eig"];
eig[{{1, 2}, {3, 1}}]
Out[7]=
(* ==> {{3.44949}, {-1.44949}} *)
В документации можно более подробно прочесть об особенностях использования и прочих функциональных возможностях.
Несколько простых примеров
Построим в Mathematica для начала поверхность логотипа MATLAB и добавим манипулятор, который будет регулировать период колебаний:
In[8]:=
Manipulate[
ListPlot3D@MFunction["membrane"][k],
{k, 1, 12, 1}
]
Out[8]=
Фуллереновая структура (bucky ball) прямо из MATLAB:
In[9]:=
AdjacencyGraph@Round@MFunction["bucky"][]
Out[9]=
Отобразить данные из Mathematica в отдельном масштабируемом окне для рисунков, которые применяются в MATLAB также легко:
In[9]:=
mlf = LibraryFunctionLoad["demo_numerical", "mandelbrot", {Complex}, Integer];
mandel = Table[mlf[x + I y], {y, -1.25, 1.25, .002}, {x, -2., 0.5, .002}];
MFunction["image", "Output" -> False][mandel]
Out[9]=
Несколько примеров посложнее
Указанные ниже примеры иллюстрируют решения реальных задач с использованием MATLink, позволяя использовать лучшие качества систем MATLAB и Mathematica.
? Быстрые триангуляции Делоне
В составе Mathematica содержится функция DelaunayTriangulation внутри пакета ComputationalGeometry (в 10-й версии этот пакет стал встроенным в ядро и теперь эта функции носит имя DelaunayMesh, она оптимизирована и теперь её производительность не уступает MATLAB — прим. ред.), однако работает она весьма медленно (хотя у неё есть и свои сильные стороны, такие как использование точной арифметики и работа с коллинеарными точками). Это, в свою очередь, приводит к тому, что ListDensityPlot работает весьма неэффективно (что становится заметно при построении нескольких тысяч точек и более). С использованием MATLink, мы можем задействовать функцию Делоне из MATLAB для вычисления триангуляции Делоне некоторого набора точек следующим образом:
In[10]:=
delaunay = Composition[Round, MFunction["delaunay"]];
Поскольку функция системы Mathematica возвращает список смежных вершин, нам необходимо провести пост-обработку результата для того, чтобы сравнить с результатом из MATLAB'а:
In[11]:=
Needs["ComputationalGeometry`"];
delaunayMma[points_] :=
Module[{tr, triples},
tr = DelaunayTriangulation[points];
triples = Flatten[
Function[{v, list},
Switch[Length[list],
(* account for nodes with connectivity 2 or less *)
1, {},
2, {Flatten[{v, list}]}, _, {v, ##}& @@@ Partition[list, 2, 1, {1, 1}]]
] @@@ tr, 1];
Cases[GatherBy[triples, Sort], a_ /; Length[a] == 3 :> a[[1]]]
]
Случайный набор точек обычно имеет уникальную триангуляцию Делоне, так что нам необходимо будет проверить, что системы выдают один и тот же результат.
In[12]:=
pts = RandomVariate[NormalDistribution[], {100, 2}];
Sort[Sort /@ delaunay[pts]] === Sort[Sort /@ delaunayMma[pts]]
И построим триангуляцию с помощью:
In[13]:=
trianglesToLines[t_] :=
Union@Flatten[{{#1, #2}, {#2, #3}, {#1, #3}} & @@
Transpose[Sort /@ t], {{1, 3}, {2}}];
Graphics@GraphicsComplex[pts, Line@trianglesToLines@delaunay[pts]]
Out[13]:=
Однако помимо того, что delaunay работает значительно быстрее DelaunayTriangulation (особенно для больших наборов данных), она также быстрее и triangulator, который используется внутри ListDensityPlot. Следовательно, мы можем использовать delaunay из MATLAB'а для разработки своей версии listDensityPlot, которая работает быстрее, чем встроенная функция, а также может обрабатывать большие наборы данных следующим образом:
In[14]:=
Options[listDensityPlot] = Options[Graphics] ~Join~ {ColorFunction -> Automatic, MeshStyle -> None, Frame -> True};
listDensityPlot[data_?MatrixQ, opt : OptionsPattern[]] :=
Module[{in, out, tri, colfun},
tri = delaunay[data[[All, 1;;2]]];
colfun = OptionValue[ColorFunction];
If[Not@MatchQ[colfun, _Symbol | _Function], Check[colfun = ColorData[colfun], colfun = Automatic]];
If[colfun === Automatic, colfun = ColorData["LakeColors"]];
Graphics[
GraphicsComplex[data[[All, 1;;2]],
GraphicsGroup[{EdgeForm[OptionValue[MeshStyle]], Polygon[tri]}],
VertexColors -> colfun /@ Rescale[data[[All, 3]]]
],
Sequence @@ FilterRules[{opt}, Options[Graphics]], Method -> {"GridLinesInFront" -> True}
]
]
Давайте теперь сравним нашу функцию со встроенной, используя при этом массив из 30 000 точек:
In[15]:=
pts = RandomReal[{-1, 1}, {30000, 2}];
values = Sin[3 Sqrt[#1^2 + #2^2]] & @@@ pts;
In[16]:=
listDensityPlot[ArrayFlatten[{{pts, List /@ values}}], Frame -> True] // AbsoluteTiming
Out[16]=
{0.409001, --Graphics--}
In[17]:=
ListDensityPlot[ArrayFlatten[{{pts, List /@ values}}]] // AbsoluteTiming
Out[17]=
{12.416587, --Graphics--}
Разница в скорости выполнения получилась весьма значительной (~30 раз). Для работы с сотнями тысяч точек ListDensityPlot оказывается практически непригодной, в то время как listDensityPlot требует лишь несколько секунд.
Также важно учитывать, что для замеров скорости работы MATLink необходимо использовать функцию AbsoluteTiming, с помощью которой вычисляется всё затраченное время, в то время как Timing измеряет только то время, когда CPU использовался ядром Mathematica, не измеряя время, которое затратил MATLAB.
? Фильтрация аудиоданных с помощью инструментов для обработки сигналов (signal processing toolbox)
Как известно, функционал по обработке сигналов отсутствовал в Mathematica до девятой версии, и по-прежнему уступает инструментам MATLAB с точки зрения функциональности и простоты использования. Предположим, у нас 8 версия Mathematica, новые функции отсутствуют и мы хотим провести частотный анализ некоторого аудиофайла и реализовать фильтрацию. Вот как это можно будет сделать:
In[18]:=
{data, fs} = {#[[1, 1, 1]], #[[1, 2]]} &@ExampleData[{"Sound", "Apollo13Problem"}];
spectrogram = MFunction["spectrogram", "Output" -> False]; (* Use MATLAB's spectrogram *)
spectrogram[data, 1000, 0, 1024, fs]
Очевидно, что частоты в основном приходятся на диапозон ниже 2,5 кГц, так что мы можем разработать в MATLAB фильтр нижних частот, а также сделать вспомогательную функцию, которая будет возвращать отфильтрованные данные:
In[19]:=
MSet["fs", fs];
MEvaluate["
[z, p, k] = butter(6, 2.5e3/fs, 'low') ;
[sos, g] = zp2sos(z, p, k) ;
Hd = dfilt.df2tsos(sos, g) ;
"]
filter = MFunction["myfilt", "@(x)filter(Hd,x)"];
Out[19]=
Теперь мы всё подготовили для того, чтобы применять фильтрующую функцию к данным напрямую из Mathematica. Этот пример показывает, как мы можем восполнить пробелы в функциональности. Таким образом, мы можем сэкономить большое количество времени на проектирование фильтра в Mathematica (а это не самая простая задача) и многие часы на его отладку. Код для фильтра Баттерворта можно взять откуда угодно — c файлообменника или Stack Overflow, из фрагментов ранее написанного кода, или, как в данном случае, из примера в документации. Небольшие изменения в параметрах под текущие нужды, и мы теперь можем работать с этим материалом в Mathematica.
В качестве последнего примера предлагаю обработать некоторые данные с помощью нашего фильтра и построить спектрограмму:
In[19]:=
filteredData = filter@data;
spectrogram[filteredData, 1000, 0, 1024, fs]
Чисто ради забавы можем проиграть оба аудиофайла — отфильтрованный и исходный —и сравнить разницу в их звучании:
In[20]:=
ListPlay[data, SampleRate -> fs]
ListPlay[filteredData, SampleRate -> fs]
Если вдруг вы найдёте какие-то ошибки и проблемы в работе MATLink, пожалуйста, сообщите о них на почту (matlink.m@gmail.com), либо на GitHub, либо в комментариях к оригиналу этой статьи.
Дополнения
? Вызов MATLAB из Mathematica с помощью NETLink
Небольшое замечание о том, как можно вызвать MATLAB с помощью NETLink в операционной системе Windows, используя COM-интерфейс MATLAB:
In[21]:=
Needs["NETLink`"]
matlab = CreateCOMObject["matlab.application"]
Out[21]=
«NETObject[COMInterface[MLApp.DIMLApp]]»
Теперь можно обращаться к функциям MATLAB:
In[22]:=
In[4]:= matlab@Execute["version"]
Out[22]=
"
ans =
7.9.0.529 (R2009b)
"
In[23]:=
matlab@Execute["a=2"];matlab@Execute["a*2"]
Out[23]=
"
ans =
4
"
? О преобразовании выражений из синтаксиса Mathematica в синтаксис MATLAB'а
Существует пакет под названием ToMatlab, который преобразовывает выражения из Mathematica в их MATLAB-эквиваленты. Вот пример:
In[24]:=
<<ToMatlab`
Expand[(x + Log@y)^5] // ToMatlab
Out[24]:=
x.^5+5.*x.^4.*log(y)+10.*x.^3.*log(y).^2+10.*x.^2.*log(y).^3+5.* ...
x.*log(y).^4+log(y).^5;
Можно заметить, что выражения весьма удобным способом разбиваются с использованием ...;
Вот ещё пример с конвертацией матриц:
In[25]:=
RandomInteger[5, {5, 5}] // ToMatlab
Out[25]:=
[5,0,5,3,4;
5,5,3,0,2;
1,4,4,4,4;
0,3,2,5,5;
4,5,5,1,1];
Стоит отметить, что такие вещи, как общие определения, не могут быть преобразованы в синтаксис MATLAB'а. Разумеется, не могут быть сконвертированы и те конструкции, которые не поддерживаются MATLAB'ом — шаблонные выражения, например.
Чтобы установить этот пакет, достаточно просто извлечь файл ToMatlab.m в папку:
In[26]:=
FileNameJoin[{$UserBaseDirectory, "Applications"}]
По вопросам о технологиях Wolfram пишите на info-russia@wolfram.com
Поделиться с друзьями