MathGroup Archive 1996

[Date Index] [Thread Index] [Author Index]

Search the Archive

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] ====


  • Prev by Date: Re: PowerBook Woes
  • Next by Date: Re: Complex Default
  • Previous by thread: Re: Complex Default
  • Next by thread: Re: Complex Default