MATLAB

Пакет MATLAB.jl предоставляет интерфейс для работы с MATLAB® из Julia с использованием API C для MATLAB. Другими словами, этот пакет позволяет пользователям вызывать функции MATLAB в Julia, тем самым упрощая взаимодействие с MATLAB из языка Julia.

Обзор

Этот пакет предназначен для реализации двух аспектов:

  • Создание mxArray (структур данных, используемых MATLAB для представления массивов и других видов данных) и работа с ними

  • Взаимодействие с сеансами подсистемы MATLAB

Внимание!

  • Массивы строк MATLAB не поддерживаются, поэтому будет выведено исключение, возникающее при ошибке. Это также применимо, если они вложены в структуру MATLAB. Это ограничение API C для MATLAB. Для упрощения преобразования в совместимый формат для использования с MATLAB.jl можно обратиться к функции MATLAB convertContainedStringsToChars.

  • Кроме того, при использовании библиотеки MATLAB.jl в Julia не поддерживается потоковая обработка.

Использование

Класс MxArray

Экземпляр MxArray инкапсулирует переменную MATLAB. Этот пакет предоставляет ряд функций для работы с такими экземплярами.

Создание переменных MATLAB в Julia

Для создания переменных MATLAB (типа MxArray) можно использовать функцию mxarray, как показано ниже.

mxarray(Float64, n)   # создает нулевой массив MATLAB размером n на 1 с типом значения double
mxarray(Int32, m, n)  # создает нулевой массив MATLAB размером m на n с типом значения int32
mxarray(Bool, m, n)   # создает логический массив MATLAB размером m на n

mxarray(Float64, (n1, n2, n3))  # создает массив MATLAB размером n1 на n2 на n3

mxcellarray(m, n)        # создает массив ячеек MATLAB
mxstruct("a", "b", "c")  # создает структуру MATLAB с заданными полями

Вы можете преобразовать переменную Julia в переменную MATLAB.

a = rand(m, n)

x = mxarray(a)     # преобразует a в массив MATLAB
x = mxarray(1.2)   # преобразует скаляр 1.2 в переменную MATLAB

a = sprand(m, n, 0.1)
x = mxarray(a)     # преобразует разреженную матрицу в разреженную матрицу MATLAB

x = mxarray("abc") # преобразует строку в массив символов MATLAB

x = mxarray(["a", 1, 2.3])  # преобразует массив Julia в массив ячеек MATLAB

x = mxarray(Dict("a"=>1, "b"=>"string", "c"=>[1,2,3])) # преобразует словарь Julia в структуру MATLAB

Функция mxarray может также преобразовывать составной тип в структуру Julia:

struct S
    x::Float64
    y::Vector{Int32}
    z::Bool
end

s = S(1.2, Int32[1, 2], false)

x = mxarray(s)   # создает структуру MATLAB с тремя полями: x, y, z
xc = mxarray([s, s])  # создает массив ячеек MATLAB, каждая ячейка которого является структурой.
xs = mxstructarray([s, s])  # создает массив структур MATLAB

Примечание. В целях безопасности при взаимодействии переменных MATLAB и Julia используется глубокая копия.

После завершения использования переменной MATLAB можно вызвать delete для освобождения памяти. Но делать это необязательно, так как переменная будет удалена сборщиком мусора.

delete(x)

Примечание. Если вы поместите переменную MATLAB x в сеанс подсистемы MATLAB, подсистема MATLAB возьмет на себя управление ее жизненным циклом, и вам не нужно будет удалять переменную явным образом.

Доступ к переменным MATLAB

Доступ к атрибутам и данным переменной MATLAB можно получить с помощью функций, предоставляемых этим пакетом.

# предположим, что x имеет тип MxArray
nrows(x)    # возвращает количество строк в x
ncols(x)    # возвращает количество столбцов в x
nelems(x)   # возвращает количество элементов в x
ndims(x)    # возвращает количество измерений в x
size(x)     # возвращает размер x в виде кортежа
size(x, d)  # возвращает размер x вместе с конкретным измерением

eltype(x)   # возвращает тип элемента x (в Julia Type)
elsize(x)   # возвращает количество байт для каждого элемента

data_ptr(x)   # возвращает указатель на данные (в формате Ptr{T}), где T — это eltype(x)

# предположим, что s является структурой MATLAB
mxnfields(s)	# возвращает количество полей в структуре s

Вы можете также выполнять тесты по переменной MATLAB.

