1

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]]
   ]
xyz
  • 605
  • 4
  • 38
  • 117
  • 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? – Mr.Wizard Jun 26 '15 at 13:05
  • @Mr.Wizard, OK, I will update my question and give a good example to make my question more clear. – xyz Jun 28 '15 at 03:41
  • 1
    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. – J. M.'s missing motivation Jun 28 '15 at 06:56
  • @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:) – xyz Jun 28 '15 at 07:51
  • I don't see N[] anywhere in your code, so you will need to insert N[#, OptionValue[WorkingPrecision]] & in the appropriate places. – J. M.'s missing motivation Jun 28 '15 at 09:13
  • @Guesswhoitis., If the precision of a number is MachinePrecision, then N[#, OptionValue[WorkingPrecision]] & will failed. – xyz Jun 28 '15 at 09:26
  • I don't understand what you mean. N[Pi, MachinePrecision] works as expected. N[20.5, 30] also works as expected. – J. M.'s missing motivation Jun 28 '15 at 09:36

2 Answers2

3

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]
 ]
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • +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:) – xyz Jan 11 '15 at 13:24
  • 1
    @ShutaoTang It is hard to address code that isn't there. I added a second section that perhaps gives you additional tools. – Mr.Wizard Jan 11 '15 at 13:27
  • It is first time for me to see the defintion like this: fun[][]:=...@Mr.Wizard, I am always learning:) – xyz Jan 11 '15 at 13:37
  • 1
    @ShutaoTang Please see these: (96), (544), (7999), – Mr.Wizard Jan 11 '15 at 13:39
1

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

xyz
  • 605
  • 4
  • 38
  • 117