Как сделать внешние функции Mathematica прерываемыми?
У меня был ранее вопрос об интеграции Mathematica с функциями, написанными на C++.
Это дополнительный вопрос:
Если вычисление занимает слишком много времени, я бы хотел прервать его, используя Evaluation> Abort Evaluation. Какие технологии, предложенные в ответах, позволяют использовать прерываемую функцию расширения на основе С? Как может быть реализована "прерываемость" на стороне C?
Мне нужно сделать свою функцию прерываемой таким образом, чтобы это не повредило ни ее, ни ядро Mathematica (т.е. должна быть возможность повторного вызова функции из Mathematica после того, как она была прервана).
3 ответа
Для функций на основе MathLink вам придется сделать две вещи (в Windows): использовать MLAbort
проверить на прерывания и позвонить MLCallYieldFunction
, чтобы дать процессор временно. Оба описаны в руководстве по MathLink Тоддом Гэйли, которое можно найти здесь.
Используя биты из моего предыдущего ответа, вот пример кода для вычисления простых чисел (неэффективно, но это то, что нам нужно здесь для иллюстрации):
code =
"
#include <stdlib.h>
extern void primes(int n);
static void yield(){
MLCallYieldFunction(
MLYieldFunction(stdlink),
stdlink,
(MLYieldParameters)0 );
}
static void abort(){
MLPutFunction(stdlink,\" Abort \",0);
}
void primes(int n){
int i = 0, j=0,prime = 1, *d = (int *)malloc(n*sizeof(int)),ctr = 0;
if(!d) {
abort();
return;
}
for(i=2;!MLAbort && i<=n;i++){
j=2;
prime = 1;
while (!MLAbort && j*j <=i){
if(i % j == 0){
prime = 0;
break;
}
j++;
}
if(prime) d[ctr++] = i;
yield();
}
if(MLAbort){
abort();
goto R1;
}
MLPutFunction(stdlink,\"List\",ctr);
for(i=0; !MLAbort && i < ctr; i++ ){
MLPutInteger(stdlink,d[i]);
yield();
}
if(MLAbort) abort();
R1: free(d);
}
";
и шаблон:
template =
"
void primes P((int ));
:Begin:
:Function: primes
:Pattern: primes[n_Integer]
:Arguments: { n }
:ArgumentTypes: { Integer }
:ReturnType: Manual
:End:
";
Вот код для создания программы (взят из предыдущего ответа, немного изменен):
Needs["CCompilerDriver`"];
fullCCode = makeMLinkCodeF[code];
projectDir = "C:\\Temp\\MLProject1";
If[! FileExistsQ[projectDir], CreateDirectory[projectDir]]
pname = "primes";
files = MapThread[
Export[FileNameJoin[{projectDir, pname <> #2}], #1,
"String"] &, {{fullCCode, template}, {".c", ".tm"}}];
Теперь здесь мы создаем это:
In[461]:= exe=CreateExecutable[files,pname];
Install[exe]
Out[462]= LinkObject["C:\Users\Archie\AppData\Roaming\Mathematica\SystemFiles\LibraryResources\
Windows-x86-64\primes.exe",161,10]
и использовать это:
In[464]:= primes[20]
Out[464]= {2,3,5,7,11,13,17,19}
In[465]:= primes[10000000]
Out[465]= $Aborted
В последнем случае я использовал Alt+"." прервать вычисление. Обратите внимание, что это не будет работать правильно, если вы не включите вызов yield
,
Общая идеология такова, что вы должны проверить MLAbort
и позвонить MLCallYieldFunction
для каждого дорогостоящего вычисления, такого как большие циклы и т. д. Возможно, делать это для внутренних циклов, как я делал выше, является излишним. Одна вещь, которую вы могли бы попытаться сделать, это отделить исходный код с помощью препроцессора C (макросы).
Если вы используете LibraryLink для связи внешнего кода C с ядром Mathematica, вы можете использовать функцию обратного вызова библиотеки AbortQ, чтобы проверить, выполняется ли прерывание.
Даже не пытаясь это сделать, похоже, что функциональность Expression Packet могла бы работать таким образом - если ваш код C возвращается назад и периодически запрашивает у mathematica дополнительную работу, то, надеюсь, прерывание выполнения на стороне mathematica скажет коду C, что больше нет работы