11
$\begingroup$

I'm trying to understand Trace. Usually it doesn't repeat but it adds {-1,-1} for his simple expression after having a correct result.

-{1}//Trace

{-1, {-1}, {-1, -1}, {-1}}

Why -{1}//Trace is longer than -Range[1]//Trace?

$\endgroup$
8
  • $\begingroup$ You might find it more informative to use TracePrint[] instead. $\endgroup$ Commented May 20, 2017 at 20:33
  • $\begingroup$ Also -{1} // Trace // FullForm reveals some hidden structure. $\endgroup$
    – Michael E2
    Commented May 20, 2017 at 20:42
  • $\begingroup$ @Shadowray, no, I want to make a function, that list argument list_List that is to be used as -list. as list is big, Trace is also. I don't know if I sould use other expression $\endgroup$
    – iot
    Commented May 20, 2017 at 20:44
  • $\begingroup$ @MichaelE2, -Range[1]// Trace// FullForm doesn't do transformations that first expression does. Why is it so? $\endgroup$
    – iot
    Commented May 20, 2017 at 20:59
  • 5
    $\begingroup$ I suspect it's because Range[1] produces a packed array (Developer`PackedArrayQ@Range[1]), and they have different rules for evaluation than other lists. The difference is more apparent with longer lists, e.g. -{1, 2} vs. -Range[2]. $\endgroup$
    – Michael E2
    Commented May 20, 2017 at 21:28

1 Answer 1

12
$\begingroup$

This is a good question for uncovering an important difference between listability and vectorization. Sometimes these terms are used interchangeably, but I would like to make a technical distinction. In both Listable and vectorized functions, the value of the function on a list is equal to values of the function on each element of the list. From the theoretical point of view of a function is defined by its values, there is no difference, but on practical grounds, there is a difference in how efficiently a function is computed. A function is vectorized if it treats a vector (or array) as if it were atomic. The ability to do this depends on the architecture of the CPU; it is implemented in Mathematica by packed arrays (see this technical note by Rob Knapp, this Q&A and this one). Generally speaking, when a packed array is passed to a function that is not vectorized, the array will be unpacked (search the site, or perhaps start here).

The difference one sees in the Trace of -{1} and -Range[1] is due to Range[n] being a packed array. For the most part, the difference between the listability and vectorization is invisible to the user. One sees it mainly speed, memory usage, and in this case the evaluation sequence exposed by Trace. One can also test whether a list is a packed array with Developer`PackedArrayQ. One can trace unpacking with On["Packing"]. Aside these tools, I do not think that it is possible for a user to define a function of the form f[...] :=... that is both Listable and vectorized; the user's only option is to write a non-Listable function in terms of built-in vectorized functions or use Compile (or perhaps write a LibraryFunction). In other words, if one sets SetAttributes[f, Listable], the listability attribute is applied first: f[{x1, x2,...}] becomes {f[x1], f[x2],...} before f is evaluated, which will unpack a packed array. But in the OP's example, with Times, which is Listable, this does not happen. It is similar with Plus and other vectorized functions. It must be that there are internal rules that are applied for such functions, shadows of which show up in Trace, but which are not explained in the documentation (AFAIK).

We can see the above in the OP's examples. One gets a more complete picture of the evaluation sequence with Trace[expr, TraceOriginal -> True] (TracePrint, mentioned by @J.M., also shows more). Even further, the differences are somewhat more convincing comparing the examples -{1, 2} and -Range[2]. But I deal with the OP's examples and let the reader explore the rest.

In -{1}, the Listable attribute is applied first, the first two steps of -{1} // Trace:

Times[-1, List[1]]
List[Times[-1, 1]]

In -Range[1], the vectorization can be seen in the transformation from the third to fourth step:

Times[-1, List[1]]
List[-1]]

What is invisible is that List[1], the result of Range[1] is a packed array, whereas List[1], the "result" of {1}, is not. This difference may be verified by executing the following:

{1} // Developer`PackedArrayQ
Range[1] // Developer`PackedArrayQ

One can see the same listability step applied if Range[1] is unpacked, which can be done with the function Developer`FromPackedArray:

-Developer`FromPackedArray@Range[1] // Trace

Addendum: Help with reading Trace output.

WReach's traceView2 is a wonderful function for presenting the evaluation sequence.

Here is another function for formatting Trace output. The output consists of nested lists corresponding to the depth of the evaluation stack. The following indicates each level by grouping and indenting its items.

Clear[fmt, ifmt];
fmt[x_] := Block[{fmt`step = 0}, ifmt[x]];
ifmt[s_List] := Row[
   {Style[RawBoxes@"{", SpanMaxSize -> Infinity],
    " ",
    Column[ifmt /@ s]}];
ifmt[x_] := Row@{Style[++fmt`step, Italic, "Label"], ". ", FullForm /@ x};

Op's examples:

-List[1] // Trace // fmt

-Range[1] // Trace // fmt

Trace[expr] omits some steps of the evaluation such as the Head of each expression. Trace[expr, TraceOriginal -> True] shows more.

Exercises.

Compare these with each other and with the ordinary trace above.

Trace[-List[1], TraceOriginal -> True] // fmt
Trace[-Range[1], TraceOriginal -> True] // fmt

Examine these (I think the difference between listability and vectorization is even more obvious):

Trace[-List[1, 2], TraceOriginal -> True] // fmt
Trace[-Range[2], TraceOriginal -> True] // fmt
$\endgroup$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.