Last week I said I would provide a discussion of UpValues once I resolved
some subtle details. Well I still haven't resolved the details, but I will
tell you all that I know on the subject.
Suppose you wanted to make it so the kernel would
automatically use the simplification:
Cosh[z]+Sinh[z]-->Exp[z]
Below I show different ways of doing this.
First we should Unprotect the symbols we will work with.
---------------------------
In[1]:=
Unprotect[Plus,Sinh,Cosh];
The line below will use the identity for Cosh[z]+Sinh[z] and associate the
definition with Plus which is apparent when you evaluate (??Plus ). When
the identity is associated with Plus the kernel will take more time to
perform addition. Actually in Versions 3 and 4 Plus (Times as well) apply
built-in rules before user defined rules. As far as I am aware all other
kernel functions apply user defined rules first. So to put it more
correctly (I hope); when the identity below is used the kernel will check to
see if this identity can be used whenever an expression has the head Plus
after using all built-in rules for Plus.
For example the kernel will not try the identity below on
(4+2/3), but it will try it on (4+x).
----------------------
In[2]:=
Cosh[z_]+Sinh[z_]:=Exp[z];
??Plus
(* Plus information includes the identity above. *)
The identity above was stored in DownValues[Plus]. We should clear out the
DownValue from Plus before moving on. The output of (??Plus ) proves the
identity was removed.
In[4]:=
DownValues[Plus]={};
??Plus
(* The identity was removed from Plus information. *)
-----------------------
Instead we can associate the identity with (Sinh) using the code in the next
line. The "Sinh/:" part of the code says to only associate the definition
with Sinh. When done this way the use of this identity will have no impact
on how fast the kernel evaluates expressions with the Head Plus or Cosh.
But this will have an impact on how fast expressions with the Head Sinh
evaluate. This is likely a better approach since (Sinh) is used much less
often than addition. However, it isn't clear to me how much of an impact it
would have on the evaluation of Sinh[_]. Anyway that is a very practical
application of UpValues.
The result of evaluating (??Sinh ), (??Cosh ), (??Plus ) will show the
identity is associated with Sinh but not Cosh or Plus.
In[6]:=
Sinh/:Cosh[z_]+Sinh[z_]:=Exp[z];
??Sinh
(* Sinh information includes the identity above. *)
In[8]:=
??Cosh
(* Cosh information doesn't include the identity above. *)
In[9]:=
??Plus
(* Plus information doesn't include the identity above. *)
The identity above was stored in UpValues[Sinh]. We should clear out the
UpValue before moving on. The output of (??Sinh ) proves that it was
removed.
In[10]:=
UpValues[Sinh]={};
??Sinh
(* Sinh information doesn't include the identity above. *)
------------------------
We could also use the next line to associate the identity with both (Sinh)
and (Cosh), but not (Plus). This is clear from the result of evaluating
(??Sinh ), (??Cosh ), (??Plus ). In this case the identity is stored in
DownValues[Sinh] and DownValues[Cosh]. When this method is used both (Sinh)
and (Cosh) will run slower.
In[11]:=
Cosh[z_]+Sinh[z_]^:=Exp[z];
??Sinh
(* Sinh information does include the identity above. *)
In[12]:=
??Cosh
(* Cosh information does include the identity above. *)
In[13]:=
??Plus
(* Plus information doesn't include the identity above. *)
--------------------
Now suppose you wanted the kernel to automatically use the identity
Sin[z]^2 + Cos[z]^2 -->1
Notice both (Sin[z]^2) and (Cos[z]^2) have the Head Power.
You could give Plus a DownValue using:
Sin[z_]^2 + Cos[z_]^2 :=1
Actually the next line also works just as well since (z) isn't used on the
right side. Let me know if you want to know why it matters if (z) is used on
the right side.
Sin[z_]^2 + Cos[z_]^2 =1
Instead you could use an UpValue for Power using either of the next four
lines.
Sin[z_]^2 + Cos[z_]^2 ^:=1
Sin[z_]^2 + Cos[z_]^2 ^=1
Power/:Sin[z_]^2 + Cos[z_]^2 :=1
Power/:Sin[z_]^2 + Cos[z_]^2 =1
Let me know if you want me to elaborate on the differences between
lhs^:=rhs
lhs^=rhs
f/:lhs:=rhs
f/:lhs=rhs
BUT Mathematica will not let you associate the definition above with (Sin)
or (Cos). You might be tempted to use one of the following, but they will
not work because (Sin) and (Cos) are too deep in the expression.
Sin/:( Sin[z_]^2 +Cos[z_]^2 )=1
Cos/:( Sin[z_]^2 +Cos[z_]^2 )=1
Sin/:( Sin[z_]^2 +Cos[z_]^2 ):=1
Cos/:( Sin[z_]^2 +Cos[z_]^2 ):=1
When you evaluate something with the form
f/:lhs^=rhs
(f) must be a symbol, and lhs[[n,0]] must return (f) for some integer (n).
The same thing goes for f/:lhs^:=rhs
In the case where ( lhs = Sin[z_]^2+Cos[z_]^2 )
lhs[[1,1,0]]-->Cos
lhs[[2,1,0]]-->Sin
You see both Sin and Cos are in so deep that the identity can't be
associated with either Sin or Cos.
--------------------
When you use
lhs^:=rhs
An UpValue is given to every symbol that can be accessed using
lhs[[n,0]] for any integer (n).
For example when you use lhs^:=rhs
with ( lhs=Cosh[z_]+Sinh[z_] )
lhs[[1,0]]-->Cosh
lhs[[2,0]]-->Sinh
so an UpValue is given to both Cosh, Sinh.
---------------------
Before continuing you might want to evaluate the next line to put everything
back to normal.
In[13]:=
DownValues[Plus]={};
UpValues[Cosh]={};
UpValues[Sinh]={};
Protect[Plus,Cosh,Sinh];
--------------------
Regards,
Ted Ersek
For Mathematica tips, tricks see
http://www.dot.net.au/~elisha/ersek/Tricks.html