Mathematica и C / C ++: обмен данными - PullRequest
7 голосов
/ 24 июня 2011

Я хотел бы знать, как обмениваться данными между Mathematica и C / C ++ с каналами. В учебнике Mathematica говорится, что «когда вы открываете файл или канал, Mathematica создает« объект потока », который указывает открытый поток, связанный с файлом или каналом».

Я знаю, как создавать файлы в C и Mathematica , и я могу заставить каждую программу читать и записывать их. То, что я до сих пор не знаю, как это сделать, это как отправить вывод из C через канал в другую программу, а тем более как это сделать из Mathematica.

Вот функция, в которой Mathematica записывает матрицу в двоичный файл, а также читает файл, записанный в этом формате.

writeDoubleMatrix[obj_, fileName_] := Module[{file},
  file = OpenWrite[fileName, BinaryFormat -> True];
  BinaryWrite[file, Length@obj, "Integer32"];
  BinaryWrite[file, Length@obj[[1]], "Integer32"];
  BinaryWrite[file, Flatten[obj], "Real64"];
  Close[file]
 ]
readDoubleMatrix[fileName_] := Module[{file, obj, m, n},
  file = OpenRead[fileName, BinaryFormat -> True];
  m = BinaryRead[file, "Integer32"];
  n = BinaryRead[file, "Integer32"];
  obj = BinaryReadList[file, "Real64", m*n];
  Close[file];
  Partition[obj, n]
 ]

Первая функция запишет в файл 2 целых числа (размер матрицы) и данные матрицы. Я не делаю никакой проверки ошибок здесь, и поэтому я предполагаю, что данные быть написано конкретно в виде {{r11, r12, ..., r1n}, ...., {rm1, rm2, ..., rmn}}. Вторая функция сможет читать двоичный файл и возвращать матрицу.

Далее идет моя программа на Си. Эта программа будет считывать данные, хранящиеся в файле MathematicaData.bin, умножать эту матрицу на 2 и записывать данные в другой файл.

// genData.c
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char** argv){
    int m, n, i;
    double* matrix;
    FILE* fin;
    FILE* fout;

    // Reading input file
    fin = fopen(argv[1], "rb");
    fread(&m, sizeof(int), 1, fin);
    fread(&n, sizeof(int), 1, fin);
    matrix = (double*)malloc(m*n*sizeof(double));
    fread(matrix, sizeof(double), m*n, fin);
    fclose(fin);

    //Modifying data
    for (i = 0; i < m*n; ++i) matrix[i] = 2*matrix[i];

    // Writing output file
    fout = fopen(argv[2], "wb");
    fwrite(&m, sizeof(int), 1, fout);
    fwrite(&n, sizeof(int), 1, fout);
    fwrite(matrix, sizeof(double), m*n, fout);
    fclose(fout);

    // De-allocate memory used for matrix.
    free(matrix);
    return 0;
 }

Эта программа не имеет проверки ошибок. Вы должны быть осторожны, как вы используете его, иначе программа может не обнаружить файлы или даже выделить объем памяти, который вы хотите. В любом случае мы можем скомпилировать программу с вашим компилятором по вашему выбору.

gcc -o genData genData.c

И теперь мы можем попытаться использовать эти функции для связи между двумя языками из Mathematica.

matrix = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
writeDoubleMatrix[matrix, "MathematicaData.bin"];
Run["./genData MathematicaData.bin CData.bin"];
readDoubleMatrix["CData.bin"]

Если все прошло хорошо, вы должны получить

{{2., 4., 6., 8.}, {10., 12., 14., 16.}, {18., 20., 22., 24.}}

Да, это очень трудоемкий способ умножения матрицы на 2, но это всего лишь простой пример, показывающий, как обмениваться данными из Mathematica в C и из C в Mathematica. Что мне не нравится, так это то, что все сначала сохраняется в файле, а затем читается в другой программе. Может кто-нибудь показать мне, как обмениваться данными без записи файлов. У меня есть ощущение, что мне нужны каналы, но я не знаю, как их читать или писать на любом языке. Было бы полезно, если бы вы могли изменить эту программу, чтобы адаптировать ее к каналам.


UPDATE:

Я узнал, как сделать программу на C "pipeable".

//genDataPipe.c
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char** argv){
    int m, n, i;
    double* matrix;

    // Reading input file
    fread(&m, sizeof(int), 1, stdin);
    fread(&n, sizeof(int), 1, stdin);
    matrix = (double*)malloc(m*n*sizeof(double));
    fread(matrix, sizeof(double), m*n, stdin);

    //Modifying data
    for (i = 0; i < m*n; ++i) matrix[i] = 2*matrix[i];

    // Writing output file
    fwrite(&m, sizeof(int), 1, stdout);
    fwrite(&n, sizeof(int), 1, stdout);
    fwrite(matrix, sizeof(double), m*n, stdout);

    // Deallocate memory used for matrix.
    free(matrix);
    return 0;
}

Это означает, что мы должны использовать программу следующим образом:

./genDataPipe < fileIn.bin > fileOut.bin

Я искал в документации на стороне Mathematica , но все, что я понял, - это открыть файл с помощью внешней команды. Возьмите OpenWrite, например:

В компьютерных системах, которые поддерживают каналы, OpenWrite ["! Command"] запускает внешнюю программу, указанную командой, и открывает канал, чтобы отправить в него входные данные.

