Complex.js
Complex.js copied to clipboard
Behaviour of non-finite complex numbers.
I was looking into issue josdejong/mathjs#804 regarding the behaviour of negative numbers raised to infinite powers.
Support for non-finite complex numbers seems to be incomplete in complex.js and I was wondering if it was something that you are planning to support more comprehensively. For example:
new Complex(-0.5).pow(Infinity)currently givesNaNwhich is inconsistant with the behaviour ofMath.pow(-0.5, Infinity)which (correctly) gives0.
Additionally I found a couple of bug which need fixing regardless of whether the library is extended to add proper support for complex numbers.
new Complex(0).inverse()currently gives0.new Complex(Infinity).equals(Infinity)currently returnsfalse.
Oh, that are good findings! Thanks a lot for the ticket and notifying me about #804! I'll look into this. About the last point, it's a mere philosophical question if infinity should equal infinity, or more specifically is 1/0 == 2/0? JavaScript simply compares the infintiy bit, but other languages treat this case differently.
Yeah that's fair re the equality - an isFinite might be a better way to test for infinite numbers although !isFinite(z) && z.im === 0 && z.re > 0 is far more unwieldy than z.equal(Infinity).
Is full support for infinite values (eg in the pow function) worth increasing the size of the library?
Okay, I added a new branch "infinity": https://github.com/infusion/Complex.js/commit/f06449fef848cf7dd4fd0aba3e48c31c054dcf94
I added a new symbol Complex.Infinity, which sets real and imaginary part to infinity. This symbol is returned for inv(0), div(z, 0) and if the angle for cos/sin in pow is becoming infinity (which originally forced the number to become nan).
The actual code changes don't change the size of the library dramatically, so I would say it's worthwhile to add the feature.
I would say we keep equal as it is, but think about adding isFinite (which is true <=> isFinite(re)&isFinite(im)).
isFinite sounds like a good solution.
I noticed that currently complex(0).div(0) === complex(0). This is in both the master and infinity branches.
Additionally currently complex(1).div(0) === complex(Infinity, 0) where as complex(0).inverse() === complex(Infinity, Infinity). I am not sure about the best behaviour for this as I feel that the division of two real numbers should be real but also that inverse(z) should be equivalent to 1/z.
Finally complex(0, Infinity).mul(1) === complex(NaN, Infinity) at the moment.
Hm, yep I thought the same way and I think a real infinity should stay real. However, wolframalpha decided (maybe for the same reason) to print complex infinity for any case, the complex and real: http://www.wolframalpha.com/input/?i=1%2F0
0/0 Should be fixed, right. NaN is an approproate value for that.
The different behavior of div and inverse must be fixed as well.
About the last one, the multiplication, it happens to become NaN since 0Infinity=NaN. I see your point that the multiplicative neutral element should return the same value after multiplication (which would be the case, if we treat 0Infinity as 0. I must have a look, if this can be generalized this way.)
Okay, there is an update in infinity branch.
isFinitewas added- 0/0 is now complex infinity as well, even if it's not defined. Let's see if we can get along with complex infinity and not returning a "complex nan" at all - since I don't want to add a special check for this rare case.
- About imaginary infinity times one - I'm not sure if we should fix that (see last post).
- 1/0=complex inf is consistent with inv(0)=complex inf
-
I am not a big fan of
0/0 === ComplexInfinityas that would be different from every other maths language and I don't think consistent with complex maths although I had a look at the wikipedia article and it all went over my head. -
I had a go at implementing full support for infinity in the
mulfunction see my pr. I don't like how messy it is but I couldn't find anyway to handle non finite numbers apart from bashing every case. -
I also had look at the parse function and added support for infinite complex numbers in the
{r, phi}form. I don't thinkInfinity + 5imakes any sense so unless the number is purely real or purely imaginary the parse function sets both the re and imaginary components to Infinity. This slightly reduces the number of cases I had to include in themulfunction.
Hi
What is the purpose of these lines in the divide function?
return new Complex(
(a !== 0) ? (a / 0) : Infinity,
(b !== 0) ? (b / 0) : Infinity);
If a is not zero then the expression evaluates to Infinity anyway.
https://github.com/infusion/Complex.js/blob/infinity/complex.js#L387-l388.
I believe that the whole of the if (0 === c) conditional could be removed.
The purpose is to keep the sign of each Infinity.
But as both the real and imaginary parts will be infinite the return value will get converted to complex infinity anyway.
I am also reasonably convinced that new Complex(0).div(0) should be ComplexNaN; I think its worth a special case.
Hi! I would like to ask how this issue is progressing. I am interested in having complex infinity fixed in the library. After all this is a Complex library so I would assume it treats infinities mathematically correctly. Most of the workings of complex infinity is on the wikipedia page https://en.wikipedia.org/wiki/Riemann_sphere But I am also happy to summarize this here, if needed.
@Balagge have a look at pull #12, I have had a go at making the required fixes but I don't have an intensive knowledge of the maths so if you could have a look through the changes that would be grand.
Otherwise we are waiting on @infusion
Unfortunately I currently (and for quite a while) do not have the time to understand the code and test it. What I can do is write down what I consider the mathematically correct behavior.
Below c, c1, c2, etc. denotes any complex infinity object, z, z1, z2, etc. denotes any Complex object other than a complex infinity (i.e. finite)
Construction and equality
- complex infinities should be constructed by the
Complexconstructor if either the real part or the imaginary part is infinite (Infinityand-Infinitydoes not matter), or the radius is positive Infinity. So the following are all complex infinities:c = new Complex(Infinity, 1)c = new Complex(0,Infinity)c = new Complex({re:Infinity, im:-Infinity})c = new Complex({arg:3, abs:Infinity}(however,abs:-Infinityis invalid. also,arg:Infinityandarg:-Infinityshould be invalid) - Any
cshould be aComplexobject different from anyz, soz.equals(c) === falseandc.equals(z) === falseshould always hold (note thatcis any complex infinity object andzis any finite complex object). - Complex Infinities should be equal (even if two different object)., so
c1.equals(c2) === trueshould hold for any (different) complex infinity objects.
Addition, subtraction, negation
- Adding infinity to any finite complex should yield a complex infinity, so
z.add(c1).equals(c2) === true, andc1.add(z).equals(c2) === trueshould always hold (note the usual usage of letters) - Adding complex infinities is not defined (UNLIKE real infinities), so
c1.add(c2)should not have a value (NaNor something else? I do not know how exactly to represent undefined operations, but probably there is some way to do that already) - Negation returns the same complex infinity (UNLIKE real infinities), so
c1.neg().equals(c2) === trueshould always hold - Subtraction is the same as adding the negated operand. Specifically
z.sub(c1).equals(c2) === true,c1.sub(z).equals(c2) === true,c1.sub(c2)is invalid (not defined) - sign is not defined, so
c.sign()should not have a value.
Real and imaginary part, argument and absolute value
- real part is not defined. This may be a problem, but
c.reshould NOT have a definite value - imaginary part is not defined.
- argument is not defined, so
c.arg()should not have a value - absolute value is positive infinity, so
c.abs() === Infinityshould hold.
Multiplication
- Anything (finite or infinite), except zero, multiplied by a complex infinity is a complex infinity, so
z.mul(c1).equals(c2) === true, andc1.mul(z).equals(c2) === trueunlesszis zero.z.mul(c1)andc1.mul(z)is not defined ifzis zero.c1.mul(c2).equals(c3) === true
Division
Division has multiple cases. both the dividend and the divisor must be examined and three cases separated: zero, non-zero (but finite), infinity. This yields 9 possible combinations (here I use more traditional notation and z', z1', z2', etc. mean nonzero, finite complex numbers)
13) 0 / 0 is not defined
14) 0 / z' is 0, as usual.
15) 0 / ∞ is also 0.
16) z' / 0 is ∞.
17) z1' / z2' is normal division of complexes.
18) z1' / ∞ is 0.
19) ∞ / 0 is not defined
20) ∞ / z' is ∞
21) ∞ / ∞ is not defined.
Multiplicative inverse
- this follows from the division above, here I use
1/znotation instead ofz.inverse(),1 / 0is∞1 / z'is normal inverse of a complex1 / ∞is0.
This is the basic stuff, other functions require some more thinking but I will come back when I have some time
Ah thanks, I'll have a look through these :)
Yeah, I back everything except the following which I have questions about.
0. How about new Complex(Infinity, 0), should this not be real infinity?
If it is then by symmetry new Complex(0, Infinity) should be imaginary infinity.
2. See https://github.com/infusion/Complex.js/issues/5#issuecomment-296390253, I would be happy for ComplexInfinity.equals(ComplexInfinity) === true.
4. Yeah we have ComplexNaN for times like this.
8. Hmm, need to think more about this. Possibly worthy of a separate issue.
22. Also requires thought.
I am of the opinion that new Complex(1, 0).div(0) should equal 1 / 0. When using real numbers the behaviour of this library should be identical to using JavaScript numbers.
I realise though that mathematically this is wrong but I think I can live with that.
I see what you mean. Unfortunately, it is impossible to extend the complex plane with a single complex infinity in such a way that it is mathematically reasonable ("correct") and operations for real numbers behave exactly as on the (extended) real number line at the same time.
The closest thing is using "directed infinities". The real infinity (javascript: Infinity) is then "an infinity in the direction of 1". (direction = when viewed from point 0, or simply "to the east"). The real negative infinity (javascript: -Infinity) is "an infinity in the direction of -1, or simply "to the west". You can have then an infinite number of infinities in all possible directions (like infinity in the direction of i, which is infinity "to the north", but also "infinity in the direction of 1+i", or northeast.
This is possible, and indeed, Wolfram uses this approach. It has a ComplexInfinity and an infinite number of DirectedInfinity[z] different infinities (where z is any non-zero complex number). The "well-known" Infinity in javascript is then not ComplexInfinity, but DirectedInfinity[1] in Wolfram. (the square brackets are used around function arguments). Or, with traditional notation, where ∞ is the real positive infinity (javascript Infinity) :
∞ = 1∞ = x∞ (where x is any positive real number),
-∞ = (-1)∞ = x∞ (where x is any negative real number)
For complex infinity, wolfram uses a symbol similar to this: ⧝ (U+29DD). But then
∞ ≠ ⧝ unfortunately, and
1 / 0 = ⧝ and 1 / 0 ≠ ∞ (on the complex plane), however, 1 / 0 = ∞ (on the real number line). :(
This becomes even more problematic, because you then have to define all operations for all of these new infinities, and some of the operations are again undefined. To my knowledge there is little, if any, practical use for these directed infinities. But I may be mistaken.
https://en.wikipedia.org/wiki/Directed_infinity
http://mathworld.wolfram.com/DirectedInfinity.html
https://www.wolframalpha.com/input/?i=directed+infinity
btw javascript has some mathematically incorrect behavior as well, so maybe it is not a great idea to follow it.
example: Math.pow(-2,Infinity), which yields Infinity. This is incorrect. On the real number line, this has no value, and therefore should probably be NaN (because the sequence (-2)^n does not converge). On the complex plane, if we use Infinity for both the real ∞ and the complex infinity ⧝, it would be true, in the sense that (-2)^∞ =⧝. However, Infinity cannot mean both the positive real infinity ∞ and the complex infinity ⧝. Those behave differently, for example an easy way to tell the difference between the two is this: ⧝+⧝ is not defined, but ∞+∞ = ∞. Since in javascript Infinity + Infinity yields Infinity we can be sure that Infinity represents the real infinity, not the complex one.
This does not mean of course that the javascript behavior for 1 / 0 yielding Infinity is incorrect. It is correct on the real number line. But since Complex.js models the complex plane, not the real numbers, it should follow complex math and the mathematically correct behavior for new Complex(1, 0).div(0) should be ComplexNaN then (if we do not have complex infinity), or, better still, ComplexInfinity.
This is MATLAB's behaviour, my preference would be for Complex.js to be the same.

I see. Looks kind of silly, based on these it seems MATLAB models 8 selected directions on the complex plane (i∞, (1+i)∞, 1∞, (1-i)∞, (-i)∞, (-1-i)∞, (-1)∞, (-1+i)∞ ) which is very arbitrary. I do not know MATLAB, but I remember vaguely that I came across some other case where it had questionable behavior.
The real question is: what is the practical use of all these infinities? I see the following use cases generally:
- (real) infinity is sometimes useful as an upper or lower bound instead of an actual number value. So if you have a constraint like
a < x < b, then you can easily useInfinityand-Infinityinstead of a number foraorband that pretty much simplifies your code, especially if the value ofaandbcomes from some configuration option or from another computation. But this case applies to reals only. - infinity may result as a "final" value from some computation you execute, which otherwise cannot yield a number (or Complex). Instead of treating these cases as
NaNor even raising some exception, this can be sometimes useful to detect some edge case.NaNis a pretty useless construct because you have no clue what causes it. Some form of infinity, however, may have some semantics in the particular scenario that can be handled meaningfully. This applies to both real and complex computations as well. - same as above, but the resulting infinity is not a "final" value, but some intermediate value. It then may be meaningfully processed at a later stage of the computation. This can be very handy, like if you have a formula:
1 / (1 + f(x)), and maybe yourf(x)can return Infinity, the computation may yield a perfectly valid result (0in this case, assuming real domain). Again, this can simplify your code dramatically.
These are the cases I have ever came across. The first one is the most common (at least for me), but does not apply to complexes (no ordering on complex plane).
The second one can be achieved by any infinity construct (whether a single complex infinity, or several directed infinities, or the MATLAB version with 8 selected directed infinities - I still think this is silly). The more different kinds of infinities you have, the less useful this becomes because it will be difficult and error prone how you check the return value for these infinities.
The third one is a piece of cake, provided that the underlying logic is mathematically sound. It does not occur frequently, but when it does, it can greatly simplify code (no need to separate a lot of different edge cases). However, if some arbitrary rules are used, like in MATLAB, then most probably the result will not be what you expect in a particular scenario, so you end up checking those edge cases despite the built in infinity constructs / computation rules, hence no improvement / simplification in your code and the infinities become useless like NaN.
Of course this is by no means a detailed analysis, rather just my personal opinion based on (my particular) experience and intuition. Which still tells me that the best / most elegant version would be a single complex infinity. It would be great, however, to hear opinions from others as well.
Nevertheless I have to give credit to the argument that it is a nice feature to have that things like 1/0 can be computed without an underlying assumption of the domain (reals or complexes). In other words, 1/0 yields the "same" result for numbers and Complexes. On the other hand, this library is about complex numbers, not a general purpose math engine, so such an assumption is not a problem in this case.
Sorry about the delay. :)
Having these 8 infinities is easy to implement with each complex object containing two fields (re and im) where as anymore infinities would require much more involvement. I think that is why MATLAB does it this way.
The more different kinds of infinities you have, the less useful this becomes because it will be difficult and error prone how you check the return value for these infinities.
I propose adding an isFinite() function which would be the recommended way to test for infinite complex numbers. Especially as the current behaviour is for equals() to consider all infinite values unequal.
Therefore options:
- A single complex infinity.
- ± real infinity, ± imaginary infinity, complex infinity
- ± real infinity, ± imaginary infinity, 4 complex infinities one in each quadrant.
I vote for 2. @infusion Do you have an opinion?
see #14 as well.
I vote for option 1 (Riemann Sphere) that @balagge recommended. It is both mathematically and programmatically elegant and shouldn't add much if any to the size of the library. I have been looking for a complex library that implemented it for a while now...
Issue resolved by #17, we can close this issue as soon as a new version is pushed to npm.
I think these changes are semver major just incase anyone was previously doing new Complex(0).div(1) which used to give { re: Infinity, im: 0 } and in the new version gives { re: Infinity, im: Infinity }
@infusion could you release a version to npm?