is_double(x)   # определяет, является ли x двойным массивом
is_sparse(x)   # определяет, является ли x разреженным
is_complex(x)  # определяет, является ли x сложным
is_cell(x)     # определяет, является ли x массивом ячеек
is_struct(x)   # определяет, является ли x структурой
is_empty(x)    # определяет, является ли x пустым

...            # существует еще множество других вариантов

Преобразование переменных MATLAB в Julia

a = jarray(x)   # преобразует x в массив Julia
a = jvector(x)  # преобразует x в вектор Julia (одномерный массив), если x является вектором
a = jscalar(x)  # преобразует x в скаляр Julia
a = jmatrix(x)  # преобразует x в матрицу Julia
a = jstring(x)  # преобразует x в строку Julia
a = jdict(x)    # преобразует структуру MATLAB в словарь Julia (с использованием имен полей в качестве ключей)

a = jvalue(x)  # преобразует x в значение Julia заданным по умолчанию способом

Чтение и запись MAT-файлов

Этот пакет предоставляет ряд функций для работы с MAT-файлами пакета MATLAB:

mf = MatFile(filename, mode)    # открывает MAT-файл, используя определенный режим, и возвращает дескриптор
mf = MatFile(filename)          # открывает MAT-файл для чтения, эквивалентно MatFile(filename, "r")
close(mf)                       # закрывает MAT-файл.

get_mvariable(mf, name)   # получает переменную и возвращает mxArray
get_variable(mf, name)    # получает переменную, но преобразует ее в значение Julia с помощью `jvalue`

put_variable(mf, name, v)   # помещает переменную v в MAT-файл
                            # v может быть либо экземпляром MxArray, либо обычной переменной
                            # Если v не является MxArray, он будет преобразован с помощью `mxarray`

put_variables(mf; name1=v1, name2=v2, ...)  # помещает несколько переменных, используя именованные аргументы

variable_names(mf)   # получает вектор всех имен переменных в MAT-файле

Существуют также удобные функции, позволяющие получить или поместить все переменные за один вызов:

read_matfile(filename)    # возвращает словарь, сопоставляющий имя каждой переменной
                          # с экземпляром MxArray

write_matfile(filename; name1=v1, name2=v2, ...)  # записывает все переменные, заданные в
                                                  # списке именованных аргументов, в MAT-файл

И read_matfile, и write_matfile перед возвратом закрывают дескриптор MAT-файла.

Примеры:

struct S
    x::Float64
    y::Bool
    z::Vector{Float64}
end

write_matfile("test.mat";
    a = Int32[1 2 3; 4 5 6],
    b = [1.2, 3.4, 5.6, 7.8],
    c = [[0.0, 1.0], [1.0, 2.0], [1.0, 2.0, 3.0]],
    d = Dict("name"=>"MATLAB", "score"=>100.0),
    s = "abcde",
    ss = [S(1.0, true, [1., 2.]), S(2.0, false, [3., 4.])] )

В этом примере будет создан MAT-файл с именем test.mat, содержащий шесть переменных MATLAB:

  • a: массив типа int32 размером 2 на 3

  • b: массив типа double размером 4 на 1

  • c: массив ячеек размером 3 на 1, где каждая ячейка содержит двойной вектор

  • d: структура с двумя полями: имя и оценка

  • s: строка (т. е. массив символов)

  • ss: массив структур с двумя элементами и тремя полями — x, y и z

Использование механизма MATLAB

Базовое использование

Для вычисления выражений в MATLAB можно открыть сеанс подсистемы MATLAB и работать с ним. Вызвать MATLAB из Julia можно тремя способами:

  • Пользовательский строковый литерал mat"" позволяет писать синтаксис MATLAB внутри Julia и использовать переменные Julia непосредственно из MATLAB с помощью интерполяции.

  • eval_string вычисляет строку, содержащую выражения MATLAB (обычно используется с вспомогательными макросами @mget и @mput).

  • Функция mxcall вызывает заданную функцию MATLAB и возвращает результат.

Как правило, предпочтительным способом взаимодействия с подсистемой MATLAB является использование пользовательского строкового литерала mat"".

Примечание. Может существовать несколько (разумных) способов преобразования переменной MATLAB в массив Julia. Например, MATLAB представляет скаляр с помощью матрицы размером 1 на 1. Преобразовать такую матрицу обратно в Julia можно двумя способами: (1) преобразовать в скалярное число или (2) преобразовать в матрицу размером 1 на 1.

Пользовательский строковой литерал mat""

Текст внутри пользовательского строкового литерала mat"" представлен в синтаксисе MATLAB. Переменные из Julia могут быть интерполированы в код MATLAB путем добавления символа доллара в качестве префикса так, как вы бы интерполировали их в обычную строку.

