Как сделать внешние функции Mathematica прерываемыми? - PullRequest
7 голосов
/ 26 ноября 2011

У меня был ранее вопрос об интеграции Mathematica с функциями, написанными на C ++.

Это дополнительный вопрос:

Если вычисление занимает слишком много времени, яхотелось бы иметь возможность прервать его, используя Оценка> Оценка прерывания .Какие из технологий, предложенных в ответах, позволяют использовать прерываемую функцию расширения на основе С?Как реализовать «прерываемость» на стороне C?

Мне нужно сделать свою функцию прерываемой таким образом, чтобы это не повредило ни ее, ни ядро ​​Mathematica (т. Е. Должна быть возможность повторного вызова функции изMathematica после того, как он был прерван)

Ответы [ 3 ]

5 голосов
/ 27 ноября 2011

Для функций, основанных на 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 (макросы).

3 голосов
/ 27 ноября 2011

Если вы используете LibraryLink для связи внешнего кода C с ядром Mathematica, вы можете использовать функцию обратного вызова библиотеки AbortQ , чтобы проверить, выполняется ли прерывание.

3 голосов
/ 26 ноября 2011

Даже не пытаясь это сделать, похоже, что функциональность Expression Packet может работать таким образом - если ваш код на C возвращается и запрашивает у mathematica дополнительную работу периодически, а затем, возможно, прерывает выполнение насторона mathematica сообщит коду C, что больше не нужно делать.

...