Mathematica Asked on August 12, 2021
I am trying to adopt very interesting universal option check method. And I am completely lost since in one case in seems working, while in the other don’t. I am looking for explanation why it fails an possible ideas for improving the code below.
These are two functions. The first checks option values. If option has suboption, it is given in the form Method->{"Method1", SubOption1->"value1",..}
ClearAll[gaOptionCheck];
gaOptionCheck::invldopt =
"Option `1` for function `2` received invalid value `3`. Valid pattern is `1` -> `4`.";
gaOptionCheck[testFunction_] :=
Function[code,
Module[{tag, msg, catch, suggest},
msg = Function[{v, t},
Message[gaOptionCheck::invldopt, Sequence @@ t]; v];
catch = Function[c, Catch[c, _tag, msg], HoldAll];
(* add property for test function which has suboptions *)
testFunction[
optName_, {valch_String, suboptions__}] := (If[
testFunction[optName, valch], Print["Entering in suboptions"];
TrueQ[
AllTrue[MapAt[{valch, #} &,
List @@@ {suboptions}, {All,
1}], (If[testFunction @@ #, True,
Sow[suggest[optName] = testFunction@(#[[1]])];
False]) &]],
Sow[suggest[optName] = testFunction[optName]]; False]);
(*Taken from:https://mathematica.stackexchange.com/questions/
116623/how-to-check-the-validity-of-an-option-
value (Leonid Shifrin).*)
catch@
ReplaceAll[Unevaluated@code,
o : HoldPattern[OptionValue[f_, _, name_]] :>
With[{val = o},
If[! testFunction[name, val],
If[! ValueQ[suggest[name]],
Sow[suggest[name] = testFunction[name]]];
Throw[$Failed,
tag[name, f, val,
Sequence @@ Flatten[Reap[suggest[name]]]]],(*else*)val]]]],
HoldAll];
The second one sets the provided option value to the container, for example theOption["optionName"]=value1.
Options[gaSetNamedOptionValue] = {Quiet -> True};
testOptionForsetNamedOptionValue[Quiet] = "True|False";
testOptionForsetNamedOptionValue[Quiet, val_] :=
MatchQ[val, True | False];
gaSetNamedOptionValue[{commandName_, optionName_, containerName_},
optionValue_, OptionsPattern[]] :=
If[MatchQ[optionValue, $Failed],(* if option value is not valid,
do nothing*)Null,
(* case of complex option: if some of suboptions were set,
set them together with rest of suboptions *)
Module[{determinableSubOptions, complexSubOptionList =
DeleteDuplicatesBy[
List @@@
Flatten[{Cases[optionValue,
HoldPattern[_Rule | _RuleDelayed]],
Cases[optionName /. Options[commandName],
HoldPattern[_Rule | _RuleDelayed]]}, 1], First]},
Print[{"detecting option Value of", optionValue}];
If[complexSubOptionList =!= {},
determinableSubOptions =
MapAt[ToString, complexSubOptionList, {All, 1}]];
Switch[optionValue,
HoldPattern[{_String, ___?OptionQ}],
Set @@ {containerName[ToString[optionName]], optionValue[[1]]};
If[complexSubOptionList =!= {},
Set @@@ MapAt[containerName, determinableSubOptions, {All, 1}]],
(* case when option is simple or complex option when none of
suboptions were set *)
_,
Set @@ {containerName[ToString[optionName]], optionValue};
(* for option with suboption in addition we set all suboptions *)
If[
MatchQ[optionName /. Options[commandName],
HoldPattern[{_, __?OptionQ}]],
Set @@@ MapAt[containerName,
List @@@ Rest[optionName /. Options[commandName]], {All, 1}];
];
];
gaOptionCheck[testOptionForsetNamedOptionValue][
OptionValue[Quiet]];
If[! OptionValue[Quiet],
CellPrint[{TextCell[
Column[{Row[{TextCell[
"Options which are accessable to
gaSetNamedOptionValue[ ] are"]}],
Row[{ExpressionCell[determinableSubOptions]}],
Row[{TextCell["Their values are set to"]}],
Row[{ExpressionCell[Definition[containerName]]}]}]]}];
];
];
];
Now for rather complicated case, when I test the complex option validity of different command it works as expected, for example
Options[gaProductPairExpand] = {CoefficientFunction -> Expand,
Quiet -> True,
Method -> {"RealTimePairProduct", MaxIterations -> Infinity}};
ClearAll[testOptionProductPairExpand];
(* simple option case *)
testOptionProductPairExpand[CoefficientFunction] =
Expand | Identity |
"any transformation (i.e with head Function[]) of expression";
testOptionProductPairExpand[Quiet] = "True" | "False";
(* two argument cal is used for actual check*)
testOptionProductPairExpand[CoefficientFunction,
val_Symbol | val_Function] :=
MatchQ[val, Expand | Identity | _Function];
testOptionProductPairExpand[Quiet, val_] := BooleanQ[val];
(* case of option which may have suboptions *)
(* single argument
call is used to print valid option pattern for user*)
testOptionProductPairExpand[Method] =
"ConvolutionPairProduct ()" |
"RealTimePairProduct (MaxIterations)" |
"PrecomputedPairProduct (OmitGrades)";
testOptionProductPairExpand[{"RealTimePairProduct", MaxIterations}] =
HoldForm[{"RealTimePairProduct",
MaxIterations -> Alternatives[Infinity, "Non negative integer"]}];
testOptionProductPairExpand[{"PrecomputedPairProduct", OmitGrades}] =
HoldForm[{"PrecomputedPairProduct",
OmitGrades -> Alternatives[{}, "list of nonnegative integers"]}];
(* if option has suboptions, test function should be defined for each
suboption*)
testOptionProductPairExpand[Method, val_String | {val_String}] :=
MatchQ[val,
"ConvolutionPairProduct" | "RealTimePairProduct" |
"PrecomputedPairProduct"];
testOptionProductPairExpand[{"RealTimePairProduct", MaxIterations},
val_] := MatchQ[val, Alternatives[Infinity, _Integer?NonNegative]];
testOptionProductPairExpand[{"PrecomputedPairProduct", OmitGrades},
val_] := MatchQ[val, {___Integer?NonNegative}];
Options[gaProductExpand] = {Except -> {},
"IncludeProductPairExpandOptionValues" -> True};
(* simple option case *)
testOptionProductExpand["Except"] =
"{GeometricProduct,OuterProduct,InnerProduct,LeftContract,
RightContract} in any combination";
testOptionProductExpand["IncludeProductPairExpandOptionValues"] =
"True | False";
(* two argument cal is used for actual check*)
testOptionProductExpand["Except", val_Symbol | val_List] :=
ContainsAll[{GeometricProduct, OuterProduct, InnerProduct,
LeftContract, RightContract}, Flatten[{val}]];
testOptionProductExpand["IncludeProductPairExpandOptionValues",
val_Symbol] := BooleanQ[val];
SetAttributes[gaProductExpand, {Listable}];
gaProductExpand[expression_,
op : OptionsPattern[{gaProductExpand, gaProductPairExpand}]] :=
Module[{exceptHeads =
gaOptionCheck[testOptionProductExpand][
OptionValue[gaProductExpand, "Except"]],
includeExplicitOptionValues =
gaOptionCheck[testOptionProductExpand][
OptionValue[gaProductExpand,
"IncludeProductPairExpandOptionValues"]],
allProductPairExpandOptions =
FilterRules[{op}, Options[gaProductPairExpand]], theOption,
restOpts, trueHeads, trueHeadsHeads, trueHeadsBlanks},
(*set values of all options: exeptional usage,
we set options for gaProductPairExpand not gaProductExpand*)
If[includeExplicitOptionValues,
gaSetNamedOptionValue[{gaProductPairExpand, #, theOption},
gaOptionCheck[testOptionProductPairExpand][OptionValue[#]],
Quiet -> True
] & /@ (First /@ allProductPairExpandOptions);
If[Head[theOption["Method"]] === theOption,
theOption["Method"] = "RealTimePairProduct"];
If[Head[theOption["MaxIterations"]] === theOption,
theOption["MaxIterations"] = Infinity];
If[Head[theOption["OmitGrades"]] === theOption,
theOption["OmitGrades"] = {}];
If[Head[theOption["CoefficientFunction"]] === theOption,
theOption["CoefficientFunction"] = Expand];
If[Head[theOption["Quiet"]] === theOption,
theOption["Quiet"] = True];
restOpts = {theOption["CoefficientFunction"], theOption["Quiet"],
theOption["Method"], theOption["MaxIterations"],
theOption["OmitGrades"]},
(* old behaviour,
when gaProductPairExpand evaluates options itself *)
restOpts = Sequence @@ allProductPairExpandOptions
];
]
Check:
gaProductExpand[any, Except -> {GeometricProduct, OuterProduct},
Method -> {"RealTimePairProduct", MaxIterations -> 1}]
Out:
Entering in suboptions
{detecting option Value of,{RealTimePairProduct,MaxIterations->1}}
And for wrong input it produces what I want:
gaProductExpand[any, Except -> {GeometricProduct, OuterProduct},
Method -> {"RealTimePairProduct", MaxIterations -> a}]
Out:Entering in suboptions
gaOptionCheck::invldopt: Option Method for function {gaProductExpand,gaProductPairExpand} received invalid value {RealTimePairProduct,MaxIterations->a}. Valid pattern is Method -> {RealTimePairProduct,MaxIterations->[Infinity]|Non negative integer}.
However the seemingly simple case fails and few days I cannot think why.
Here is very similar code:
Options[gaDeterminantOfMV] = {Method -> Automatic,
Expand -> Automatic, Quiet -> False};
(*Method[Rule]{"Involutions",gaDeterminantFormula[Rule]"Recursive",
Direction[Rule]"Left"} *)
(* case of option which may have suboptions *)
(* single argument
call is used to print valid option pattern for user*)
testOptionDeterminantOfMV[Method] =
Alternatives[Automatic,
"Involutions (gaDeterminantFormula, Direction)", "Det()"];
testOptionDeterminantOfMV[{"Involutions", gaDeterminantFormula}] =
HoldForm[{"Involutions",
gaDeterminantFormula ->
Alternatives["Recursive", "Explicit", "Optimal"]}];
testOptionDeterminantOfMV[{"Involutions", Direction}] =
HoldForm[{"Involutions",
Direction -> Alternatives["Left", "Right"]}];
(* if option has suboptions, test function should be defined for each
suboption*)
testOptionDeterminantOfMV[Method, val_] :=
MatchQ[val, Alternatives[Automatic, "Involutions", "Det"]];
testOptionDeterminantOfMV[Method, {val_String, ___?OptionQ}] :=
MatchQ[val, Alternatives["Involutions", "Det"]];
testOptionDeterminantOfMV[{"Involutions", gaDeterminantFormula},
val_] := MatchQ[val,
Alternatives["Recursive", "Explicit", "Optimal"]];
testOptionDeterminantOfMV[{"Involutions", Direction}, val_] :=
MatchQ[val, Alternatives["Left", "Right"]];
(* case of simple option *)
testOptionDeterminantOfMV[Expand] =
Automatic | Expand | Identity |
"any transformation (i.e with head Function[]) of expression";
testOptionDeterminantOfMV[Quiet] = "True" | "False";
(* two argument cal is used for actual check*)
testOptionDeterminantOfMV[Expand, val_Symbol | val_Function] :=
MatchQ[val, Automatic | Expand | Identity | _Function];
testOptionDeterminantOfMV[Quiet, val_] := BooleanQ[val];
gaDeterminantOfMV::unknownMethod =
"Determinant computation method Method->`1` is unknown. Possible
values are Automatic, Involutions and Det. Method[Rule]Det will
first calculate matrix representation of MV. The default is to use
Method->Involutions for algebras with p+q<=6 and Method[Rule]Det in
all other cases.";
gaDeterminantOfMV::OptimalMethod =
"Explicit optimized formulas are known for dimension
n[LessEqual]6. Your algebra has dimension `1`. Switching suboption
gaDeterminantFormula->"Explicit".";
gaDeterminantOfMV::fail =
"Failed to replace quaternion entries by complex 2x2 matrices in
`1`.";
gaDeterminantOfMV::unexpectedDim =
"The calculated matrix dimension `1`.";
gaDeterminantOfMV[expr_, op : OptionsPattern[{gaDeterminantOfMV}]] :=
If[False,
"Skip branch",
Module[{theOption, allOptions = Options[gaDeterminantOfMV], tt},
Print[{op}];
(*gaSetNamedOptionValue[{gaDeterminantOfMV,#,theOption},
gaOptionCheck[testOptionDeterminantOfMV][OptionValue[#]],
Quiet[Rule]True
]&/@(First/@allOptions);*)
gaSetNamedOptionValue[{gaDeterminantOfMV, #, theOption},
gaOptionCheck[testOptionDeterminantOfMV][OptionValue[#]],
Quiet -> False
] & /@ (Flatten[First /@ {op}]);
(* check if main options obtained values *)
If[Head[theOption["Method"]] === theOption,
theOption["Method"] = Automatic];
If[Head[theOption["Expand"]] === theOption,
theOption["Expand"] = Automatic];
If[Head[theOption["Quiet"]] === theOption,
theOption["Quiet"] = False];
{theOption["Method"], theOption["gaDeterminantFormula"],
theOption["Direction"], theOption["Expand"], theOption["Quiet"]}
]
];
It even don’t enter suboption checking part:
gaDeterminantOfMV[expr,
Method -> {"Involutions", gaDeterminantFormula -> "Explicit1",
Expand -> "None"}]
Out:
{Method->{Involutions,gaDeterminantFormula->Explicit1,Expand->None}}
{detecting option Value of,{Involutions,gaDeterminantFormula->Explicit1,Expand->None}}
Options which are accessable to gaSetNamedOptionValue[ ] are
{{"gaDeterminantFormula","Explicit1"},{"Expand","None"}}
Their values are set to
Attributes[theOption$2725]={Temporary}
theOption$2725["Expand"]="None"
theOption$2725["gaDeterminantFormula"]="Explicit1"
theOption$2725["Method"]="Involutions"
Since I gave bad value gaDeterminantFormula->"Explicit1" (instead "Explicit") it should be able to detect that.
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP