1
$\begingroup$

Assming that I have a function myFunc which has some options.

Options[myFunc]={ firstOpt->1, secondOpt->"A", thirdOpt->True };

where, I set the values of firstOpt to 1 or 2,and set the value secondOpt to "A" or "B".

myFunc[arg1_,arg2_,OptionsPattern[]]:=
 Module[{method},
  method= OptionValue/@{firstOpt,secondOpt,thirdOpt};
  Switch[
   method,
   {1,"A",True},subFunc1[...],
   {2,"A",True},subFunc2[...],
   {1,"B",True},subFunc3[...],
   {2,"B",True},subFunc4[...],
   {1,"A",False},subFunc4[...],
   {2,"A",False},subFunc5[...],
   {1,"B",False},subFunc6[...],
   {2,"B",False},subFunc7[...],
  ]
 ]

Obviously, this is a fussy and awkward solution.So I would like to know how to deal with condition when myFunc has many options.

Or is it possible to know Mathematica how to deal with many options? For instance,

 Length@Options@ArrayPlot
 (*48*)

An example(Implementation of Runge-Kutta Algorithm)

 (*MiddlePoint formula*)
 middlePointOrderTwo[{xn_, yn_}, step_, func_] :=
  Module[{K1, K2},
   K1 = func[xn, yn];
   K2 = func[xn + 1/2 step, yn + 1/2 step K1];
   {xn + step, yn + step K2}
 ]
 (*Henu formula of order 2*)
 henuOrderTwo[{xn_, yn_}, step_, func_] :=
  Module[{K1, K2},
   K1 = func[xn, yn];
   K2 = func[xn + 2/3 step, yn + 2/3 step K1];
   {xn + step, yn + 1/4 step (K1 + 3 K2)}
 ]
 (*Henu formula of order 2*)
 henuOrderThree[{xn_, yn_}, step_, func_] :=
  Module[{K1, K2, K3},
   K1 = func[xn, yn];
   K2 = func[xn + 1/3 step, yn + 1/3 step K1];
   K3 = func[xn + 2/3 step, yn + 2/3 step K2];
  {xn + step, yn + 1/4 step (K1 + 3 K3)}
 ]
 (*Kutta formula of order 3*)
 kuttaOrderThree[{xn_, yn_}, step_, func_] :=
  Module[{K1, K2, K3},
   K1 = func[xn, yn];
   K2 = func[xn + 1/2 step, yn + 1/2 step K1];
   K3 = func[xn + step, yn - step K1 + 2 step K2];
   {xn + step, yn + 1/6 step (K1 + 4 K2 + K3)}
 ]
 (*Runge-Kutta formula of order 4*)    
 rungeKuttaOrderFour[{xn_, yn_}, step_, func_] :=
  Module[{K1, K2, K3, K4},
   K1 = func[xn, yn];
   K2 = func[xn + 1/2 step, yn + 1/2 step K1];
   K3 = func[xn + 1/2 step, yn + 1/2 step K2];
   K4 = func[xn + step, yn + step K3];
   {xn + step, yn + 1/6 step (K1 + 2 K2 + 2 K3 + K4)}
  ]

  rungeKuttaFormula[{a_, b_}, ya_, step_, func_, OptionsPattern[]] :=
   Module[{OrderMethod, num},
    OrderMethod = OptionValue[SolvingOrderMethod];
    num = IntegerPart[(b - a)/step];
    Switch[
     OrderMethod,
     {2, "Henu"},
     NestList[
      henuOrderTwo[#, step, func] &, {a, ya}, num],
     {2, "MiddlePoint"},
     NestList[
      middlePointOrderTwo[#, step, func] &, {a, ya}, num],
     {3, "Henu"},
     NestList[
      henuOrderThree[#, step, func] &, {a, ya}, num],
     {3, "Kutta"},
     NestList[
      kuttaOrderThree[#, step, func] &, {a, ya}, num],
     {4, "RungeKutta"},
     NestList[
      rungeKuttaOrderFour[#, step, func] &, {a, ya}, num]]
   ]
$\endgroup$
  • $\begingroup$ I notice that you did not Accept my answer. I realize I did not give the answer you hoped for but I also found it difficult because you did not give other examples. Now that you have more experience with Mathematica perhaps you can update the question and I can try again? $\endgroup$ – Mr.Wizard Jun 26 '15 at 13:05
  • $\begingroup$ @Mr.Wizard, OK, I will update my question and give a good example to make my question more clear. $\endgroup$ – xyz Jun 28 '15 at 3:41
  • 1
    $\begingroup$ Can't give this a detailed look at the moment, but here's a suggestion: since you're implementing the various flavors of Runge-Kutta, the most maintainable way of going about it is to store each method as its Butcher table, and then write a general routine that parses the Butcher table and produces the required updates for the next step of the integration. $\endgroup$ – J. M. will be back soon Jun 28 '15 at 6:56
  • $\begingroup$ @Guesswhoitis., Dear J.M. I'd like to add a option WorkingPrecision in the RKSolve like the built-in NSolve. For instane NSolve[x^2 + 2 x - 2 == 0, x, WorkingPrecision -> 20] gives the result {{x -> -2.7320508075688772935}, {x -> 0.73205080756887729353}}. However, I didn't know how to implement it. Could you give me some suggestions or hints. Thanks:) $\endgroup$ – xyz Jun 28 '15 at 7:51
  • $\begingroup$ I don't see N[] anywhere in your code, so you will need to insert N[#, OptionValue[WorkingPrecision]] & in the appropriate places. $\endgroup$ – J. M. will be back soon Jun 28 '15 at 9:13
3
$\begingroup$

It is easiest when each options controls something that is more or less independent of other options. If as in your example each combination of options results in (the need for) a different subroutine things do get complicated.

A basic strategy is to look for repetitions segments of code an replace them with a single copy. For example in your rungeKuttaFormula code every option combination results in the same call:

NestList[subfn[#, step, func] &, {a, ya}, num]

The only variation being subfn. Therefore I would refactor it into something like:

rungeKuttaFormula[{a_, b_}, ya_, step_, func_, OptionsPattern[]] :=
 Module[{num, subfn},
  num = IntegerPart[(b - a)/step];
  subfn =
   Switch[OptionValue[SolvingOrderMethod],
    {2, "Henu"},        henuOrderTwo,
    {2, "MiddlePoint"}, middlePointOrderTwo,
    {3, "Henu"},        henuOrderThree,
    {3, "Kutta"},       kuttaOrderThree,
    {4, "RungeKutta"},  rungeKuttaOrderFour,
    _,                  Return[$Failed, Module]
   ];
  NestList[subfn[#, step, func] &, {a, ya}, num]
 ]

(For important code that would be reused I would test the Option value as part of the argument test and issue a Message if the value was not acceptable, rather than returning $Failed. Reference: How to program a F::argx message?)


A second level of refinement would be to make each method its own function with a parameter for the order:

henu[2][{xn_, yn_}, step_, func_] :=
 Module[{K1, K2},
  K1 = func[xn, yn];
  K2 = func[xn + 2/3 step, yn + 2/3 step K1];
  {xn + step, yn + 1/4 step (K1 + 3 K2)}
 ]

henu[3][{xn_, yn_}, step_, func_] :=
 Module[{K1, K2, K3},
  K1 = func[xn, yn];
  K2 = func[xn + 1/3 step, yn + 1/3 step K1];
  K3 = func[xn + 2/3 step, yn + 2/3 step K2];
  {xn + step, yn + 1/4 step (K1 + 3 K3)}
 ]

Calling these is then easier, so our code would look something like:

rungeKuttaFormula[{a_, b_}, ya_, step_, func_, OptionsPattern[]] :=
 Module[{num, subfn, ord, method},
  num = IntegerPart[(b - a)/step];
  {ord, method} = OptionValue[SolvingOrderMethod];
  subfn =
   method /. {
     "Henu"        -> henu,
     "MiddlePoint" -> middlePoint,
     "Kutta"       -> kutta,
     "RungeKutta"  -> rungeKutta
     };
  NestList[subfn[ord][#, step, func] &, {a, ya}, num]
 ]
$\endgroup$
  • $\begingroup$ +1,Dear Mr.Wizard, It's indeed a better solution to refactor my example of Runge-Kutta algorithm. In fact, in many cases, the call of subroutine is different.So I'd like to know that is there a good strategy to deal with that condition.Thanks a lot:) $\endgroup$ – xyz Jan 11 '15 at 13:24
  • 1
    $\begingroup$ @ShutaoTang It is hard to address code that isn't there. I added a second section that perhaps gives you additional tools. $\endgroup$ – Mr.Wizard Jan 11 '15 at 13:27
  • $\begingroup$ It is first time for me to see the defintion like this: fun[][]:=...@Mr.Wizard, I am always learning:) $\endgroup$ – xyz Jan 11 '15 at 13:37
  • 1
    $\begingroup$ @ShutaoTang Please see these: (96), (544), (7999), $\endgroup$ – Mr.Wizard Jan 11 '15 at 13:39
1
$\begingroup$

Runge-Kutta Formula

enter image description here enter image description here enter image description here

Implementation

Options[RKSolve] = {Method -> Automatic, WorkingPrecision -> MachinePrecision};

RKSolve::badmeth = "`1` is not a valid value of option `2`";

RKSolve[func_, {a_, b_, h_: .1}, ya_, opts : OptionsPattern[]] := 
 Module[{num, method, subfn, order},
  method = Method /. {opts} /. Options[RKSolve];
  If[
   ! MemberQ[
    {{"Euler", 2}, {"Midpoint", 2}, {"Heun", 2}, {"Heun", 3},
     {"Kutta", 3}, {"RK", 4}, {"Gill", 4}, Automatic}, method],
    Message[RKSolve::badmeth, method, Method]; Return[$Failed]];

  If[method === Automatic, method = {"RK", 4}];
  {subfn, order} = ToExpression[method];
  num = Round[(b - a)/h];
  NestList[
   subfn[order][func, #, h] &, {a, ya}, num]
]

Auxiliary function

Using the suggestion comes from @Mr.Wizard

(*Order 2*)
Euler[2][func_, {x_, y_}, h_] :=
 With[
  {K = FoldList[func[x + #2, y + #2 #1] &, func[x, y], {h}]},
  {x + h, y + K.{1, 1} h/2}
 ]

Midpoint[2][func_, {x_, y_}, h_] :=
 With[
  {K = FoldList[func[x + #2, y + #2 #1] &, func[x, y], {h/2}]},
  {x + h, y + K.{0, 1} }
 ]

Heun[2][func_, {x_, y_}, h_] :=
 With[
  {K = FoldList[func[x + #2, y + #2 #1] &, func[x, y], {2/3 h}]},
  {x + h, y + K.{1, 3} h/4}
 ]

(*Order 3*)
Heun[3][func_, {x_, y_}, h_] :=
 With[
  {K =
    FoldList[func[x + #2, y + #2 #1] &, func[x, y], {2/3 h, 2/3 h}]},
  {x + h, y + K.{1, 0, 3} h/4}
 ]

Kutta[3][func_, {x_, y_}, h_] :=
 Module[{K1, K2, K3},
  K1 = func[x, y];
  K2 = func[x + 1/2 h, y + 1/2 h K1];
  K3 = func[x + h, y - h K1 + 2 h K2];
  {x + h, y + 1/6 h (K1 + 4 K2 + K3)}
 ]

(*Order 4*)
RK[4][func_, {x_, y_}, h_] :=
 With[
  {K =
    FoldList[
     func[x + #2, y + #2 #1] &, func[x, y], {h/2, h/2, h}]},
  {x + h, y + K.{1, 2, 2, 1} h/6}
 ]

Gill[4][func_, {xn_, yn_}, h_] :=
 Module[{K1, K2, K3, K4},
  K1 = func[xn, yn];
  K2 = func[xn + 1/2 h, yn + 1/2 h K1];
  K3 = func[xn + 1/2 h, 
    yn + (Sqrt[2] - 1)/2 h K1 + (2 - Sqrt[2])/2 h K2];
  K4 = func[xn + h, yn - Sqrt[2]/2 h K2 + (1 + Sqrt[2]/2) h K3];
  {xn + h, yn + 1/6 h (K1 + (2 - Sqrt[2]) K2 + (2 + Sqrt[2]) K3 + K4)}
 ] 

Limitation

I didn't know how to deal with the option WorkingPrecision -> MachinePrecision in RKSolve

$\endgroup$

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.