Re: Complex Default
- To: mathgroup at smc.vnet.net
- Subject: [mg3036] Re: Complex Default
- From: danl (Daniel Lichtblau)
- Date: Thu, 25 Jan 1996 03:20:15 -0500
- Organization: Wolfram Research, Inc.
In article <4e4nd8$6pj at dragonfly.wri.com> evans at gte.net (Mark Evans)
writes:
> The following was a suggestion I sent to Wolfram Research some time ago.
> I am curious what other people think of it.
>
> Mark Evans
> evans at gte.net
>
>
> Gentlemen:
>
> This suggestion relates to the use of complex numbers in Mathematica.
>
> Mathematica should incorporate a global user setting that shuts off the
> implicit assumption that undefined variables are complex numbers. When
> used, this preference setting would mean that instead of
>
> In[1]:=
> realVar
> Out[1]=
> realVar
> In[2]:=
> Re[realVar]
> Out[2]=
> Re[realVar]
> In[3]:=
> Im[realVar]
> Out[3]=
> Im[realVar]
> In[4]:=
> Arg[realVar]
> Out[4]=
> Arg[realVar]
> In[5]:=
> Abs[realVar]
> Out[5]=
> Abs[realVar]
>
>
> we would get some simplifications with $ComplexDefault = False:
>
>
> In[1]:=
> realVar
> Out[1]=
> realVar
> In[2]:=
> Re[realVar] (* known *)
> Out[2]=
> realVar
> In[3]:=
> Im[realVar] (* known *)
> Out[3]=
> 0
> In[4]:=
> Arg[realVar] (* known *)
> Out[4]=
> 0
> In[5]:=
> Abs[realVar] (* could be +/- realVar; leave unknown *)
> Out[5]=
> Abs[realVar]
>
>
> I think you would implement this as a system variable, say
$ComplexDefault
> = [True | False]. The above outputs would correspond to $ComplexDefault
=
> True and $ComplexDefault = False, respectively.
>
> It turns out that even when working with phasors, all of my undefined
> variables are real. Expressions only become complex when I personally
> insert the imaginary unit. These insertions are the only occurrence of
> the imaginary unit in all of the computations.
>
> Under such circumstances, I have been somewhat frustrated by
Mathematica's
> refusal to simplify expressions on the chance that a complex number
might
> be hiding behind symbols I know to be real. Try, for example,
> Conjugate[Exp[I x]]. Here was an interesting variant:
>
> In[44]:=
> Timing[Conjugate[Exp[I (kx^2 + ky^2)]]]
> Out[44]=
> 2 2
> I (kx + ky )
> {12.05 Second, Conjugate[E ]}
>
> Thus on an PowerMac 8100/80 MHz, it took Mathematica 12 seconds to find
> out that it did not know how to negate the minus sign in a very simple
> complex exponential. This was with Roman Maeder's "ReIm" package
already
> loaded. When I defined kx and ky to be real per his package, the time
was
> reduced to 3.5 seconds; still a long time for such a simple thing. This
> expression is like 2+2. It should be a snap.
>
> I suspect this is a problem in the start-up code that pattern-matches
the
> form of the input. This is precisely my point, and it obtains more
> generally in Mathematica: we want to keep general-purpose symbolic
code,
> but we would also like to see greater convenience in manipulation of
> expressions with known character. I have perused the ComplexExpand.m
> package and find that it is encumbered with far too much generality for
my
> purposes. If you experiment with (symbolic) complex numbers, using
> ComplexExpand[], Conjugate[], Re[], and so on, you will find that even
> when it is possible to get a final answer, the wait is very long.
>
Regarding the handling of Conjugate[Exp[I (kx^2 + ky^2)]], the fact that
it takes so long is a bug, corrected in our development version. It should
and will be quite fast. Note that ComplexExpand handles it quickly. The
following was run on a NeXT turbo.
In[3]:= ComplexExpand[Conjugate[Exp[I (kx^2 + ky^2)]]] // Timing //
InputForm
Out[3]//InputForm=
{0.1166666666666662*Second, Cos[kx^2 + ky^2] - I*Sin[kx^2 + ky^2]}
> I have also examined Roman Maeder's "ReIm" package. I like the idea of
> using upvalues to clarify whether a symbol is real or complex. However,
> it is a debatable matter how these upvalues should be treated when
> $ComplexDefault = False. I suppose the way to handle this situation is
to
> stipulate symmetry. Thus $ComplexDefault = False means that an
undefined
> symbol is REAL unless specifically upvalued into a COMPLEX number; and
> $ComplexDefault = True means the symbol is COMPLEX unless specifically
> upvalued into a REAL number. It would cost a little bit more time in
> terms of lookups, but tolerable amounts for completeness' sake.
>
> I think this whole concept is philosophically sound as well. The new
> system variable would be a user-definable statement of his working
> habits. If you have qualms, consider the following. Quaternions are a
> generalization of complex numbers, but you don't initialize the
> Mathematica kernel with the assumption that any undefined symbol can be
a
> quaternion. You stop off at complex numbers, because they are far more
> common than quaternions. This being the case, why not allow the user to
> terminate generalizing assumptions at the field of real numbers? Real
> numbers are far more common than complex numbers, just as complex
numbers
> are far more common than quaternions.
I'll comment on this at the end.
>
> The workaround I have developed for now is simply to replace occurences
of
> Complex[a_,b_] with Complex[a,-b]:
>
> conj[number_] := number /. {Complex[re_,im_] -> Complex[re, -im]}
>
> The rules of complex algebra are such that this simple formula is
> generally safe. Thus (u1 + u2)* = u1* + u2*, (u1 u2)* = u1* u2*,
(u1/u2)*
> = (u1*/u2*). In these cases the replacement rule works. General
> algebraic expressions can therefore be input to conj[] and will come out
> all right. (I would be interested if you think of any problematic
cases.)
I would too. It looks sound to me, assuming it is applied to NumberQ
expressions or expressions where all else may be regarded as real.
>
> When $ComplexDefault = False, the kernel would still return complex
roots,
> eigenvalues, and the like when asked for them. What we are talking
about
> here are the kernel's internal assumptions about undefined symbols. You
> might have the kernel issue messages each time the assumption could lead
> to erroneous output for the sake of unwary users. Personally, I would
> Off[] this message in my Init.m file.
>
> You might well have to implement additional C++ and Mathematica code
> modules to handle special cases when arguments to various functions can
be
> assumed real. However the resulting speed improvements would probably
pay
> for those efforts.
>
>
> Mark Evans
> evans at gte.net
>
>
We have relatively quiet over the years regarding the issue of global
assumptions and simplifications. I'll say a bit about it here. I do not
speak for my colleagues, let alone Wolfram Research, Inc. (but my guess is
they agree).
First, most Mathematica functions have a stated or at least well known
domain of applicability. For example, PolynomialGCD will handle
polynomials over the rational numbers. Any other input may or may not give
a sensible result; nothing guaranteed. No assumptions are made about the
indeterminates; we simply work in a polynomial algebra. For a much
different example, Log handles complex numbers, the domain over which a
principal branch of the logarithm is defined. One can talk about discrete
logarithms in fields of nonzero characteristic. We do not implement such.
I do not know whether it even makes sense to talk about a log or square
root in the algebra of quaternions, because I do not know if there is any
notion of a principal branch; whatever would correspond to a Riemann
surface is now higher dimensional and is not an algebraic variety. In any
case such an object is going to be of use to a highly specialized audience
at best. As with discrete logs or modular square roots, it could be
implemented in Mathematica packages and this need not be done by WRI.
Indeed, it is likely to be done better by an outsider with expertise in
the relevant branch of mathematics.
Many functions we provide can make sense on domains other than the one
we use, and in some cases we try to extend these functions. The most
common method is to localize such extension to the function level, rather
than, say, through global assumptions. We do this via Options.
PolynomialGCD, for example, will take the option Modulus->prime because it
is well defined and reasonably common to take gcds of polynomials in the
field of integers modulo some prime. One can do
SetOptions[PolynomialGCD, Modulus->...]
to make ALL PolynomialGCD calls work over said modulus, but imagine the
chaos of, say, SetGlobalAssumptionsForAllFunctions[Modulus->...].
Functions that do not handle modular numbers, e.g. Log, will be completely
at a loss.
Several years ago some attempt to handle the "assume real values" desire
via the package ReIm.m. One problem comes up immediately. Suppose you set
an upvalue to the effect that some symbol is real. What if some internal
code tests for reals by checking that the imaginary part is zero? For this
to work, Im[] would have to check for upvalues. The internal code would
need to be rewritten, it would become both slower and unmaintainable, and
this would need to be done for all manner of identities. People sometimes
recommend we do this, but nobody has volunteered a working package
prototype. Is that any wonder? To get back to ReIm.m, it was decided (by
its developer, I believe) that a better approach would be to isolate the
assumption of reality into a function, and thus was born ComplexExpand. So
we are back to an individual function handling certain assumptions. Why?
Because it is feasible, maintainable, and imposes no burden on other
functions, where, to reiterate, particular assumptions may either make no
sense or be undesirable to implement, and will always slow the code.
I'll discuss the issue of global assumptions a bit more generally. I see
no way to design, let alone recode, the Mathematica kernel to handle
arbitrary user assumptions. How, for example, should Solve[x^2-3*x+4==0,
x] work with an assumption set of the form {Modulus->11, x!=3}? Should the
assumptions even apply, or should x be scoped locally? If they should
apply, how do we code roots? Find all solutions, and throw out any that
equal 3? What about handling assumptions on parameters, e.g. Solve[x^2 +
a*x + b == 0, x] with a, b, and x assumed real? The solns in general are
algebraic functions in the parameters a and b, and even for different real
values of a, b these functions will not in general be real-valued; to
fully specify where they are real valued would involve cylindrical
algebraic decomposition (CAD), itself a HARD problem to tackle. I am aware
of no program that has successfully combined global assumptions with an
equation solver (although the specific case of polynomial equations and
all variables assumed real is done by CAD in some specialized software).
A useful area for pursuit is that of simplification under assumptions.
Rather than try to force global assumptions on all manner of functions,
the goal would be to have a simplifier (possibly Simplify) that would
take a list of assumptions and then attempt to heuristically simplify its
input under those assumptions. A classic example might be input as
Simplify[Sqrt[x^2], AssumptionList->{Re[x] > 4}]
I expect we will pursue this in the not-too-distant future, as it is both
useful and not completely unreasonable to implement (well, depends on the
expectations of the user, I guess).
Let me summarize why I think implementation of global assumptions is not
sound. (1) It imposes a burden on all functions, rather than localizing
the burden as is done with Options. (2) If done via upvalues, it will slow
and vastly complicate all code. Other methods will likewise burden the
code, and almost certainly slow it. (3) It is not feasible to make the
kernel handle all assumptions a user might specify. It cannot be done
"correctly" on even a small fraction of Mathematica functions. (4) For
most purposes it is a simplify-with-assumptions that is desired anyway.
That is a direction we could and may pursue.
Daniel Lichtblau
Wolfram Research
danl at wri.com
==== [MESSAGE SEPARATOR] ====