Настройка диагностических сообщений об ошибках в больших проектах Mathematica - PullRequest
18 голосов
/ 14 ноября 2010

Всякий раз, когда я создаю большой проект Mathematica, я сталкиваюсь с этой проблемой: Предотвращение лавины ошибок времени выполнения в Mathematica , т.е. сообщения об ошибках Mathematica являются непрозрачными, архаичными и легионными.

Идея заключается в том, чтобы отключить все собственные сообщения об ошибках Mathematica и реализовать собственные проверки типов и сообщения об ошибках в каждой функции и модуле. Однако я не нашел простого и эффективного способа сделать это, и в итоге, например, какая-то функция генерирует вызов функции error 20 deep, а затем получает целый каскад сообщений об ошибках вплоть до основной подпрограммы.

Как бы вы настроили простой механизм для этого, который генерирует только одно сообщение об ошибке в функции, которая испытывает ошибку, и простой список цепочки вызовов функций?

РЕДАКТИРОВАТЬ: Так как он пришел в пару ответов; Я специально ищу что-то более легкое в отношении выходных данных, которые он производит (в противном случае я мог бы просто придерживаться сообщений об ошибках Mathematica), и, очевидно, также легкое в вычислительных затратах. Таким образом, в то время как Stack и Trace определенно не требуют дополнительных затрат, их вывод в сложных проектах анализировать не так быстро, и необходимо выполнить некоторую работу по его упрощению.

Ответы [ 5 ]

9 голосов
/ 16 ноября 2010

ЯСИ - еще одна (глупая?) Идея ...

Перечитайте ваш вопрос ...

Идея заключается в том, чтобы отключить все собственные сообщения об ошибках 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

заключение ... Отменяется при первом сообщении и оставляет «разумный» след стека. «Разумный» означает «Должен быть улучшен».

Но это совершенно не навязчиво!

3 голосов
/ 15 ноября 2010

Одна попытка реализовать идею @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
3 голосов
/ 15 ноября 2010

Предложение для извлечения стека, может быть, что-то, что полагается на Trace?

Пример использования Trace ниже, от Криса Чиассона.Этот код сохраняет дерево оценки 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]]

Чтобы ответить на вопрос Самсдрама, код ниже (также от Криса)) дает дерево оценки выражения 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]]
3 голосов
/ 14 ноября 2010

Чтобы заставить мяч катиться здесь - одна идея, с которой я играю;создание псевдостека.

Сначала создайте глобальную переменную theStack={}, а затем в каждом Function или Module начинайте с AppendTo[theStack,"thisFuncName"] и заканчивайте theStack=Most@theStack.Предполагая умеренную (~ несколько десятков) глубину вызовов функций, это не должно добавить каких-либо существенных накладных расходов.

Затем реализуйте свою собственную проверку ввода / ошибок и используйте Print@theStack;Abort[]; для ошибок.

УточненияЭтот метод может включать в себя:

  1. Изучение способа динамического получения «thisFuncionName», чтобы AppendTo[] мог быть превращен в идентичный вызов функции для всех Functions и Module.
  2. Использование Message[] Вместо Print[].
  3. Выдача других важных переменных / информации о состоянии на theStack.
2 голосов
/ 15 ноября 2010

Возможно, мы передумали это. Что, если мы просто немного подправим соответствие шаблонов аргументам. Например, если мы изменили функцию, чтобы проверить числовое значение, и добавили некоторый код, чтобы вывести ошибку в случае сбоя. Например,

 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 дважды, один раз для проверки типа и один раз для реального. Это может быть плохо, если задействован дорогой вызов функции.

Я не нашел пути решения этой второй проблемы и хотел бы получить предложения по этому вопросу. Являются ли продолжения выходом из этой проблемы?

Надеюсь, это поможет.

...