Как сделать внешние функции Mathematica прерываемыми?

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

Это следующий вопрос:

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

I необходимо сделать мою функцию прерываемой таким образом, чтобы не повредить ни ее, ни ядро 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 для каждого дорогого вычисления, как большие петли etc. Возможно, делать это для внутренних петель, как я сделал выше, - это перебор. Одна вещь, которую вы можете попробовать сделать, - это разложить код шаблона на множители, используя препроцессор C (макросы).


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


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