在大型 Mathematica 项目中设置诊断错误消息

发布于 2024-10-01 20:20:24 字数 526 浏览 1 评论 0原文

每当我创建一个大型 Mathematica 项目时,我都会遇到这个问题: 防止 Mathematica 中出现雪崩运行时错误,即 Mathematica 的错误消息是不透明的、过时的和大量的。

然后的想法是禁用 Mathematica 自己的所有错误消息,并在每个函数和模块中实现您自己的类型检查和错误消息。然而,我还没有找到一种简单而有效的方法来做到这一点,并最终得到,例如,某些函数生成错误 20 函数深度调用,然后得到一整串错误消息一直返回到主例程。

您将如何为此建立一种简单的机制,仅在遇到错误的函数上生成一条错误消息以及函数调用链的简单列表?

编辑:因为它已经出现在几个答案中;我特别寻找关于它产生的输出的轻量级的东西(否则我只能坚持使用 Mathematica 的错误消息)并且显然在计算开销上也是轻量级的。因此,虽然 Stack 和 Trace 的开销肯定很小,但它们在复杂项目中的输出解析起来并不快,需要做一些工作来简化它。

Whenever I create a large Mathematica project I run into this problem: Preventing avalanche of runtime errors in Mathematica, i.e., Mathematica's error message are opaque, archaic, and legion.

The idea then is to disable all of Mathematica's own error messages and implement type checking and error messages of your own in every Function and Module. However I have not found a simple and efficient way of doing this and end up with, e.g., some function generating an error 20 function calls deep and then get a whole cascade of error messages all the way back up to the main routine.

How would you set up a simple mechanism for this that only generates one error message at the function that experiences the error and a simple list of the chain of function calls?

