Ways to speed up user implemented RK4Speed up Numerical IntegrationHow to choose MaxStepFraction for optimal speed of NDSolveNIntegrate: how to speed up code?Compiling FoldList implementation for RK4How to speed up this code?Solving an unstable BVP numerically, accurately and efficientlyHow to speed up integral of results of PDE modelSolve BVP involving user defined functionWays to speed up PickSpeed up ParametricNDSolve

How do I go from 300 unfinished/half written blog posts, to published posts?

How to run a prison with the smallest amount of guards?

Are student evaluations of teaching assistants read by others in the faculty?

How to safely derail a train during transit?

How to be diplomatic in refusing to write code that breaches the privacy of our users

Trouble understanding the speech of overseas colleagues

What is paid subscription needed for in Mortal Kombat 11?

Large drywall patch supports

Tiptoe or tiphoof? Adjusting words to better fit fantasy races

Is expanding the research of a group into machine learning as a PhD student risky?

Is HostGator storing my password in plaintext?

How does the UK government determine the size of a mandate?

Is there a good way to store credentials outside of a password manager?

How do scammers retract money, while you can’t?

I'm in charge of equipment buying but no one's ever happy with what I choose. How to fix this?

Method to test if a number is a perfect power?

Applicability of Single Responsibility Principle

What Brexit proposals are on the table in the indicative votes on the 27th of March 2019?

Pole-zeros of a real-valued causal FIR system

How to Reset Passwords on Multiple Websites Easily?

What is the difference between "behavior" and "behaviour"?

Anatomically Correct Strange Women In Ponds Distributing Swords

Opposite of a diet

What can we do to stop prior company from asking us questions?



Ways to speed up user implemented RK4


Speed up Numerical IntegrationHow to choose MaxStepFraction for optimal speed of NDSolveNIntegrate: how to speed up code?Compiling FoldList implementation for RK4How to speed up this code?Solving an unstable BVP numerically, accurately and efficientlyHow to speed up integral of results of PDE modelSolve BVP involving user defined functionWays to speed up PickSpeed up ParametricNDSolve













8












$begingroup$


So, I've implemented RK4, and I'm wondering what I can do to make it more efficient? What I've got so far is below. I wish to still record all steps. I think AppendTo is doing the most damage to the time, is there a faster alternative?



rk4[f_, variables_, valtinit_, tinit_, tfinal_, nsteps_] := 
Module[table, xlist, ylist, step, k1, k2, k3, k4,
xlist = tinit;
step = N[(tfinal - tinit)/(nsteps)];
ylist = valtinit;
table = xlist, ylist;
Table[
k1 = step* f /. MapThread[Rule, variables, ylist]; (*
Equivalent to step* f/.Thread[Rule[variables,ylist]]*)
k2 = step*f /. MapThread[Rule, variables, k1/2 + ylist];
k3 = step*f /. MapThread[Rule, variables, k2/2 + ylist];
k4 = step*f /. MapThread[Rule, variables, k3 + ylist];
ylist += 1/6 (k1 + 2 (k2 + k3) + k4);
xlist += step;
AppendTo[table, xlist, ylist];
xlist, ylist, nsteps];
table
];


Example Input:



funclist = -x + y, x - y;
initials = 1, 2;
variables = x, y;
init = 0;
final = 200;
nstep = 20000;
approx = rk4[funclist, variables, initials, init, final, nstep]//AbsoluteTiming;



3.59932,...




I'd love some suggestions!










share|improve this question











