Re: Complex Default
- Subject: [mg3036] Re: Complex Default
- From: danl (Daniel Lichtblau)
- Date: 25 Jan 1996 06:30:47 -0600
- Approved: usenet@wri.com
- Distribution: local
- Newsgroups: wri.mathgroup
- Organization: Wolfram Research, Inc.
- Sender: mj at wri.com
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