如何使外部 Mathematica 函数可中断?

发布于 2024-12-18 04:34:32 字数 302 浏览 2 评论 0原文

我之前有一个关于将 Mathematica 与 C++ 编写的函数集成的问题。

这是一个后续问题:

如果计算需要太长了,我希望能够使用Evaluation > 中止它中止评估。答案中建议的哪些技术可以实现可中断的基于 C 的扩展功能? C端如何实现“可中断”?

我需要使我的函数可中断,既不会损坏它,也不会损坏 Mathematica 内核(即应该可以在中断后从 Mathematica 再次调用该函数)

I had an earlier question about integrating Mathematica with functions written in C++.

This is a follow-up question:

If the computation takes too long I'd like to be able to abort it using Evaluation > Abort Evaluation. Which of the technologies suggested in the answers make it possible to have an interruptible C-based extension function? How can "interruptibility" be implemented on the C side?

I need to make my function interruptible in a way which will corrupt neither it, nor the Mathematica kernel (i.e. it should be possible to call the function again from Mathematica after it has been interrupted)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

属性 2024-12-25 04:34:32

对于基于 MathLink 的函数,您必须执行两件事(在 Windows 上):使用 MLAbort 检查中止,并调用 MLCallYieldFunction 暂时让出处理器。 Todd Gayley 早先在 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 预处理器(宏)分解样板代码。

For MathLink - based functions, you will have to do two things (On Windows): use MLAbort to check for aborts, and call MLCallYieldFunction, to yield the processor temporarily. Both are described in the MathLink tutorial by Todd Gayley from way back, available here.

Using the bits from my previous answer, here is an example code to compute the prime numbers (in an inefficient manner, but this is what we need here for an illustration):

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);
 }
 ";

and the template:

template = 
"
void primes P((int ));

:Begin:
:Function:       primes
:Pattern:        primes[n_Integer]
:Arguments:      { n }
:ArgumentTypes:  { Integer }
:ReturnType:     Manual
:End:
";

Here is the code to create the program (taken from the previous answer, slightly modified):

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"}}];

Now, here we create it:

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]

and use it:

In[464]:= primes[20]
Out[464]= {2,3,5,7,11,13,17,19}

In[465]:= primes[10000000]
Out[465]= $Aborted

In the latter case, I used Alt+"." to abort the computation. Note that this won't work correctly if you do not include a call to yield.

The general ideology is that you have to check for MLAbort and call MLCallYieldFunction for every expensive computation, such as large loops etc. Perhaps, doing that for inner loops like I did above is an overkill though. One thing you could try doing is to factor the boilerplate code away by using the C preprocessor (macros).

孤城病女 2024-12-25 04:34:32

在没有尝试过的情况下,看起来 Expression Packet 功能可能会起作用以这种方式 - 如果您的 C 代码返回并要求 mathematica 定期执行更多工作,那么希望在 mathematica 端中止执行将告诉 C 代码没有更多工作要做。

Without ever having tried it, it looks like the Expression Packet functionality might work in this way - if your C code goes back and asks mathematica for some more work to do periodically, then hopefully aborting execution on the mathematica side will tell the C code that there is no more work to do.

梦冥 2024-12-25 04:34:32

如果您使用 LibraryLink 将外部 C 代码链接到 Mathematica 内核,您可以使用库回调函数 AbortQ 检查是否正在进行中止。

If you are using LibraryLink to link external C code to the Mathematica kernel, you can use the Library callback function AbortQ to check if an abort is in progress.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文