$endgroup$







  • 3




    $begingroup$
    AppendTo is quadratic time complexity. Might be better to preallocate and set by index. Also it'll be much faster to not use Rule and instead code stuff up a little bit more explicitly. As a general rule, too, use vectorized operators. Those can be very fast. And if everything can be totally functional over "packed arrays" (look them up here) it'll be very quick too.
    $endgroup$
    – b3m2a1
    yesterday










  • $begingroup$
    I'll work on implementing it more explicity, this is what came to find first though. It'll require some changes to the inputs, I'll have to ponder this. And preallocating the list is a quick change that won't be an issue to do, I can't believe I forgot that's faster :(. Thanks though!
    $endgroup$
    – Shinaolord
    yesterday










  • $begingroup$
    Shinaoloard, using Join[ xlist, ylist, Table[ k1 = step*f /. MapThread[Rule, variables, ylist]; k2 = step*f /. MapThread[Rule, variables, k1/2 + ylist]; k3 = step*f /. MapThread[Rule, variables, k2/2 + ylist]; k4 = step*f /. MapThread[Rule, variables, k3 + ylist]; ylist += 1/6 (k1 + 2 (k2 + k3) + k4); xlist += step; xlist, ylist, nsteps ] ] as return value is already a first step. There is no point in appending if you use a Table anyways.
    $endgroup$
    – Henrik Schumacher
    yesterday






  • 3




    $begingroup$
    Why not just get NDSolve[] to use fourth-order Runge-Kutta to begin with?
    $endgroup$
    – J. M. is slightly pensive
    23 hours ago






  • 1




    $begingroup$
    @J.M.isslightlypensive I know it can, I just wanted to make sure I could actually code it myself, instead of just using options to get Mathematica to do it for me (:. Thanks for trying to help though!!
    $endgroup$
    – Shinaolord
    13 hours ago















8












$begingroup$


So, I've implemented RK4, and I'm wondering what I can do to make it more efficient? What I've got so far is below. I wish to still record all steps. I think AppendTo is doing the most damage to the time, is there a faster alternative?



rk4[f_, variables_, valtinit_, tinit_, tfinal_, nsteps_] := 
Module[table, xlist, ylist, step, k1, k2, k3, k4,
xlist = tinit;
step = N[(tfinal - tinit)/(nsteps)];
ylist = valtinit;
table = xlist, ylist;
Table[
k1 = step* f /. MapThread[Rule, variables, ylist]; (*
Equivalent to step* f/.Thread[Rule[variables,ylist]]*)
k2 = step*f /. MapThread[Rule, variables, k1/2 + ylist];
k3 = step*f /. MapThread[Rule, variables, k2/2 + ylist];
k4 = step*f /. MapThread[Rule, variables, k3 + ylist];
ylist += 1/6 (k1 + 2 (k2 + k3) + k4);
xlist += step;
AppendTo[table, xlist, ylist];
xlist, ylist, nsteps];
table
];


Example Input:



funclist = -x + y, x - y;
initials = 1, 2;
variables = x, y;
init = 0;
final = 200;
nstep = 20000;
approx = rk4[funclist, variables, initials, init, final, nstep]//AbsoluteTiming;



3.59932,...




I'd love some suggestions!










share|improve this question











$endgroup$







  • 3




    $begingroup$
    AppendTo is quadratic time complexity. Might be better to preallocate and set by index. Also it'll be much faster to not use Rule and instead code stuff up a little bit more explicitly. As a general rule, too, use vectorized operators. Those can be very fast. And if everything can be totally functional over "packed arrays" (look them up here) it'll be very quick too.
    $endgroup$
    – b3m2a1
    yesterday










  • $begingroup$
    I'll work on implementing it more explicity, this is what came to find first though. It'll require some changes to the inputs, I'll have to ponder this. And preallocating the list is a quick change that won't be an issue to do, I can't believe I forgot that's faster :(. Thanks though!
    $endgroup$
    – Shinaolord
    yesterday










  • $begingroup$
    Shinaoloard, using Join[ xlist, ylist, Table[ k1 = step*f /. MapThread[Rule, variables, ylist]; k2 = step*f /. MapThread[Rule, variables, k1/2 + ylist]; k3 = step*f /. MapThread[Rule, variables, k2/2 + ylist]; k4 = step*f /. MapThread[Rule, variables, k3 + ylist]; ylist += 1/6 (k1 + 2 (k2 + k3) + k4); xlist += step; xlist, ylist, nsteps ] ] as return value is already a first step. There is no point in appending if you use a Table anyways.
    $endgroup$
    – Henrik Schumacher
    yesterday






  • 3




    $begingroup$
    Why not just get NDSolve[] to use fourth-order Runge-Kutta to begin with?
    $endgroup$
    – J. M. is slightly pensive
    23 hours ago






  • 1




    $begingroup$
    @J.M.isslightlypensive I know it can, I just wanted to make sure I could actually code it myself, instead of just using options to get Mathematica to do it for me (:. Thanks for trying to help though!!
    $endgroup$
    – Shinaolord
    13 hours ago













8












8








8


1



$begingroup$


So, I've implemented RK4, and I'm wondering what I can do to make it more efficient? What I've got so far is below. I wish to still record all steps. I think AppendTo is doing the most damage to the time, is there a faster alternative?



rk4[f_, variables_, valtinit_, tinit_, tfinal_, nsteps_] := 
Module[table, xlist, ylist, step, k1, k2, k3, k4,
xlist = tinit;
step = N[(tfinal - tinit)/(nsteps)];
ylist = valtinit;
table = xlist, ylist;
Table[
k1 = step* f /. MapThread[Rule, variables, ylist]; (*
Equivalent to step* f/.Thread[Rule[variables,ylist]]*)
k2 = step*f /. MapThread[Rule, variables, k1/2 + ylist];
k3 = step*f /. MapThread[Rule, variables, k2/2 + ylist];
k4 = step*f /. MapThread[Rule, variables, k3 + ylist];
ylist += 1/6 (k1 + 2 (k2 + k3) + k4);
xlist += step;
AppendTo[table, xlist, ylist];
xlist, ylist, nsteps];
table
];


Example Input:



funclist = -x + y, x - y;
initials = 1, 2;
variables = x, y;
init = 0;
final = 200;
nstep = 20000;
approx = rk4[funclist, variables, initials, init, final, nstep]//AbsoluteTiming;



3.59932,...




I'd love some suggestions!










share|improve this question











$endgroup$




So, I've implemented RK4, and I'm wondering what I can do to make it more efficient? What I've got so far is below. I wish to still record all steps. I think AppendTo is doing the most damage to the time, is there a faster alternative?



rk4[f_, variables_, valtinit_, tinit_, tfinal_, nsteps_] := 
Module[table, xlist, ylist, step, k1, k2, k3, k4,
xlist = tinit;
step = N[(tfinal - tinit)/(nsteps)];
ylist = valtinit;
table = xlist, ylist;
Table[
k1 = step* f /. MapThread[Rule, variables, ylist]; (*
Equivalent to step* f/.Thread[Rule[variables,ylist]]*)
k2 = step*f /. MapThread[Rule, variables, k1/2 + ylist];
k3 = step*f /. MapThread[Rule, variables, k2/2 + ylist];
k4 = step*f /. MapThread[Rule, variables, k3 + ylist];
ylist += 1/6 (k1 + 2 (k2 + k3) + k4);
xlist += step;
AppendTo[table, xlist, ylist];
xlist, ylist, nsteps];
table
];


Example Input:



funclist = -x + y, x - y;
initials = 1, 2;
variables = x, y;
init = 0;
final = 200;
nstep = 20000;
approx = rk4[funclist, variables, initials, init, final, nstep]//AbsoluteTiming;



3.59932,...




I'd love some suggestions!







differential-equations numerical-integration performance-tuning






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited yesterday









xzczd

27.4k573255




27.4k573255










asked yesterday









ShinaolordShinaolord

1088




1088







  • 3




    $begingroup$
    AppendTo is quadratic time complexity. Might be better to preallocate and set by index. Also it'll be much faster to not use Rule and instead code stuff up a little bit more explicitly. As a general rule, too, use vectorized operators. Those can be very fast. And if everything can be totally functional over "packed arrays" (look them up here) it'll be very quick too.
    $endgroup$
    – b3m2a1
    yesterday










  • $begingroup$
    I'll work on implementing it more explicity, this is what came to find first though. It'll require some changes to the inputs, I'll have to ponder this. And preallocating the list is a quick change that won't be an issue to do, I can't believe I forgot that's faster :(. Thanks though!
    $endgroup$
    – Shinaolord
    yesterday










  • $begingroup$
    Shinaoloard, using Join[ xlist, ylist, Table[ k1 = step*f /. MapThread[Rule, variables, ylist]; k2 = step*f /. MapThread[Rule, variables, k1/2 + ylist]; k3 = step*f /. MapThread[Rule, variables, k2/2 + ylist]; k4 = step*f /. MapThread[Rule, variables, k3 + ylist]; ylist += 1/6 (k1 + 2 (k2 + k3) + k4); xlist += step; xlist, ylist, nsteps ] ] as return value is already a first step. There is no point in appending if you use a Table anyways.
    $endgroup$
    – Henrik Schumacher
    yesterday






  • 3




    $begingroup$
    Why not just get NDSolve[] to use fourth-order Runge-Kutta to begin with?
    $endgroup$
    – J. M. is slightly pensive
    23 hours ago






  • 1




    $begingroup$
    @J.M.isslightlypensive I know it can, I just wanted to make sure I could actually code it myself, instead of just using options to get Mathematica to do it for me (:. Thanks for trying to help though!!
    $endgroup$
    – Shinaolord
    13 hours ago












  • 3




    $begingroup$
    AppendTo is quadratic time complexity. Might be better to preallocate and set by index. Also it'll be much faster to not use Rule and instead code stuff up a little bit more explicitly. As a general rule, too, use vectorized operators. Those can be very fast. And if everything can be totally functional over "packed arrays" (look them up here) it'll be very quick too.
    $endgroup$
    – b3m2a1
    yesterday










  • $begingroup$
    I'll work on implementing it more explicity, this is what came to find first though. It'll require some changes to the inputs, I'll have to ponder this. And preallocating the list is a quick change that won't be an issue to do, I can't believe I forgot that's faster :(. Thanks though!
    $endgroup$
    – Shinaolord
    yesterday










  • $begingroup$
    Shinaoloard, using Join[ xlist, ylist, Table[ k1 = step*f /. MapThread[Rule, variables, ylist]; k2 = step*f /. MapThread[Rule, variables, k1/2 + ylist]; k3 = step*f /. MapThread[Rule, variables, k2/2 + ylist]; k4 = step*f /. MapThread[Rule, variables, k3 + ylist]; ylist += 1/6 (k1 + 2 (k2 + k3) + k4); xlist += step; xlist, ylist, nsteps ] ] as return value is already a first step. There is no point in appending if you use a Table anyways.
    $endgroup$
    – Henrik Schumacher
    yesterday






  • 3




    $begingroup$
    Why not just get NDSolve[] to use fourth-order Runge-Kutta to begin with?
    $endgroup$
    – J. M. is slightly pensive
    23 hours ago






  • 1




    $begingroup$
    @J.M.isslightlypensive I know it can, I just wanted to make sure I could actually code it myself, instead of just using options to get Mathematica to do it for me (:. Thanks for trying to help though!!
    $endgroup$
    – Shinaolord
    13 hours ago







3




3




$begingroup$
AppendTo is quadratic time complexity. Might be better to preallocate and set by index. Also it'll be much faster to not use Rule and instead code stuff up a little bit more explicitly. As a general rule, too, use vectorized operators. Those can be very fast. And if everything can be totally functional over "packed arrays" (look them up here) it'll be very quick too.
$endgroup$
– b3m2a1
yesterday




$begingroup$
AppendTo is quadratic time complexity. Might be better to preallocate and set by index. Also it'll be much faster to not use Rule and instead code stuff up a little bit more explicitly. As a general rule, too, use vectorized operators. Those can be very fast. And if everything can be totally functional over "packed arrays" (look them up here) it'll be very quick too.
$endgroup$
– b3m2a1
yesterday












$begingroup$
I'll work on implementing it more explicity, this is what came to find first though. It'll require some changes to the inputs, I'll have to ponder this. And preallocating the list is a quick change that won't be an issue to do, I can't believe I forgot that's faster :(. Thanks though!
$endgroup$
– Shinaolord
yesterday




$begingroup$
I'll work on implementing it more explicity, this is what came to find first though. It'll require some changes to the inputs, I'll have to ponder this. And preallocating the list is a quick change that won't be an issue to do, I can't believe I forgot that's faster :(. Thanks though!
$endgroup$
– Shinaolord
yesterday












$begingroup$
Shinaoloard, using Join[ xlist, ylist, Table[ k1 = step*f /. MapThread[Rule, variables, ylist]; k2 = step*f /. MapThread[Rule, variables, k1/2 + ylist]; k3 = step*f /. MapThread[Rule, variables, k2/2 + ylist]; k4 = step*f /. MapThread[Rule, variables, k3 + ylist]; ylist += 1/6 (k1 + 2 (k2 + k3) + k4); xlist += step; xlist, ylist, nsteps ] ] as return value is already a first step. There is no point in appending if you use a Table anyways.
$endgroup$
– Henrik Schumacher
yesterday




$begingroup$
Shinaoloard, using Join[ xlist, ylist, Table[ k1 = step*f /. MapThread[Rule, variables, ylist]; k2 = step*f /. MapThread[Rule, variables, k1/2 + ylist]; k3 = step*f /. MapThread[Rule, variables, k2/2 + ylist]; k4 = step*f /. MapThread[Rule, variables, k3 + ylist]; ylist += 1/6 (k1 + 2 (k2 + k3) + k4); xlist += step; xlist, ylist, nsteps ] ] as return value is already a first step. There is no point in appending if you use a Table anyways.
$endgroup$
– Henrik Schumacher
yesterday




3




3




$begingroup$
Why not just get NDSolve[] to use fourth-order Runge-Kutta to begin with?
$endgroup$
– J. M. is slightly pensive
23 hours ago




$begingroup$
Why not just get NDSolve[] to use fourth-order Runge-Kutta to begin with?
$endgroup$
– J. M. is slightly pensive
23 hours ago




1




1




$begingroup$
@J.M.isslightlypensive I know it can, I just wanted to make sure I could actually code it myself, instead of just using options to get Mathematica to do it for me (:. Thanks for trying to help though!!
$endgroup$
– Shinaolord
13 hours ago




$begingroup$
@J.M.isslightlypensive I know it can, I just wanted to make sure I could actually code it myself, instead of just using options to get Mathematica to do it for me (:. Thanks for trying to help though!!
$endgroup$
– Shinaolord
13 hours ago










1 Answer
1






active

oldest

votes


















15












$begingroup$

Just to give you an impression how fast things may get when you use the right tools.



For given stepsize τ and given vector field F, this creates a CompiledFunction cStep that computes a single Runge-Kutta step



F = X [Function] -Indexed[X, 2], Indexed[X, 1];

τ = 0.01;
Block[YY, Y, k1, k2, k3, k4,

YY = Table[Compile`GetElement[Y, i], i, 1, 2];
k1 = τ F[YY];
k2 = τ F[0.5 k1 + YY];
k3 = τ F[0.5 k2 + YY];
k4 = τ F[k3 + YY];

cStep = With[code = YY + (k1 + 2. (k2 + k3) + k4)/6. ,
Compile[Y, _Real, 1,
code,
CompilationTarget -> "C",
RuntimeOptions -> "Speed"
]
]
];


Now we can apply it 20 million times with NestList and it stills takes only about 2 seconds.



nsteps = 20000000;
xlist = Range[0., τ nsteps, τ];
Ylist = NestList[cStep, 1., 0., nsteps]; // AbsoluteTiming // First



2.08678




Edit



This can be sped up even more my avoiding NestList (the loop behind it can also be compiled which saves several calls to libraries) and by utilizing that the dimension of the ODE is known at compile time. For low dimensional systems, it may be also beneficial to avoid tensor operations altogether and to perform computations in scalar registers as done below.



τ = 0.01;
cFlow = Block[YY, Y, k1, k2, k3, k4, τ, Ylist, j,
YY = Table[Compile`GetElement[Ylist, j, i], i, 1, 2];
k1 = τ F[YY];
k2 = τ F[0.5 k1 + YY];
k3 = τ F[0.5 k2 + YY];
k4 = τ F[k3 + YY];
With[
code1 = (YY + (k1 + 2. (k2 + k3) + k4)/6)[[1]],
code2 = (YY + (k1 + 2. (k2 + k3) + k4)/6)[[2]]
,
Compile[Y0, _Real, 1, τ, _Real, n, _Integer,
Block[Ylist,
Ylist = Table[0., n + 1, Length[Y0]];
Ylist[[1]] = Y0;
Do[
Ylist[[j + 1, 1]] = code1;
Ylist[[j + 1, 2]] = code2;
,
j, 1, n];
Ylist
],
CompilationTarget -> "C", RuntimeOptions -> "Speed"
]
]
];
Ylist2 = cFlow[1., 0., τ, nsteps]; // AbsoluteTiming // First



1.06549




Don't be too upset by parts of the code being highlighted in red; this is on purpose.






share|improve this answer











$endgroup$








  • 1




    $begingroup$
    Damn, you definitely know how to use Mathematica A LOT more efficiently than I do. Thanks!
    $endgroup$
    – Shinaolord
    yesterday










  • $begingroup$
    You're welcome.
    $endgroup$
    – Henrik Schumacher
    yesterday










  • $begingroup$
    I'll have to play around with Compile, it definitely seems like a massive speed up if used correctly.
    $endgroup$
    – Shinaolord
    yesterday






  • 1




    $begingroup$
    This is exactly the kind of thing I like to show people when they complain about the slowness of Mathematica. Of course with some cleverness in vectorized operations Compile could probably be avoided altogether if one so desired.
    $endgroup$
    – b3m2a1
    18 hours ago











  • $begingroup$
    @b3m2a1 Yeah, right. However, with ODE systems of this small dimension (dim = 2) I am not sure how to utilize vectorization since the ODE has to be solved in serial and because the vector field F may be very nonlinear.
    $endgroup$
    – Henrik Schumacher
    18 hours ago










Your Answer





StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["$", "$"], ["\\(","\\)"]]);
);
);
, "mathjax-editing");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "387"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













draft saved

draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fmathematica.stackexchange.com%2fquestions%2f194002%2fways-to-speed-up-user-implemented-rk4%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









15












$begingroup$

Just to give you an impression how fast things may get when you use the right tools.



For given stepsize τ and given vector field F, this creates a CompiledFunction cStep that computes a single Runge-Kutta step



F = X [Function] -Indexed[X, 2], Indexed[X, 1];

τ = 0.01;
Block[YY, Y, k1, k2, k3, k4,

YY = Table[Compile`GetElement[Y, i], i, 1, 2];
k1 = τ F[YY];
k2 = τ F[0.5 k1 + YY];
k3 = τ F[0.5 k2 + YY];
k4 = τ F[k3 + YY];

cStep = With[code = YY + (k1 + 2. (k2 + k3) + k4)/6. ,
Compile[Y, _Real, 1,
code,
CompilationTarget -> "C",
RuntimeOptions -> "Speed"
]
]
];


Now we can apply it 20 million times with NestList and it stills takes only about 2 seconds.



nsteps = 20000000;
xlist = Range[0., τ nsteps, τ];
Ylist = NestList[cStep, 1., 0., nsteps]; // AbsoluteTiming // First



2.08678




Edit



This can be sped up even more my avoiding NestList (the loop behind it can also be compiled which saves several calls to libraries) and by utilizing that the dimension of the ODE is known at compile time. For low dimensional systems, it may be also beneficial to avoid tensor operations altogether and to perform computations in scalar registers as done below.



τ = 0.01;
cFlow = Block[YY, Y, k1, k2, k3, k4, τ, Ylist, j,
YY = Table[Compile`GetElement[Ylist, j, i], i, 1, 2];
k1 = τ F[YY];
k2 = τ F[0.5 k1 + YY];
k3 = τ F[0.5 k2 + YY];
k4 = τ F[k3 + YY];
With[
code1 = (YY + (k1 + 2. (k2 + k3) + k4)/6)[[1]],
code2 = (YY + (k1 + 2. (k2 + k3) + k4)/6)[[2]]
,
Compile[Y0, _Real, 1, τ, _Real, n, _Integer,
Block[Ylist,
Ylist = Table[0., n + 1, Length[Y0]];
Ylist[[1]] = Y0;
Do[
Ylist[[j + 1, 1]] = code1;
Ylist[[j + 1, 2]] = code2;
,
j, 1, n];
Ylist
],
CompilationTarget -> "C", RuntimeOptions -> "Speed"
]
]
];
Ylist2 = cFlow[1., 0., τ, nsteps]; // AbsoluteTiming // First



1.06549




Don't be too upset by parts of the code being highlighted in red; this is on purpose.






share|improve this answer











$endgroup$








  • 1




    $begingroup$
    Damn, you definitely know how to use Mathematica A LOT more efficiently than I do. Thanks!
    $endgroup$
    – Shinaolord
    yesterday










  • $begingroup$
    You're welcome.
    $endgroup$
    – Henrik Schumacher
    yesterday










  • $begingroup$
    I'll have to play around with Compile, it definitely seems like a massive speed up if used correctly.
    $endgroup$
    – Shinaolord
    yesterday






  • 1




    $begingroup$
    This is exactly the kind of thing I like to show people when they complain about the slowness of Mathematica. Of course with some cleverness in vectorized operations Compile could probably be avoided altogether if one so desired.
    $endgroup$
    – b3m2a1
    18 hours ago











  • $begingroup$
    @b3m2a1 Yeah, right. However, with ODE systems of this small dimension (dim = 2) I am not sure how to utilize vectorization since the ODE has to be solved in serial and because the vector field F may be very nonlinear.
    $endgroup$
    – Henrik Schumacher
    18 hours ago















15












$begingroup$

Just to give you an impression how fast things may get when you use the right tools.



For given stepsize τ and given vector field F, this creates a CompiledFunction cStep that computes a single Runge-Kutta step



F = X [Function] -Indexed[X, 2], Indexed[X, 1];

τ = 0.01;
Block[YY, Y, k1, k2, k3, k4,

YY = Table[Compile`GetElement[Y, i], i, 1, 2];
k1 = τ F[YY];
k2 = τ F[0.5 k1 + YY];
k3 = τ F[0.5 k2 + YY];
k4 = τ F[k3 + YY];

cStep = With[code = YY + (k1 + 2. (k2 + k3) + k4)/6. ,
Compile[Y, _Real, 1,
code,
CompilationTarget -> "C",
RuntimeOptions -> "Speed"
]
]
];


Now we can apply it 20 million times with NestList and it stills takes only about 2 seconds.



nsteps = 20000000;
xlist = Range[0., τ nsteps, τ];
Ylist = NestList[cStep, 1., 0., nsteps]; // AbsoluteTiming // First



2.08678




Edit



This can be sped up even more my avoiding NestList (the loop behind it can also be compiled which saves several calls to libraries) and by utilizing that the dimension of the ODE is known at compile time. For low dimensional systems, it may be also beneficial to avoid tensor operations altogether and to perform computations in scalar registers as done below.



τ = 0.01;
cFlow = Block[YY, Y, k1, k2, k3, k4, τ, Ylist, j,
YY = Table[Compile`GetElement[Ylist, j, i], i, 1, 2];
k1 = τ F[YY];
k2 = τ F[0.5 k1 + YY];
k3 = τ F[0.5 k2 + YY];
k4 = τ F[k3 + YY];
With[
code1 = (YY + (k1 + 2. (k2 + k3) + k4)/6)[[1]],
code2 = (YY + (k1 + 2. (k2 + k3) + k4)/6)[[2]]
,
Compile[Y0, _Real, 1, τ, _Real, n, _Integer,
Block[Ylist,
Ylist = Table[0., n + 1, Length[Y0]];
Ylist[[1]] = Y0;
Do[
Ylist[[j + 1, 1]] = code1;
Ylist[[j + 1, 2]] = code2;
,
j, 1, n];
Ylist
],
CompilationTarget -> "C", RuntimeOptions -> "Speed"
]
]
];
Ylist2 = cFlow[1., 0., τ, nsteps]; // AbsoluteTiming // First



1.06549




Don't be too upset by parts of the code being highlighted in red; this is on purpose.






share|improve this answer











$endgroup$








  • 1




    $begingroup$
    Damn, you definitely know how to use Mathematica A LOT more efficiently than I do. Thanks!
    $endgroup$
    – Shinaolord
    yesterday










  • $begingroup$
    You're welcome.
    $endgroup$
    – Henrik Schumacher
    yesterday










  • $begingroup$
    I'll have to play around with Compile, it definitely seems like a massive speed up if used correctly.
    $endgroup$
    – Shinaolord
    yesterday






  • 1




    $begingroup$
    This is exactly the kind of thing I like to show people when they complain about the slowness of Mathematica. Of course with some cleverness in vectorized operations Compile could probably be avoided altogether if one so desired.
    $endgroup$
    – b3m2a1
    18 hours ago











  • $begingroup$
    @b3m2a1 Yeah, right. However, with ODE systems of this small dimension (dim = 2) I am not sure how to utilize vectorization since the ODE has to be solved in serial and because the vector field F may be very nonlinear.
    $endgroup$
    – Henrik Schumacher
    18 hours ago













15












15








15





$begingroup$

Just to give you an impression how fast things may get when you use the right tools.



For given stepsize τ and given vector field F, this creates a CompiledFunction cStep that computes a single Runge-Kutta step



F = X [Function] -Indexed[X, 2], Indexed[X, 1];

τ = 0.01;
Block[YY, Y, k1, k2, k3, k4,

YY = Table[Compile`GetElement[Y, i], i, 1, 2];
k1 = τ F[YY];
k2 = τ F[0.5 k1 + YY];
k3 = τ F[0.5 k2 + YY];
k4 = τ F[k3 + YY];

cStep = With[code = YY + (k1 + 2. (k2 + k3) + k4)/6. ,
Compile[Y, _Real, 1,
code,
CompilationTarget -> "C",
RuntimeOptions -> "Speed"
]
]
];


Now we can apply it 20 million times with NestList and it stills takes only about 2 seconds.



nsteps = 20000000;
xlist = Range[0., τ nsteps, τ];
Ylist = NestList[cStep, 1., 0., nsteps]; // AbsoluteTiming // First



2.08678




Edit



This can be sped up even more my avoiding NestList (the loop behind it can also be compiled which saves several calls to libraries) and by utilizing that the dimension of the ODE is known at compile time. For low dimensional systems, it may be also beneficial to avoid tensor operations altogether and to perform computations in scalar registers as done below.



τ = 0.01;
cFlow = Block[YY, Y, k1, k2, k3, k4, τ, Ylist, j,
YY = Table[Compile`GetElement[Ylist, j, i], i, 1, 2];
k1 = τ F[YY];
k2 = τ F[0.5 k1 + YY];
k3 = τ F[0.5 k2 + YY];
k4 = τ F[k3 + YY];
With[
code1 = (YY + (k1 + 2. (k2 + k3) + k4)/6)[[1]],
code2 = (YY + (k1 + 2. (k2 + k3) + k4)/6)[[2]]
,
Compile[Y0, _Real, 1, τ, _Real, n, _Integer,
Block[Ylist,
Ylist = Table[0., n + 1, Length[Y0]];
Ylist[[1]] = Y0;
Do[
Ylist[[j + 1, 1]] = code1;
Ylist[[j + 1, 2]] = code2;
,
j, 1, n];
Ylist
],
CompilationTarget -> "C", RuntimeOptions -> "Speed"
]
]
];
Ylist2 = cFlow[1., 0., τ, nsteps]; // AbsoluteTiming // First



1.06549




Don't be too upset by parts of the code being highlighted in red; this is on purpose.






share|improve this answer











$endgroup$



Just to give you an impression how fast things may get when you use the right tools.



For given stepsize τ and given vector field F, this creates a CompiledFunction cStep that computes a single Runge-Kutta step



F = X [Function] -Indexed[X, 2], Indexed[X, 1];

τ = 0.01;
Block[YY, Y, k1, k2, k3, k4,

YY = Table[Compile`GetElement[Y, i], i, 1, 2];
k1 = τ F[YY];
k2 = τ F[0.5 k1 + YY];
k3 = τ F[0.5 k2 + YY];
k4 = τ F[k3 + YY];

cStep = With[code = YY + (k1 + 2. (k2 + k3) + k4)/6. ,
Compile[Y, _Real, 1,
code,
CompilationTarget -> "C",
RuntimeOptions -> "Speed"
]
]
];


Now we can apply it 20 million times with NestList and it stills takes only about 2 seconds.



nsteps = 20000000;
xlist = Range[0., τ nsteps, τ];
Ylist = NestList[cStep, 1., 0., nsteps]; // AbsoluteTiming // First



2.08678




Edit



This can be sped up even more my avoiding NestList (the loop behind it can also be compiled which saves several calls to libraries) and by utilizing that the dimension of the ODE is known at compile time. For low dimensional systems, it may be also beneficial to avoid tensor operations altogether and to perform computations in scalar registers as done below.



τ = 0.01;
cFlow = Block[YY, Y, k1, k2, k3, k4, τ, Ylist, j,
YY = Table[Compile`GetElement[Ylist, j, i], i, 1, 2];
k1 = τ F[YY];
k2 = τ F[0.5 k1 + YY];
k3 = τ F[0.5 k2 + YY];
k4 = τ F[k3 + YY];
With[
code1 = (YY + (k1 + 2. (k2 + k3) + k4)/6)[[1]],
code2 = (YY + (k1 + 2. (k2 + k3) + k4)/6)[[2]]
,
Compile[Y0, _Real, 1, τ, _Real, n, _Integer,
Block[Ylist,
Ylist = Table[0., n + 1, Length[Y0]];
Ylist[[1]] = Y0;
Do[
Ylist[[j + 1, 1]] = code1;
Ylist[[j + 1, 2]] = code2;
,
j, 1, n];
Ylist
],
CompilationTarget -> "C", RuntimeOptions -> "Speed"
]
]
];
Ylist2 = cFlow[1., 0., τ, nsteps]; // AbsoluteTiming // First



1.06549




Don't be too upset by parts of the code being highlighted in red; this is on purpose.







share|improve this answer














share|improve this answer



share|improve this answer








edited 12 hours ago

























answered yesterday









Henrik SchumacherHenrik Schumacher

58.1k580160




58.1k580160







  • 1




    $begingroup$
    Damn, you definitely know how to use Mathematica A LOT more efficiently than I do. Thanks!
    $endgroup$
    – Shinaolord
    yesterday










  • $begingroup$
    You're welcome.
    $endgroup$
    – Henrik Schumacher
    yesterday










  • $begingroup$
    I'll have to play around with Compile, it definitely seems like a massive speed up if used correctly.
    $endgroup$
    – Shinaolord
    yesterday






  • 1




    $begingroup$
    This is exactly the kind of thing I like to show people when they complain about the slowness of Mathematica. Of course with some cleverness in vectorized operations Compile could probably be avoided altogether if one so desired.
    $endgroup$
    – b3m2a1
    18 hours ago











  • $begingroup$
    @b3m2a1 Yeah, right. However, with ODE systems of this small dimension (dim = 2) I am not sure how to utilize vectorization since the ODE has to be solved in serial and because the vector field F may be very nonlinear.
    $endgroup$
    – Henrik Schumacher
    18 hours ago












  • 1




    $begingroup$
    Damn, you definitely know how to use Mathematica A LOT more efficiently than I do. Thanks!
    $endgroup$
    – Shinaolord
    yesterday










  • $begingroup$
    You're welcome.
    $endgroup$
    – Henrik Schumacher
    yesterday










  • $begingroup$
    I'll have to play around with Compile, it definitely seems like a massive speed up if used correctly.
    $endgroup$
    – Shinaolord
    yesterday






  • 1




    $begingroup$
    This is exactly the kind of thing I like to show people when they complain about the slowness of Mathematica. Of course with some cleverness in vectorized operations Compile could probably be avoided altogether if one so desired.
    $endgroup$
    – b3m2a1
    18 hours ago











  • $begingroup$
    @b3m2a1 Yeah, right. However, with ODE systems of this small dimension (dim = 2) I am not sure how to utilize vectorization since the ODE has to be solved in serial and because the vector field F may be very nonlinear.
    $endgroup$
    – Henrik Schumacher
    18 hours ago







1




1




$begingroup$
Damn, you definitely know how to use Mathematica A LOT more efficiently than I do. Thanks!
$endgroup$
– Shinaolord
yesterday




$begingroup$
Damn, you definitely know how to use Mathematica A LOT more efficiently than I do. Thanks!
$endgroup$
– Shinaolord
yesterday












$begingroup$
You're welcome.
$endgroup$
– Henrik Schumacher
yesterday




$begingroup$
You're welcome.
$endgroup$
– Henrik Schumacher
yesterday












$begingroup$
I'll have to play around with Compile, it definitely seems like a massive speed up if used correctly.
$endgroup$
– Shinaolord
yesterday




$begingroup$
I'll have to play around with Compile, it definitely seems like a massive speed up if used correctly.
$endgroup$
– Shinaolord
yesterday




1




1




$begingroup$
This is exactly the kind of thing I like to show people when they complain about the slowness of Mathematica. Of course with some cleverness in vectorized operations Compile could probably be avoided altogether if one so desired.
$endgroup$
– b3m2a1
18 hours ago





$begingroup$
This is exactly the kind of thing I like to show people when they complain about the slowness of Mathematica. Of course with some cleverness in vectorized operations Compile could probably be avoided altogether if one so desired.
$endgroup$
– b3m2a1
18 hours ago













$begingroup$
@b3m2a1 Yeah, right. However, with ODE systems of this small dimension (dim = 2) I am not sure how to utilize vectorization since the ODE has to be solved in serial and because the vector field F may be very nonlinear.
$endgroup$
– Henrik Schumacher
18 hours ago




$begingroup$
@b3m2a1 Yeah, right. However, with ODE systems of this small dimension (dim = 2) I am not sure how to utilize vectorization since the ODE has to be solved in serial and because the vector field F may be very nonlinear.
$endgroup$
– Henrik Schumacher
18 hours ago

















draft saved

draft discarded
















































Thanks for contributing an answer to Mathematica Stack Exchange!


  • Please be sure to answer the question. Provide details and share your research!

But avoid


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

Use MathJax to format equations. MathJax reference.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fmathematica.stackexchange.com%2fquestions%2f194002%2fways-to-speed-up-user-implemented-rk4%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Triangular numbers and gcdProving sum of a set is $0 pmod n$ if $n$ is odd, or $fracn2 pmod n$ if $n$ is even?Is greatest common divisor of two numbers really their smallest linear combination?GCD, LCM RelationshipProve a set of nonnegative integers with greatest common divisor 1 and closed under addition has all but finite many nonnegative integers.all pairs of a and b in an equation containing gcdTriangular Numbers Modulo $k$ - Hit All Values?Understanding the Existence and Uniqueness of the GCDGCD and LCM with logical symbolsThe greatest common divisor of two positive integers less than 100 is equal to 3. Their least common multiple is twelve times one of the integers.Suppose that for all integers $x$, $x|a$ and $x|b$ if and only if $x|c$. Then $c = gcd(a,b)$Which is the gcd of 2 numbers which are multiplied and the result is 600000?

Ingelân Ynhâld Etymology | Geografy | Skiednis | Polityk en bestjoer | Ekonomy | Demografy | Kultuer | Klimaat | Sjoch ek | Keppelings om utens | Boarnen, noaten en referinsjes Navigaasjemenuwww.gov.ukOffisjele webside fan it regear fan it Feriene KeninkrykOffisjele webside fan it Britske FerkearsburoNederlânsktalige ynformaasje fan it Britske FerkearsburoOffisjele webside fan English Heritage, de organisaasje dy't him ynset foar it behâld fan it Ingelske kultuergoedYnwennertallen fan alle Britske stêden út 'e folkstelling fan 2011Notes en References, op dizze sideEngland

Հադիս Բովանդակություն Անվանում և նշանակություն | Դասակարգում | Աղբյուրներ | Նավարկման ցանկ