using MATLAB

x = range(-10.0, stop=10.0, length=500)
mat"plot($x, sin($x))"  # вычисляет функцию MATLAB

y = range(2.0, stop=3.0, length=500)
mat"""
    $u = $x + $y
	$v = $x - $y
"""
@show u v               # u и v доступны из Julia

Как и в случае с обычными строковыми литералами, можно интерполировать и целые выражения Julia, например mat"$(x[1]) = ) + ](binomial(5, 2))".

eval_string

Для выполнения кода MATLAB можно также использовать функцию eval_string следующим образом:

eval_string("a = sum([1,2,3])")

Функция eval_string также принимает необязательный аргумент, указывающий, в каком сеансе MATLAB следует выполнять код, например

julia> s = MSession();
julia> eval_string(s, "a = sum([1,2,3])")
a =
     6
mxcall

Можно также вызвать функцию MATLAB напрямую для переменных Julia, используя mxcall:

x = -10.0:0.1:10.0
y = -10.0:0.1:10.0
xx, yy = mxcall(:meshgrid, 2, x, y)

Примечание. Поскольку поведение функций MATLAB зависит от количества выводов, в mxcall в качестве второго аргумента необходимо указать количество выходных аргументов.

mxcall помещает входные аргументы в рабочую область MATLAB (используя скорректированные имена), оценивает вызов функции в MATLAB и извлекает переменную из сеанса MATLAB. Эта функция используется в основном для удобства. Однако следует иметь в виду, что при этом могут возникнуть значительные издержки, связанные с обменом данными между MATLAB и Julia.

@mget и @mput

Макрос @mget можно использовать для извлечения значения переменной MATLAB в Julia.

julia> mat"a = 6"
julia> @mget a
6.0

Макрос @mput можно использовать для преобразования переменной Julia в MATLAB

julia> x = [1,2,3]
julia> @mput x
julia> eval_string("y = sum(x)")
julia> @mget y
6.0
julia> @show y
a = 63.0

Вызов пользовательской функции MATLAB

Если функция MATLAB отсутствует в текущем каталоге, перед вызовом в Julia необходимо сначала добавить ее в путь к MATLAB:

mat"addpath('/path/to/folder')"
val = mat"myfunction($arg1, $arg2)"

Например, если по адресу /path/to/folder расположен файл MATLAB с содержимым:

function [r,u] = test(x, y)
	r = x + y;
	u = x - y;
end

Эту функцию в Julia можно вызвать следующим образом:

using MATLAB

x = range(-10.0, stop=10.0, length=500)
y = range(2.0, stop=3.0, length=500)

mat"addpath('/path/to/folder')"

r, u = mxcall(:test,2,x,y)

Просмотр сеанса MATLAB (только Windows)

Чтобы открыть интерактивное окно сеанса работы с MATLAB, используйте команду show_msession(), а чтобы скрыть окно — команду hide_msession(). Предупреждение. Закрытие этого окна вручную приведет к ошибке или аварийному завершению работы. Команду hide_msession() рекомендуется использовать только для скрытия интерактивного окна.

Обратите внимание, что это работает только в Windows.

# по умолчанию
show_msession() # открывает интерактивное окно сеанса MATLAB по умолчанию
get_msession_visiblity() # получает состояние видимости сеанса
hide_msession() # скрывает интерактивное окно сеанса MATLAB по умолчанию

# аналогично
s = MSession()
show_msession(s)
get_msession_visiblity(a)
hide_msession(s)

Расширенное использование механизмов MATLAB

Этот пакет предоставляет пользователям ряд функций для управления взаимодействием с сеансами MATLAB.

Вот пример:

s1 = MSession()    # создает сеанс MATLAB
s2 = MSession(0)   # создает сеанс MATLAB без записи вывода

x = rand(3, 4)
put_variable(s1, :x, x)  # помещает x в сеанс s1

y = rand(2, 3)
put_variable(s2, :y, y)  # помещает y в сеанс s2

eval_string(s1, "r = sin(x)")  # вычисляет sin(x) в сеансе s1
eval_string(s2, "r = sin(y)")  # вычисляет sin(y) в сеансе s2

r1_mx = get_mvariable(s1, :r)  # получает r из s1
r2_mx = get_mvariable(s2, :r)  # получает r из s2

r1 = jarray(r1_mx)
r2 = jarray(r2_mx)

# ... выполняет прочие операции с r1 и r2

close(s1)  # закрывает сеанс s1
close(s2)  # закрывает сеанс s2