Prevent Part[] from trying to extract parts of symbolic expressions

14

If you have a list, e.g.

{1, 2, 3}

then you can extract the $k$th part using Part (list[[k]]):

In[1]:= {1, 2, 3}[[2]]
Out[1]= 2

The problem is that if you provide a symbolic expression in the place of the list, Part will try to decompose it:

In[1]:= list[[2]]
Part::partd: Part specification list[[2]] is longer than depth of object.
Out[1]= list[[2]]

That is, the issue occurs when the overall expression is evaluated before the value of list is known. For example:

In[1]:= list[[2]] /. list -> {1, 2, 3}
Part::partd: Part specification list[[2]] is longer than depth of object.
Out[1]= 2

Although the final output is correct, this produces an annoying spurious error message.

It's even worse if list is a compound expression, because then Part will decompose it, unexpectedly changing its value. For example,

In[1]:= SinCos[x][[1]] /. SinCos -> (θ \[Function] {Sin@θ, Cos@θ})
Out[1]= x

Wat.

Example

I commonly run into this when plotting one dimension of nested functions and data structures.

Suppose I have a function that generates a list of functions according to some parameters:

functions[x_Real, a_, b_, c_] := {Sin@x, Cos@x, Sin[a*x], Cos[b*x], Sin[c*x]};

Now I want to plot the first, third, and fourth functions in the list of functions with parameters $1,2,3$; in this case, $y = \sin(x), \sin(1 \cdot x), \cos(2 \cdot x)$. The obvious way is:

