2
$\begingroup$

People on this site keep telling me that good MMa practice avoids the use of loops.

The code below calculates the same Table of derivatives two different ways, and the one with the Do loops is much faster. I think the first, slower version is calculating the nth derivative from scratch without using the (n-1)th derivative as a starting point.

Derivative[q_, 1][y][x, v] = D[(D[y[x, v], {x, 2}] + D[y[x, v], x]^2), {x, q}]/2;
ord = 9;
Print[Timing[dgdv1 = Table[D[y[x, v], {v, i}], {i, 0, ord - 1}];]];
dgdv2 = Flatten[{y[x, v], Table[0, {i, 2, ord}]}];
Print[Timing[Do[dgdv2[[i]] = D[dgdv2[[i - 1]], v], {i, 2, ord}];]];

(*{3.931225,Null}

{2.449216,Null} *)

So how can one efficiently calculate the first several derivatives of g[x,v] wrt v without using a loop?
Bonus Question: Is there some even faster way to do this calculation?

OP's EDIT: Some commentors below were confused by the first line of my code, in which I define a derivative relationship for g[x,v]. It's not really relevant to the computation speed issue this Question is about, but here is a link for people who want to learn about defining derivatives.

$\endgroup$
  • 3
    $\begingroup$ Derivative is already defined, you code has syntax problems. $\endgroup$ – rhermans Oct 14 '15 at 14:18
  • $\begingroup$ With NestList NestList[D[#, x] &, y[x], 10] $\endgroup$ – rhermans Oct 14 '15 at 14:20
  • 1
    $\begingroup$ @rhermans No, that's how you define derivatives for functions. $\endgroup$ – Jerry Guern Oct 14 '15 at 15:58
  • $\begingroup$ @Jerry Guern - I am pretty much lost on the first statement and how it affects subsequent statements. Could you kindly explain the first statement and how it affects D[y[x,v], {v, i}]? Thank you $\endgroup$ – Jack LaVigne Oct 15 '15 at 23:44
  • $\begingroup$ @JackLaVigne The first line of my code implements a diff eq for $g[x,v]$ discussed here. I didn't discuss the meaning of it here because it's outside the scope of the question about performance tuning. $\endgroup$ – Jerry Guern Oct 16 '15 at 1:09
9
$\begingroup$
ord = 9;

Timing[d1 = Table[D[y[x, v], {v, i}], {i, 0, ord - 1}];][[1]]

(*  2.06537  *)

Rather than using Table, mapping onto a Range is often more efficient

Timing[
  d12 = D[y[x, v], {v, #}] & /@
     Range[0, ord - 1];][[1]]

(*  2.03747  *)

For a fair timing comparison, the initialization of the array should be included in the timing

Timing[
  d2 = Flatten[{y[x, v], Table[0, {i, 2, ord}]}];
  Do[d2[[i]] = D[d2[[i - 1]], v],
   {i, 2, ord}];][[1]]

(*  1.70694  *)

With symbolic operations it can sometimes be faster to Simplify intermediate steps

d22 = Flatten[{y[x, v], Table[0, {i, 2, ord}]}];
Timing[
  Do[d22[[i]] = Simplify[D[d22[[i - 1]], v]],
    {i, 2, ord}];][[1]]

(*  0.307888  *)

However, for this type of problem NestList is faster (and "more Mathematica-like")

Timing[d3 = NestList[D[#, v] &, y[x, v], ord - 1];][[1]]

(*  1.58817  *)

Again using Simplify

Timing[d32 = NestList[Simplify[D[#, v]] &, y[x, v], ord - 1];][[1]]

(*  0.060246  *)

Verifying that all approaches return the same results

d1 == d12 == d2 == d22 == d3 == d32 // Simplify

(*  True  *)
$\endgroup$
  • $\begingroup$ This was great, thanks. I posted a comparison of the two methods in an Answer below. With Expand performed on each derivative, the two methods had similar Timing. $\endgroup$ – Jerry Guern Oct 14 '15 at 17:35
0
$\begingroup$

From the OP: This is not an Answer, just an extended comment on @BobHanlon's Answer above, which I Accepted. I tried both methods on a longer run and included the Table initialization in the Timing, and I found that the methods were very similar in kernel time, but which was faster varied in different runs:

Derivative[q_, 1][y][x, v] = 
  D[(D[y[x, v], {x, 2}] + D[y[x, v], x]^2), {x, q}]/2;
ord = 16;
Print[Timing[
   dgdv2 = Flatten[{y[x, v], Table[0, {i, 2, ord}]}];
   Do[dgdv2[[i]] = Expand[D[dgdv2[[i - 1]], v]], {i, 2, ord}];]];
Print[Timing[dgdv3 = NestList[Expand[D[#, v]] &, y[x, v], ord - 1];]];
dgdv2 == dgdv3

Output from 3 runs:

(* {25.584164,Null} {25.006960,Null} True

{24.226955,Null} {24.336156,Null} True

{24.211355,Null} {24.601358,Null} True *)

$\endgroup$
  • $\begingroup$ Your use of Print is unnecessary if you don't suppress the output with the final semi-colon. expr will give the same output as Print[expr]; $\endgroup$ – Bob Hanlon Oct 14 '15 at 22:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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