TransWikia.com

Option value check with suboptions

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.

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP