Delphi / [Из песочницы] Использование dll библиотек математического ядра системы Matlab в приложениях Delphi

в 13:07, , рубрики: Новости, метки: , , , ,

Матлаб для математика — это… Это не имеет выражения в словах. Это просто наше все. Мощь, предоставляемая этим пакетом математического ПО просто ошеломляет. Если что-то нельзя сделать в матлабе, то это, скорее всего, нельзя сделать на компьютере вообще. А еще вероятнее, что Вы просто не знаете, как это делать.А теперь предадимся мечтам: как бы хотелось иметь всю эту мощь под рукой в родной и милой сердцу Delphi, например… Нет ничего проще!
Маленькое и незаметное примечание

Данная статья затрагивает лишь малую часть такой обширной темы, как взаимодействие разрабатываемых прикладных программ с сервисами, предлагаемыми Матлабом. Помимо описанного тут, существуют и другие способы воспользоваться услугами этого элитного математического пакета. Описанный способ хорош тем, что при наличии всех нужных dll библиотек, сам Матлаб уже не нужен. К тому же, данный способ относительно неплох в плане производительности. Минус его в том, что приходится таскать за собой пак библиотек почти на 10 Мб. Также не доступны функции тулбоксов Матлаба.
О чем речь?

Математическое ядро Матлаба до xx версии (ну забыл я, до какой!) было сосредоточено в пакете dll библиотек. В этих библиотеках хранятся функции для выполнения самых различных вычислений. Если установить Матлаб без единого тулбокса и без Симулинка, то все вычисления, которые можно выполнить в командной строке среды, реализованы через функции из этих самых библиотек. Помните Pascal Numerical Toolbox? Так вот он — игрушечная лопатка, по сравнению с бульдозером Матлаба. А в чем же подвох? Вам придется изрядно потрудиться, чтобы этот бульдозер завести.
Большое и жирное примечание

dll-библиотеки математического ядра Матлаба до определенной версии употребляются самим Матлабом, поэтому лежат среди исполняемых файлов пакета. В последних версиях самим Матлабом они уже не используются, однако архив с ними все равно поставляется вместе с пакетом (Необходим для работы программ, созданных в Matlab Compiler). Теоретически. Я его так и не нашел. Если хотите воспользоватеься библиотеками ищите их в интернете. Обычно, это файл mglinstaller.exe. И не забудьте поинтересоваться условиями использования — тут я тоже не в курсе. Далее я предполагаю, что библиотеки у Вас уже есть.
Как пройти в библиотеку?

dll-библиотека — это исполняемый файл в среде Windows, очень похожий на обычный .exe, с той разницей, что запускает его не пользователь, а программа, запущенная пользователем, другой программой, dll библиотекой… Короче, не об этом речь.
Библиотекой такой файл называется потому, что хранит в себе набор функций. Более того, эти функции можно еще и вызывать, потому что в библиотеке хранится т.н. таблица экспорта — сопоставление имен функций и их адресов в библиотеке. А вот чего там не хранится — так это сигнатур (типов параметров) этих функций. Таким образом, какие функции спрятаны в библиотеке, узнать теоретически можно, а вот как эти функции правильно вызвать — даже теоретически определить сложно. Ибо dll Матлаба — это чистый native code, а там, как известно, типов нет в принципе. Типы — это роскошь языка программирования.
Подозрительные типы

Итак, работаем с Delphi. А этот язык программирования, как известно, жестко типизирован. Стало быть нам нужны сигнатуры функций в dll библиотеке, иначе про творение Борланда можно забыть. В официальное документации Матлаба, вообще-то, об этом написано. Причем в приложении к нескольким языкам программирования. А теперь догадайтесь, зачем тогда нужна эта статья? Правильно. Делфи среди них нет. Делфи у них не в моде. Поэтому читаем дальше. Умные люди (не спутайте с автором) сумели таки подобрать Delphi-эквиваленты для типов параметров функций, хранящихся в библиотеках математического ядра матлаба. А вот, собственно, и они:pDouble = ^Double;{указатель на вещественное число}
mxComplexty = (mxREAL, mxCOMPLEX); {вещественный и комплексный тип}
pmxArray = ^mxArray; {указатель на массив вещественных чисел}
mxArray = record {Просто обозначить этот тип}
Учитывая то, что Матлаб писали на великом и ужасном языке С, то наличие указателей в листинге не удивляет. Было бы странным их отсутствие. Последний тип представляет собой запись с неопределенными полями. На что он нам такой сдался? Ни на что. Во-первых, далее будут использоваться исключительно указатели на объекты данного типа — pmxArray. а во-вторых, все манипуляции с этими указателями будут осуществляться разными уже готовыми, а иногда даже немного стандартными функциями. Так что в описании данных типов ограничимся комментариями в листинге.
А теперь, о типах в Матлабе. Там действует принцип: все есть матрица(именно Матлабом вдохновлялись братья Вачовски). В Delphi далеко не все вокруг — матрицы. А в нашем случае, матрицы – это структуры mxArray.
К делу

Итак, пора бы разобрать dll-библиотеки матлаба по косточкам. Задача состоит в следующем: описать на языке Delphi заголовки всех нужных нам функций. Все тот же Delphi позволяет объявлять функции как внешние, с указанием расположения тела функции, так что переписывать последнее не придется. Листинг ниже.function mlfDoubleMatrix(m,n : Integer; const pr,pi : pDouble):pmxArray;cdecl; external 'libmatlb.dll';
function mlfFft(X,n,dim:pmxArray): pmxArray; cdecl; external 'libmatlb.dll';
Обратите внимание на внешний файл в заголовках. Эта библиотека должна быть доступна. Также должен быть доступен и весь набор библиотек, о котором шла речь выше. Библиотеки в данном пакете сильно связанны друг с другом.
Что-то тут математикой и не пахнет! Верно. Ведь это не функции математического ядра Матлаба. Это функции, которые помогут нам общаться с математическими функциями. То есть://...
var
p_mxTime,p_mxX,p_mxFX: pmxArray;
//...
p_mxTime:=mlfDoubleMatrix(1,N,@Time[0],Nil);
p_mxX:=mlfDoubleMatrix(1,N,@X[0],Nil);
p_mxFX:=mlffft(p_mxX,mlfScalar(N),Nil);
//...
Функция mlfDoubleMatrix создает матрицу в формате, принятом в системе Матлаб. Под форматом подразумевается способ расположения матрицы в памяти и прочие системные тонкости. m, n — число строк и столбцов в матрице соответственно. pr — указатель на первый по счету элемент массива, содержащего ВЕЩЕСТВЕННЫЕ части всех элементов матрицы. И тут сделаем небольшой перерыв.
Матлаб хранит матрицы в структурах mxArray. mxArray может быть представлен как обычная запись (record) Delphi. Поля этой записи хранят указатели на самые различные параметры матрицы. Например, каждая матрица может иметь имя. Но это в данном случае не пригодится. Куда более важно, где и как хранятся элементы матрицы? Как известно, по умолчанию в Матлабе все числа комплексные. Не целые, и даже не просто вещественные. А комплексные. Комплексное число при этом всего лишь пара вещественных чисел, рассматриваемых совместно. А массив комплексных чисел? Правильно, пара массивов вещественных.
Теперь о массивах. Массивы вещественных и мнимых частей элементов в mlfDoubleMatrix — это обычные массивы Delphi. Лично я использовал их открытую разновидность, то есть, динамические массивы. Поэтому в примере индексация идет с нуля.
Вернемся к mlfDoubleMatrix. pr — как раз и должен указывать на первый элемент в массиве действительных (Real, поэтому pR) частей элементов нашей комплексной матрицы. А теперь догадайтесь, что такое pi? Это указатель на массив мнимых частей элементов матрицы. Почему в примере pi=nil? Время комплексным не бывает! Действительное число для Матлаба — это комплексное число с нулевой мнимой частью. А раз мнимая часть нулевая, то и выделять под неё массив было бы глупо. Поэтому, функция mlfDoubleMatrix допускает указание вместо pi(или pr) нулевого указателя. Не бойтесь, Access Violation не выскочит. А вот что если и pr и pi равны nil одновременно?? Этот вопрос оставляю на Ваше рассмотрение.(На всякий случай, как это всегда рекомендуется, сделайте резервную копию жесткого диска)
К чему такие сложности? обратите внимание на третью строчку в примере. Как Вы думаете, что означает загадочная аббревиатура fft? Подсказка: mlf — стандартный префикс многих функций в dll — библиотеках Матлаба.
И вот он момент истины. fft — это ни что иное, как Fast Fourie Transformation, что по-русски звучит как Быстрое преобразование Фурье! Это Вам не два числа перемножить! И даже не пару матриц.
Первый параметр X — это массив значений сигнала, n — число точек для преобразования, обычно суть размер X. К моему стыду, что означает третий параметр, я не знаю. Знаю только, что преобразование выполняется и без него. Кстати, формат вызова, то есть набор параметров, совпадает с параметрами в самом Матлабе, в командной строке, то есть.
Обобщая бред, написанный выше, последовательность использования функций dll библиотек математического ядра Матлаба следующая.
1. Описать типы данных, представленные выше, а, возможно, и какие-то свои. Описать заголовок функции из dll библиотеки согласно правилам Delphi. Пометить функцию как cdecl (вызов в стиле языка С) и external 'libName' (имя или полный путь к dll библиотеки). Теперь функцию можно рискнуть вызвать.
2. Упаковать исходные данные в формат mxArray. Для этого, например, можно использовать функцию mlfDoubleMatrix, которая находится все в тех же библиотеках.
3. Вызвать нужную функцию
4. Распаковать результаты
Упс… А как же их распаковать-то? Не падайте в обморок, все проделанное не канет в лета. Предлагаю Вашему вниманию еще пару функций из библиотек Матлаба.function mxGetPr(arr_ptr:pmxArray):pDouble;cdecl; external 'libmx.dll';
function mxGetPi(arr_ptr:pmxArray):pDouble; cdecl; external 'libmx.dll';
Помните mlfDoubleMatrix? pr и pi — указатели на первые элементы массивов вещественных и мнимых частей элементов матрицы соответственно. Там они были известны, ведь массивы формировали мы сами. Думаю, на этот раз Вы догадались, что mxGetPr возвращает как раз указатель на первый элемент массива с вещественными частями элементов матрицы arr_ptr, а mxGetPi — со мнимыми. arr_ptr — матрица в формате mxArray.
Работай мы в С, тут бы и конец делу. К указателю можно прибавлять числа, и вообще, извращаться над ним как угодно. Но мы работаем в Delphi. Поэтому, зная указатель на первый элемент массива, перебрать массив не получится(честно говоря, я даже не пытался. Вполне возможно, что в этом я ошибаюсь, так что не бейте меня, пожалуйста...). Поэтому, привожу еще одну функцию, на этот раз стандартную процедуру Delphi, перекочевавшую в нее еще из TurboPascal.Move(from, where, count)
from — участок памяти, откуда производится копирование данных
where — участок памяти, куда производится копирование данных
count — количество байт для копирования
Обратите внимание, в описании параметров ни разу не использовано слово указатель. Все потому, что как раз указателей эта процедура и не переваривает. Понятней будет на примере.Move(mxGetPr(p_mxFY)^,ReFY[0],N*sizeof(double));
Напомню, оператор ^ означает разыменование, то есть, доступ по указателю на объект к самому объекту. Первые два параметра — это параметры-переменные. Поэтому указатели там лишние. Вот, кстати, и пример использования mxGetPr. С mxGetPi, думаю, разберетесь сами.
Вот, собственно, и все. КАК??! спросите Вы. А список доступных в библиотеках функций? У меня такого списка нет. Но я могу рассказать, как выйти из положения.
Собственно, когда я столкнулся с описанной выше задачей, меня интересовало преобразование Фурье, приведенное в примере. Естественно, Матлаб может дать гораздо больше. Дать то он может, но сможем ли мы взять? Попробуем.
Откройте командную строку (Windows, не Матлаба) и наберите команду «tdump путь_к_библиотеке путь_для_листинга» (без кавычек, естественно). Если у Вас установлена Delphi, данная команда должна быть доступна. Если нет, то ищите решение в справке Delphi. Второй параметр можно и не указывать, но листинг будет достаточно длинный и с консоли читать его не удобно.
Команда tdump выполняет так называемый дамп PE-файла — .exe либо .dll. В случае с dll, который нас, разумеется, и интересует, помимо всего прочего будут выведены имена экспортируемых библиотекой функций. Самое вкусное расположено в файле libmatlb.dll. Эврика! Имена доступных функций имеются, осталось определить сигнатуры.
Самый простой способ — отрезать от имени префикс mlf и поискать оставшееся в справке Матлаба. Чаще всего, списки параметров совпадают. Если в матлабе функция принимает матрицу, то в Delphi надо передвать mxArray. Иногда можно передавать непосредственно числа double.
Еще более простой способ — зайти на официальный сайт и посмотреть справку по использованию библиотек в описанных там языках программирования, лучше в С, а потом поменять все типы на их Delphi-эквиваленты. (еще одно страшное признание — я на их сайте этого никогда не искал. Будьте снисходительны к автору...)

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js