Это означает, что я могу передать свой двоичный вход непосредственно в программу c. Проблема в том, что я не могу найти способ перенаправить вывод программы. Я пришел к выводу: записать данные в файл и создать оболочку для запуска внешней команды и чтения содержимого выходных данных внешней команды. Здесь мы предполагаем существование writeDoubleMatrix от ранее.

getDataPipe[fileName_] := Module[{file, obj, m, n},
  file = OpenRead["!./genDataPipe < " <> fileName, 
  BinaryFormat -> True];
  m = BinaryRead[file, "Integer32"];
  n = BinaryRead[file, "Integer32"];
  obj = BinaryReadList[file, "Real64", m*n];
  Close[file];
  Partition[obj, n]
 ]
matrix = {{1, 2, 3}, {4, 5, 6}};
writeDoubleMatrix[matrix, "MData.bin"];
output = getDataPipe["MData.bin"]

В результате получается следующее содержимое {{2., 4., 6.}, {8., 10., 12.}}.

Единственная цель, которая остается сейчас, - выяснить, как устранить необходимость writeDoubleMatrix и передать данные напрямую, без необходимости записи файла.

Ответы [ 4 ]

5 голосов
/ 24 июня 2011

Первым шагом будет создание именованного канала (как упомянуто в комментарии Артефакто выше).Это называется fifo, First In First Out.Канал делает вывод из одного файла во вход другого.Я хотел бы отметить, что эти методы предназначены только для Linux, прежде чем я начну.

В основном, каналы работают так:

mkfifo mypipe или в C: system ("mkfifo mypipe");

Следующий шаг - записать вывод в канал, потому что все в Linux является файлом, вы можете просто использовать для этого стандартные операции ввода-вывода или даже перенаправить стандартный ввод в канал.У вас уже есть код для этого как в Mathematica, так и в C. Таким образом, после записи вывода файла в ваш канал, версия Mathematica может затем прочитать ввод из канала, умножить его и отобразить в stdout или где угодно.У вас действительно не должно быть проблем с этим методом, так как канал очищается после чтения и может быть легко удален после того, как вы закончите.Если вы хотите удалить трубу впоследствии, просто запустите system ("rm myfifo");.

Если вы действительно не хотите никаких дополнительных файлов, даже если это не так уж плохо, попробуйте создать другой файл, которыйна самом деле выводит в стандартный вывод.Затем сделайте тот, который давайте Matematica читать со стандартного ввода.Теперь, чтобы передать через:

./cprogram | ./mprogram

Это означает, что выходные данные вашей C-программы должны быть входными данными вашей программы Mathematica.Насколько мне известно, это все еще создает канал, но он будет автоматически удален системой, когда это будет сделано, и средний конечный пользователь, вероятно, не увидит его.

4 голосов
/ 24 июня 2011

Ближайшая функция, которую Mathematica выполняет для чтения и записи одновременно из внешней программы: RunThrough. Однако, согласно документации, он работает не так, как вы хотите: передача информации через операции Write и Read. Вместо этого он записывает второй параметр во временный файл, выполняет команду, указанную в первом параметре, передавая ему временный файл, и записывает выходные данные команды. Это явно не то, что вы просите, но, похоже, это самая близкая из встроенных команд. Лично я бы хотел использовать MathLink, но поскольку вы, похоже, не хотите этого делать, Ответ Джонатона находится на правильном пути.

Чтобы немного расширить его ответ, документация подразумевает, что Mathematica не может одновременно читать и записывать один и тот же файл / канал. Это наводит меня на мысль о решении проблемы. Во-первых, вам нужно 2 канала: один для записи и один для чтения, и это можно сделать с помощью Run, например,

Run["mkfifo in"]
Run["mkfifo out"].

Во-вторых, откройте трубы для ввода / вывода

instrm = OpenRead["in"]
outstrn = OpenWrite["out"].

Наконец, запустите внешнюю программу, передав ей имена каналов, если она настроена на обработку параметров командной строки,

Run["prog in out"],

или с помощью перенаправления

Run["prog <in >out"].
3 голосов
/ 24 июня 2011

Это немного неловко, но вы можете использовать JLink:

Needs["JLink`"];
InstallJava[];
LoadJavaClass["java.lang.Runtime"];
runtime = java`lang`Runtime`getRuntime[];
process = runtime@exec["my program"];
input = process@getInputStream[];
output = process@getOutputStream[];

Теперь вы можете написать в канал, который является stdin на другом конце:

output@write[ToCharacterCode["hi!"]];

И читать из стандартного вывода запущенной программы:

nextByte = input@read[];
3 голосов
/ 24 июня 2011

MathLink может быть лучшим выбором для вас.Mathematica и программа на C будут отдельными процессами и будут взаимодействовать через каналы или разделяемую память.Вам нужно будет посмотреть примеры MathLink в вашей установке Mathematica.Существует инструмент для генерации связующего кода из файла шаблона с именем mprep, и вам нужно будет связать его с библиотекой MathLink, но после этого очень просто вызвать функцию C с массивом данных.

MLPutReal64Array - отправить массив данных в Mathematica

MLGetReal64Array - прочитать массив из Mathematica

Я бы взял один из примеров и начал оттуда работать.Вы, вероятно, захотите Manual ReturnType и Real64List для ArgumentType.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...