EDIT: Since it has come up in a couple of answers; I am specifically looking for something lightweight regarding the output it produces (otherwise I could just stick with Mathematica's error messages) and obviously also lightweight in computational overhead. So while Stack and Trace are definitely light on the overhead, their output in complex projects is not quick to parse and some work needs to be done simplifying it.

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

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

发布评论

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

评论(5

坏尐絯℡ 2024-10-08 20:20:24

YAsI - 又一个(愚蠢的?)想法......

重新阅读你的问题......


然后的想法是禁用 Mathematica 自己的所有错误消息,并在每个函数和模块中实现您自己的类型检查和错误消息。
发现这个

$MessagePrePrint = ( #; Print[Stack[_][[;; -5]]]; Abort[]) &  

v[x_, y_] := w[x, y];
w[x_, y_] := x/y;

StackComplete@v[1, 0];

During evaluation of In[267]:= {StackComplete[v[1,0]];,
          StackComplete[v[1,0]], v[1,0], w[1,0], 1/0, 1/0, Message[Power::infy,1/0]}

Out[267]= $Aborted

结论...在第一条消息处中止并留下“合理的”堆栈跟踪。 “合理”意味着“应该改进”。

但它是完全非侵入性的!

YAsI - Yet Another (silly?) Idea ...

Re-reading your question ...


The idea then is to disable all of Mathematica's own error messages and implement type checking and error messages of your own in every Function and Module.

Found this:

$MessagePrePrint = ( #; Print[Stack[_][[;; -5]]]; Abort[]) &  

v[x_, y_] := w[x, y];
w[x_, y_] := x/y;

StackComplete@v[1, 0];

During evaluation of In[267]:= {StackComplete[v[1,0]];,
          StackComplete[v[1,0]], v[1,0], w[1,0], 1/0, 1/0, Message[Power::infy,1/0]}

Out[267]= $Aborted

conclusion ... Aborts at first message and leaves a "reasonable" stack trace. "Reasonable" means "Should be improved".

But it is completely non-intrusive!

花落人断肠 2024-10-08 20:20:24

让事情在这里滚动是我一直在考虑的一个想法;创建伪堆栈。

首先创建一个全局变量 theStack={},然后在每个 FunctionModule 中以 AppendTo[theStack,"thisFuncName"] 开头 并以 theStack=Most@theStack 结尾。假设函数调用的深度适中(大约几十),这不会增加任何显着的开销。

然后实现您自己的打字/错误检查,并对错误使用 Print@theStack;Abort[];

此方法的改进可能包括:

  1. 找出一种动态获取“thisFuncionName”的方法,以便可以将 AppendTo[] 变成所有 FunctionsFunctions 的相同函数调用。代码>模块。
  2. 使用 Message[] 而不是 Print[]
  3. 将其他重要变量/状态信息推送到 theStack 上。

To get the ball rolling here is one idea that I've been toying with; the creation of a pseudo stack.

First make a global variable theStack={} and then in every Function or Module start with AppendTo[theStack,"thisFuncName"] and end with theStack=Most@theStack. Assuming moderate (~a few tens) depth of function calls, this should not add any significant overhead.

Then implement your own typing/error checking and use Print@theStack;Abort[]; on errors.

Refinements of this method could include:

  1. Figuring out a way to dynamically get "thisFuncionName" so that the AppendTo[] can be made into an identical function call for all Functions and Module.
  2. Using Message[] Instead of Print[].
  3. Pushing other important variables / stateful information on theStack.
演出会有结束 2024-10-08 20:20:24

实现 @Timo 的想法 (theStack) 的一种尝试

不完整,也许有缺陷,但只是继续思考它:

Clear["Global`*"];
funcDef = t_[args___]  \[CircleMinus] a_ :>
   {t["nude", args] := a,
    ReleaseHold[Hold[t[args] :=
       (If[! ValueQ[theStack], theStack = {}];
        AppendTo[theStack, ToString[t]];
        Check[ss = a, Print[{"-TheStack->", Evaluate@theStack}]; 
         Print@Hold[a]; Abort[]];
        theStack = Most@theStack;
        Return[ss])
      ]]};
v[x_, y_]\[CircleMinus]  (Sin@ g[x, y]) /. funcDef;
g[x_, y_]\[CircleMinus]  x/y /. funcDef;
v[2, 3]
v[2, 0]

输出:

Out[299]= Sin[2/3]

During evaluation of In[295]:= Power::infy: Infinite expression 1/0 encountered. >>

During evaluation of In[295]:= {-TheStack->,{v,g}}

During evaluation of In[295]:= Hold[2/0]

Out[300]= $Aborted

One attempt to implement @Timo's idea (theStack)

Incomplete and perhaps flawed, but just to keep thinking about it:

Clear["Global`*"];
funcDef = t_[args___]  \[CircleMinus] a_ :>
   {t["nude", args] := a,
    ReleaseHold[Hold[t[args] :=
       (If[! ValueQ[theStack], theStack = {}];
        AppendTo[theStack, ToString[t]];
        Check[ss = a, Print[{"-TheStack->", Evaluate@theStack}]; 
         Print@Hold[a]; Abort[]];
        theStack = Most@theStack;
        Return[ss])
      ]]};
v[x_, y_]\[CircleMinus]  (Sin@ g[x, y]) /. funcDef;
g[x_, y_]\[CircleMinus]  x/y /. funcDef;
v[2, 3]
v[2, 0]

Output:

Out[299]= Sin[2/3]

During evaluation of In[295]:= Power::infy: Infinite expression 1/0 encountered. >>

During evaluation of In[295]:= {-TheStack->,{v,g}}

During evaluation of In[295]:= Hold[2/0]

Out[300]= $Aborted
非要怀念 2024-10-08 20:20:24

关于提取堆栈的建议,也许是依赖于 Trace 的东西?

下面是使用 Trace 的示例,来自 Chris Chiasson。此代码将 1 + Sin[x + y] + Tan[x + y] 的求值树保存到 ~/temp/msgStream.m

Developer`ClearCache[];
SetAttributes[recordSteps, HoldAll];
recordSteps[expr_] :=

  Block[{$Output = List@OpenWrite["~/temp/msgStream.m"]}, 
   TracePrint[Unevaluated[expr], _?(FreeQ[#, Off] &), 
    TraceInternal -> True];
   Close /@ $Output;
   Thread[
    Union@Cases[
      ReadList["~/temp/msgStream.m", HoldComplete[Expression]], 
      symb_Symbol /; 
        AtomQ@Unevaluated@symb && 
         Context@Unevaluated@symb === "System`" :> 
       HoldComplete@symb, {0, Infinity}, Heads -> True], 
    HoldComplete]
   ];
recordSteps[1 + Tan[x + y] + Sin[x + y]]

为了回答 Samsdram 的问题,下面的代码(也来自 Chris)给出了 Mathematica 表达式的求值树。 这是来自 MathGroup 的帖子,其中包含源代码和示例。

(Attributes@# = {HoldAllComplete}) & /@ {traceToTreeAux, toVertex, 
  HoldFormComplete, getAtoms, getAtomsAux}
MakeBoxes[HoldFormComplete[args___], form_] := 
 MakeBoxes[HoldForm[args], form]
edge[{head1_, pos1_, xpr1_}, {head2_, pos2_, xpr2_}] := 
 Quiet[Rule[{head1, vertexNumberFunction@pos1, xpr1}, {head2, 
    vertexNumberFunction@pos2, xpr2}], {Rule::"rhs"}]
getAtomsAux[atom_ /; AtomQ@Unevaluated@atom] := 
 Sow[HoldFormComplete@atom, getAtomsAux]
getAtomsAux[xpr_] := Map[getAtomsAux, Unevaluated@xpr, Heads -> True]
getAtoms[xpr_] := Flatten@Reap[getAtomsAux@xpr][[2]]
toVertex[traceToTreeAux[HoldForm[heldXpr_], pos_]] := toVertex[heldXpr]
toVertex[traceToTreeAux[HoldForm[heldXprs___], pos_]] := 
 toVertex@traceToTreeAux[Sequence[], pos]
(*this code is strong enough to not need the ToString commands,but \
some of the resulting graph vertices give trouble to the graphing \
routines*)
toVertex[
  traceToTreeAux[xpr_, pos_]] := {ToString[
   Short@Extract[Unevaluated@xpr, 0, HoldFormComplete], StandardForm],
   pos, ToString[Short@First@originalTraceExtract@{pos}, StandardForm]}
traceToTreeAux[xpr_ /; AtomQ@Unevaluated@xpr, ___] := Sequence[]
traceToTreeAux[_HoldForm, ___] := Sequence[]
traceToTreeAux[xpr_, pos_] := 
 With[{lhs = toVertex@traceToTreeAux[xpr, pos], 
   args = HoldComplete @@ Unevaluated@xpr}, 
  Identity[Sequence][
   ReleaseHold[
    Function[Null, edge[lhs, toVertex@#], HoldAllComplete] /@ args], 
   ReleaseHold@args]]
traceToTree[xpr_] := 
 Block[{vertexNumber = -1, vertexNumberFunction, 
   originalTraceExtract}, 
  vertexNumberFunction[arg_] := 
   vertexNumberFunction[arg] = ++vertexNumber; 
  originalTraceExtract[pos_] := 
   Extract[Unevaluated@xpr, pos, HoldFormComplete]; {MapIndexed[
    traceToTreeAux, Unevaluated@xpr, {0, Infinity}]}]
TraceTreeFormPlot[trace_, opts___] := 
  Block[{$traceExpressionToTree = True}, 
   Through@{Unprotect, Update}@SparseArray`ExpressionToTree; 
   SparseArray`ExpressionToTree[trace, Infinity] = traceToTree@trace; 
   With[{result = ToExpression@ToBoxes@TreeForm[trace, opts]}, 
    Through@{Unprotect, Update}@SparseArray`ExpressionToTree; 
    SparseArray`ExpressionToTree[trace, Infinity] =.; 
    Through@{Update, Protect, Update}@SparseArray`ExpressionToTree; 
    result]];

TraceTreeFormPlot[Trace[Tan[x] + Sin[x] - 2*3 - 55]]

A suggestion for extracting stack, maybe something that relies on Trace?

An example of using Trace below, from Chris Chiasson. This code saves evaluation tree of 1 + Sin[x + y] + Tan[x + y] into ~/temp/msgStream.m

Developer`ClearCache[];
SetAttributes[recordSteps, HoldAll];
recordSteps[expr_] :=

  Block[{$Output = List@OpenWrite["~/temp/msgStream.m"]}, 
   TracePrint[Unevaluated[expr], _?(FreeQ[#, Off] &), 
    TraceInternal -> True];
   Close /@ $Output;
   Thread[
    Union@Cases[
      ReadList["~/temp/msgStream.m", HoldComplete[Expression]], 
      symb_Symbol /; 
        AtomQ@Unevaluated@symb && 
         Context@Unevaluated@symb === "System`" :> 
       HoldComplete@symb, {0, Infinity}, Heads -> True], 
    HoldComplete]
   ];
recordSteps[1 + Tan[x + y] + Sin[x + y]]

To answer Samsdram's question, the code below (also from Chris) gives evaluation tree of a Mathematica expression. Here is the post from MathGroup with source code and examples.

(Attributes@# = {HoldAllComplete}) & /@ {traceToTreeAux, toVertex, 
  HoldFormComplete, getAtoms, getAtomsAux}
MakeBoxes[HoldFormComplete[args___], form_] := 
 MakeBoxes[HoldForm[args], form]
edge[{head1_, pos1_, xpr1_}, {head2_, pos2_, xpr2_}] := 
 Quiet[Rule[{head1, vertexNumberFunction@pos1, xpr1}, {head2, 
    vertexNumberFunction@pos2, xpr2}], {Rule::"rhs"}]
getAtomsAux[atom_ /; AtomQ@Unevaluated@atom] := 
 Sow[HoldFormComplete@atom, getAtomsAux]
getAtomsAux[xpr_] := Map[getAtomsAux, Unevaluated@xpr, Heads -> True]
getAtoms[xpr_] := Flatten@Reap[getAtomsAux@xpr][[2]]
toVertex[traceToTreeAux[HoldForm[heldXpr_], pos_]] := toVertex[heldXpr]
toVertex[traceToTreeAux[HoldForm[heldXprs___], pos_]] := 
 toVertex@traceToTreeAux[Sequence[], pos]
(*this code is strong enough to not need the ToString commands,but \
some of the resulting graph vertices give trouble to the graphing \
routines*)
toVertex[
  traceToTreeAux[xpr_, pos_]] := {ToString[
   Short@Extract[Unevaluated@xpr, 0, HoldFormComplete], StandardForm],
   pos, ToString[Short@First@originalTraceExtract@{pos}, StandardForm]}
traceToTreeAux[xpr_ /; AtomQ@Unevaluated@xpr, ___] := Sequence[]
traceToTreeAux[_HoldForm, ___] := Sequence[]
traceToTreeAux[xpr_, pos_] := 
 With[{lhs = toVertex@traceToTreeAux[xpr, pos], 
   args = HoldComplete @@ Unevaluated@xpr}, 
  Identity[Sequence][
   ReleaseHold[
    Function[Null, edge[lhs, toVertex@#], HoldAllComplete] /@ args], 
   ReleaseHold@args]]
traceToTree[xpr_] := 
 Block[{vertexNumber = -1, vertexNumberFunction, 
   originalTraceExtract}, 
  vertexNumberFunction[arg_] := 
   vertexNumberFunction[arg] = ++vertexNumber; 
  originalTraceExtract[pos_] := 
   Extract[Unevaluated@xpr, pos, HoldFormComplete]; {MapIndexed[
    traceToTreeAux, Unevaluated@xpr, {0, Infinity}]}]
TraceTreeFormPlot[trace_, opts___] := 
  Block[{$traceExpressionToTree = True}, 
   Through@{Unprotect, Update}@SparseArray`ExpressionToTree; 
   SparseArray`ExpressionToTree[trace, Infinity] = traceToTree@trace; 
   With[{result = ToExpression@ToBoxes@TreeForm[trace, opts]}, 
    Through@{Unprotect, Update}@SparseArray`ExpressionToTree; 
    SparseArray`ExpressionToTree[trace, Infinity] =.; 
    Through@{Update, Protect, Update}@SparseArray`ExpressionToTree; 
    result]];

TraceTreeFormPlot[Trace[Tan[x] + Sin[x] - 2*3 - 55]]
°如果伤别离去 2024-10-08 20:20:24

也许我们想多了。如果我们稍微调整一下参数的模式匹配会怎样?例如,如果我们修改函数来检查数字数量,并添加一些代码以在失败时打印错误。例如,

 TypeNumeric[x_] :=   If[! NumericQ[Evaluate[x]],
 Print["error at "]; Print[Stack[]];    Print["Expression "]; Print[x];    Print["Did   
 not return a numeric value"];Return[False], 
 (*Else*)
 Return[True];] 
 SetAttributes[TypeNumeric, HoldAll];

第 2 步:如果您有一个需要数字量的函数 f[x_],只需使用标准模式测试编写它,一切都应该很好

Input:
f[x_?TypeNumeric] := Sqrt[x]
f[Log[y]]
f[Log[5]]
Output:
error at 
{f}
Expression 
Log[y]
Did not return a numeric value
f[Log[y]]

Sqrt[Log[5]]

我相信这会起作用,并且它使健壮的类型检查变得如此简单编写一个或两个函数。问题是,这可能非常低效,因为此代码对表达式 x 求值两次,一次用于类型检查,一次用于实数。如果涉及昂贵的函数调用,这可能会很糟糕。

我还没有找到解决第二个问题的方法,欢迎在这方面提出建议。继续是解决这个问题的方法吗?

希望这有帮助。

Perhaps we have been over thinking this. What if we just tweaked the pattern matching on the arguments a little. For instance, if we modified the function to check for a numeric quantity and added some code to print an error if it fails. For instance,

 TypeNumeric[x_] :=   If[! NumericQ[Evaluate[x]],
 Print["error at "]; Print[Stack[]];    Print["Expression "]; Print[x];    Print["Did   
 not return a numeric value"];Return[False], 
 (*Else*)
 Return[True];] 
 SetAttributes[TypeNumeric, HoldAll];

Step 2: If you have a function, f[x_] that requires a numeric quantity, just write it with the standard pattern test and all should be well

Input:
f[x_?TypeNumeric] := Sqrt[x]
f[Log[y]]
f[Log[5]]
Output:
error at 
{f}
Expression 
Log[y]
Did not return a numeric value
f[Log[y]]

Sqrt[Log[5]]

I believe this will work and, it makes robust type checking as simple as a writing a function or two. The problem is that this could be hugely inefficient because this code evaluates the expression x twice, once for the type checking and once for real. This could be bad if an expensive function call is involved.

I haven't figured out the way around this second problem and would welcome suggestions on that front. Are continuations the way out of this problem?

Hope this helps.

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