Plot[functions[x, 1, 2, 3][[#]]& /@ {1,3,4} // Evaluate, {x, 0, 5}]

(The Evaluate is needed to get Plot to treat the values as separate functions, so that they will be styled differently.) And Mathematica will dutifully plot... $y = x, 2, 3$:

{Plot[functions[x,1,2,3][[#]]&/@{1,3,4}//Unevaluated,{x,0,5}],
 Plot[functions[x,1,2,3][[#]]&/@{1,3,4}//Evaluate,{x,0,5}]}

Wat

Question

How do I prevent Part[] from trying to decompose symbolic expressions when it is evaluated?

I have a workaround below, but I'm interested in whether there's a better way to do it. Is there a standard built-in function that does what I'm looking for?

Mechanical snail

Posted 2012-11-15T12:00:15.700

Reputation: 2 302

2

is this Q/A related/useful? i.e. does Plot[Evaluate[Hold[functions[x, 1, 2, 3][[#]]] & /@ {1, 3, 4}], {x, 0, 1}] work for what you need to do?

– kglr – 2012-11-15T12:25:26.693

Have you considered changing the order? For example: (list /. list -> {1, 2, 3})[[2]] – xzczd – 2012-11-15T13:07:02.550

BTW, notice that deleting the _Real in functions will lead to an opposite result. – xzczd – 2012-11-15T13:13:16.500

I'm sorry if the answer to the following question is obvious to you... Are you aware of the fact that a list {a} is as much a "symbolic" expression as is list? Or do you mean by symbolic that AtomQ@list==True? – sebhofer – 2012-11-15T14:04:47.950

Answers

7

To outline the problem, if you write Part[f[[x,2]],2] with no rules for f, then part will just extract the contents of the "call" to f. Now for Plot called with evaluate, the expression evaluates fully before x is actually set, but in reality you only want it to partially evaluate and stop whenever Part attempts to extract a part of a call to f. To sketch it out, what you want is something like (f[x,1,2,3]/.x->3)[[2]] What you get is f[x,1,2,3][[2]]/.x->3.

If this behavior is something you want for a given Head of your own construction, it can be done with an UpValue definition switching over to a wrapper for part that only evaluates if the head is not the same as the one you wish to stop on:

 f /: Part[a_f, n_] := delayedPart[a, n, f]
 delayedPart[a_, n_, stopHead_] /; (Head[a] =!= stopHead) := Part[a, n]

So now, f[x, 1, 2, 3][[2]] becomes delayedPart[f[x, 1, 2, 3], 2, f] which evaluates as you want it to when x is given. So the difference from any regular function to this one is then if we add the definition for f:

 f[x_?NumericQ, a_, b_, c_] := {Sin@x, Cos@x, Sin[a*x], Cos[b*x], Sin[c*x]}
 g[VarX, 1, 2, 3][[1]] /. VarX -> 42 (* => 42 *)
 f[VarX, 1, 2, 3][[1]] /. VarX -> 42 (* => Sin[42] *)

jVincent

Posted 2012-11-15T12:00:15.700

Reputation: 14 423

6

Question

How do I prevent Part[] from trying to decompose symbolic expressions when it is evaluated?

Mathematica 10 implements something like your listPart (with additional functionality): Indexed:

Indexed can be used to indicate components of symbolic vectors, matrices, tensors, etc.

When expr is a list, Indexed[expr,i] gives expr[[i]].

When expr is a list, Indexed[expr,{i,j,...}] gives Indexed[expr[[i]],{j,...}].

For example:

expr = Indexed[q["x"], 1];

expr /. q["x"] -> {1, 2, 3}
1

Unfortunately it doesn't seem to allow for the extraction of multiple parts e.g. with Span.

Mr.Wizard

Posted 2012-11-15T12:00:15.700

Reputation: 259 163

In version 9, we can use Compile`GetElement. – chyanog – 2021-01-26T08:01:38.350

4

One way is to define a new function that is the same as Part, except will only be evaluated if the parameter is a List:

ListPart[x_List, spec__] := x[[spec]];

Then, instead of using list[[spec]], call ListPart[list, spec]. For example:

In[2]:= list[[2]]
Part::partd: Part specification list[[2]] is longer than depth of object.
Out[2]= list[[2]]

When list is not literally a List, ListPart does not evaluate...

In[3]:= ListPart[list, 2]
Out[3]= ListPart[list, 2]

... until the actual list value is available

In[4]:= ListPart[list, 2] /. list -> {1, 2, 3}
Out[4]= 2

as desired.

Disadvantage: ListPart[list, 2] is much more verbose than list[[2]].

Mechanical snail

Posted 2012-11-15T12:00:15.700

Reputation: 2 302

1

There seems to be two different issues here.

The first is with Plot: the expectation to use a definition for functions[x_Real, a_, b_, c_] := and then get individually styled lines using Part and Evaluate. This does not seem practical, as you specifically prevent symbolic evaluation with _Real (by the way, you may want _?NumericQ). You could perhaps rewrite your function to allow symbolic evaluation which I believe would solve your other problem as well.

The second issue, and the topic of your question, may best be handled by changing the definition of functions (I'll use f for brevity) such that you always provide a part specification. For example, I'll use the syntax f[args][parts]:

f[x_Real, a_, b_, c_][parts___] :=
  {Sin@x, Cos@x, Sin[a*x], Cos[b*x], Sin[c*x]}[[parts]];

f[0.3, 1, 2, 3][]

f[x, 1, 2, 3][{1, 3, 4}]

f[0.3, 1, 2, 3][{1, 3, 4}]
{0.29552, 0.955336, 0.29552, 0.825336, 0.783327}

f[x, 1, 2, 3][{1, 3, 4}]

{0.29552, 0.29552, 0.825336}

This requires no auxiliary functions or definitions, no overloading of Part, and minimal keystrokes in use. The only inconvenience is having to to add [] to each use of f. You could turn the syntax around to f[parts][args] which, due to the order of evaluations in Mathematica, would allow using f[args] plainly, but you would lose the ability to put Attributes on f that would affect args, so I do not recommend it.

Another approach is a variation of jVincent's method:

f2[x_Real, a_, b_, c_] := {Sin@x, Cos@x, Sin[a*x], Cos[b*x], Sin[c*x]};

setPartHold[name_Symbol] :=
 Module[{part},
  name /: x_name[[spec__]] := part[x, spec];
  part[other : Except[_name], spec__] := other[[spec]]
 ]

Then call setPartHold[f2] once to produce this behavior:

f2[0.3, 1, 2, 3]

f2[x, 1, 2, 3][[{1, 3, 4}]]

f2[0.3, 1, 2, 3][[{1, 3, 4}]]
{0.29552, 0.955336, 0.29552, 0.825336, 0.783327}

part$584[f2[x, 1, 2, 3], {1, 3, 4}]

{0.29552, 0.29552, 0.825336}

Note that the generated part$xxx function is active such that:

part$584[f2[x, 1, 2, 3], {1, 3, 4}] /. x -> 0.3
{0.29552, 0.29552, 0.825336}

Mr.Wizard

Posted 2012-11-15T12:00:15.700

Reputation: 259 163

0

I don't have any points to comment on Daniel W's answer.

But here's how you write this

z[[1]]/z[[2]] /. z -> zz

to not generate any part errors

Indexed[HoldForm[z], 1]/Indexed[HoldForm[z], 2] /. HoldForm[z] -> zz
Out[1]=3/4

ejkitchen

Posted 2012-11-15T12:00:15.700

Reputation: 1

0

Here is an alternate solution I just worked out for a problem I am working on. I have a function that calculates a vector of values. In my problem, this function is rather time-consuming so I do not want to evaluate it more than I absolutely have to. The toy version is

zz = Block[{a, b}, a = 3; b = 4; {a, b}]

Elsewhere in the notebook, I want to use these calculated values in a formula

z[[1]]/z[[2]] /. z -> zz

This correctly returns 3/4, but not before carping that the Part specification is longer than the depth of the object. My solution is to write my first function to return a function that accesses elements of my list

zz = Block[{a, b, xx}, a = 3; b = 4; 
  Function[i, xx[[i]]] /. xx -> {a, b}]

where I have to use a little trickery to get {a,b} to evaluate in the Function. zz is now defined as

Function[i, {3, 4}[[i]]]

Now I can write an formula in terms of the array parts

z[1]/z[2] /. z -> zz

without generating an error from Part

Daniel W

Posted 2012-11-15T12:00:15.700

Reputation: 3 196