What does “upvalue” mean in Mathematica and when to use them?

后端 未结 3 1421
猫巷女王i
猫巷女王i 2021-01-30 11:32

To me, g /: f[g[x_]] := h[x] is just verbose equivalent of f[g[x_]] := h[x]. Can you raise an example that you have to use /:?

3条回答
  •  滥情空心
    2021-01-30 12:03

    Actually, g /: f[g[x_]] := h[x] is not equivalent to f[g[x_]] := h[x]. The latter associates the definition with f, while TagSet (/:) and UpSet (^= and its delayed version, ^:=) associate the definition with g. This is a crucial difference and can be illustrated by a simple example. Let's say you want to have a set of variables that obey modulo 5 addition, i.e. 6 + 7 mod 5 = 3. So, we want anything with Head mod to behave correctly. Initially, we'd think that

    a_mod + b_mod := mod@Mod[a + b, 5]
    

    would work. But, it generates the error

    SetDelayed::write : Tag Plus in a_mod + b_mod is Protected.
    

    We could remove Unprotect Plus and our definition would then work, but this may cause problems with other definitions and as Plus accumulates more definitions, it would slow down. Alternatively, we can associate the addition property with the mod object itself via TagSet

    mod /: mod[a_] + mod[b_] := mod @ Mod[a + b, 5]
    

    or UpSetDelayed

    mod[a_] + mod[b_] ^:= mod @ Mod[a + b, 5]
    

    Setting an upvalue is somewhat more correct from a conceptual point of view since mod is the one with the different property.

    There are a couple of issues to be aware of. First, the upvalue mechanism can only scan one level deep, i.e. Plus[a_mod, b_mod] is fine, but Exp[Plus[a_mod, b_mod]] will throw an error. This may require you to get creative with an intermediate type. Secondly, from a coding perspective UpSetDelayed is easier to write, but occasionally there is some ambiguity as to which Head is the upvalue associated with. TagSet handles that by explicitly naming the appropriate Head, and in general, it is what I tend to prefer over UpSet.

    Some of Mathematica's operators do not have any behavior associated with them, so they're not protected. For these operators, you can define functions as you wish. For instance, I've defined

    a_ \[CircleTimes] b_ := KroneckerProduct[a,b]
    a_ \[CircleTimes] b_ \[CircleTimes] c__ := a \[CircleTimes] ( b \[CircleTimes] c )
    

    and

    a_ \[CirclePlus] b__ := BlockDiagonal[{a,b}]
    

    to provide convenient shorthand notations for matrix operations that I use a lot.

    My example above was a little contrived, but there are a number of times UpValues have come in handy. For example, I found that I needed a symbolic form for the complex roots of unity that behaved appropriately under multiplication and exponentiation.

    Example: A straightforward and useful example is marking a Symbol as Real:

    makeReal[a__Symbol] := (
         # /: Element[#, Reals] := True; 
         # /: Im[#] := 0; 
         # /: Re[#] := #;
         # /: Abs[#] := Sign[#] #;
         # /: Arg[#] := Piecewise[{{0, Sign[#] >= 0}, {Pi, Sign[#] < 0}}]
       ) & /@ List[a]
    

    Note the use of TagSet as Element[ a, Reals ] ^:= True would be ambiguous. What would the rule be attached to a or Reals? Also, if we wanted a positive real number, we could set Arg[#]:=0 which allows Simplify to behave as expected, e.g. Simplify[Sqrt[a^2]] == a.

提交回复
热议问题