How to deal with the condition that a function own many options?

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]]
]

• 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? Commented Jun 26, 2015 at 13:05
• @Mr.Wizard, OK, I will update my question and give a good example to make my question more clear.
– xyz
Commented Jun 28, 2015 at 3:41
• 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. Commented Jun 28, 2015 at 6: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
Commented Jun 28, 2015 at 7:51
• I don't see N[] anywhere in your code, so you will need to insert N[#, OptionValue[WorkingPrecision]] & in the appropriate places. Commented Jun 28, 2015 at 9:13

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]
]

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

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],

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