power returns invalid complex number for negative base and Infinity as exponent
Following inputs return NaN - aNi in Notepad. Seems like all negative bases yield the same:
(-0.5)^Infinity
(-1)^Infinity
(-2)^Infinty
probably the underlying function pow() returns a Complex with both Real and Imaginary part set to NaN. (haven't checked).
Definitely, NaN + aNi is wrong. But whether this is simply an issue with the rendering, or the underlying logic, is to be analyzed. Some thoughts:
-
Whether the result should be a
Complexwith both Real and Imaginary parts set toNaN, is questionable (after all there is aNaNalready, no need to introduce a "second, complexNaN" value maybe). -
for
-1 < a < 0, the result should probably be zero, as in javascript, this one converges:Math.pow(-0.5,Infinity) // 0 -
for
a < -1the result should not be Infinity (so, unlike javascript), because this is clearly divergent. (I don't understand javascript behavior:Math.pow(-2,Infinity) // Infinity -
-Infinityas exponent should also be considered (currently these cases also return incorrectNaN + aNi, but convergence / divergence should also be considered separately for-Infinity)
Thanks for reporting Paal. That NaN - aNi looks like a formatting issue. Besides that we can definitely give these edge cases with NaN and infinity some more thought. We should keep in mind that we're working with a numerical system (with it's limitations).
I had a look into this: the pow function checks if the base is positive or the exponent is an integer or config.predictable = true and if so returns the result of Math.pow. Otherwise the pow function of Complex.js is used which returns Nan + NaNi for infinite exponents.
Therefore with config.predictable = true negative bases raised to the power of infinity return either Infinity or zero.
I feel that the behaviour should not change in this way regarding config.predictable so it would be worth adding a check for infinite exponents to the pow function.
@HarrySarson would you like to pick this issue up?
Sure I can have a go :)
:+1: thanks
What are your views on the best result for (-2)^Infinity?
- a)
Infinityas javascript, matlab and c++ all go for. - b) Not infinity as @balagge says - possibly
NaN?. - c) Some sort of
ComplexInfinitywhich how it is done in wolfram alpha.
I feel like (c) is the most elegant solution but would require either a new type or and extension of complex.js. (a) and (b) are much easier to do.
Additionally the behaviour of complex.js is currently to return (NaN + NaN * i) for any infinite powers - it would be nice if math.pow(2, Infinity).toString() === math.pow(math.complex(2), Infinity)).toString() so I might raise an issue with complex.js for better support for infinities.
I think returning complex infinity for (-2)^Infinity would be most neat, or else complex NaN like it does now.
For complex numbers NaN can make sense, since the the power function is defined via sin/cos and sin(inf)=nan. However, if we think about it, pow(c∈C, inf) is an infinite rotation of an arrow. With this interpretation, Infinity would make sense too. However, I would go with a new "symbol" - "complex infinity" as well.
I made some minor changes on complex.js regarding the infinity behavior. I'd be glad if you guys could have a look: https://github.com/infusion/Complex.js/issues/5
Ah, and about the printing error, I catch NaN's in complex.js's toString(). I suspect, that we added an own stringifier for math.js.
I've fixed the wrong formatting of NaN + aNi which should be NaN + NaNi. math.js indeed still overwrites the .toString of Complex.js. Maybe that's not needed at all. Let's check that out.
Sounds good @infusion . It may make sense to create a special constant complex infinity rather than using Infinity. It may make sense to create this as a constant in math.js: math.ComplexInfinity, which would simply be the new Complex.Infinity constant of Complex.js. Probably we can do the same for complex NaN: ComplexNaN?
@josdejong for "NaN + NaNi", I decided for complex.js to make the whole complex-number NaN as soon as one component becomes NaN. So, the output is "NaN". I think this lacks some information in the output, but at the end it's just a feedback, that an operation failed, other than for infinity, where the individual dimensions can become infinity with a certain meaning.
Mapping Complex.Infinity to math.ComplexInfinity sounds good to me :)
ComplexNaN is still a big question. We actually use complex numbers to make Math.sqrt(-1)!=NaN work. There're only rare discontinuities where NaN can occur. That are 0/0 and the strange case when 0*Infinity is NaN in Javascript, which we can fix. For my last push to the "infinity" branch of complex.js, I decided to make 0/0 complex infinity as well, since I don't like to introduce a new symbol for that.
What do you guys think?
I think complex infinity is a great idea. Complex infinity is different from "real" infinities Infinity and -Infinity, because those have directions on the complex plane, whereas complex infinity is directionless.
Complex infinity (denoted by I below) should satisfy:
-
I + z = I, for any z ∈ ℂ; -
-I = I(UNLIKE real Infinities!!); -
I * I = I; -
I * z = Ifor any z ∈ ℂ, z ≠ 0; -
z / I = 0for any z ∈ ℂ, z ≠ 0;
Edge cases:
-
I + Iis not defined (NaN), UNLIKE real Infinities!; -
0 * Iis not defined (NaN?) -
I / Iis not defined (NaN?) -
I - Iis not defined (NaN?)
ComplexNaN, on the other hand, maybe should be avoided. I do not see justification for a "separate" NaN value. (for the record: I do not see justification for NaN in the first place). But accepting the fact that we do have NaN, for whatever reason, then creating another one seems awkward to me.
So if any operation could/should return "complex NaN", I would vote for returning simply 'NaN' instead.
http://reference.wolfram.com/language/ref/ComplexInfinity.html
This page is also useful, it has some return values in Wolfram of standard functions on ComplexInfinity. (under "Neat Examples")
If the complex infinity is in the exponent, I would say:
-
z ^ I = I, for any z ∈ ℂ, abs(z) >1 (this includesI^I = Ias well); -
z ^ I = 1, if z = 1; -
z ^ Iis undefined for any z ∈ ℂ, abs(z) = 1 and z ≠ 1; -
z ^ I = 0for any z ∈ ℂ, abs(z) <1
update: these may be (are probably) wrong, someone should check it who has better knowledge of complex analysis than me... I have studied this type of stuff more than 2 decades ago.
The discussion about ComplexInfinity may be better placed at infusion/Complex.js#5 as the implementation of complex pow is handed there.
complex(0).mul(Infinity) shouldn't simply return NaN as complex(0).mul(Infinity).add(4) should propagate the NaN value (in a similar way to 0*Infinity+5) where as if "normal" NaN was returned the user would have to check the result of every function call which would be very annoying for them.
Apart from that I agree :)
Makes sense. So to summarize: we want Infinity, ComplexInfinity, and NaN but no "complex NaN".
The latter may be tricky since that means that after every operations with complex numbers we have to check whether the resulting complex number contains NaN for one or both re and im. What we could do though is adjust the presentation of a complex number containing NaN (like Complex.js already does), and adjust equality checks such that math.equal(math.complex(NaN, NaN), NaN) === true and function isNaN handles complex NaN instances too.
Complex.js now has infinity support in master branch. When the next release is published to npm we should be able to fix math.pow(complex, Infinity).
I think it should "just work" but some tests might fail and even if they don't we should add some new tests. I will make a PR when the new version is published. :)
I thik the concept of complex infinity must be introduced for math.js as well. (with a special symbol or sth)
Great if you can pick this up coming time Harry !
Hi Team - just picking up this issue to see if I can help.
First, I've confirmed via the Math notepad that the following two cases work as expected. Please see testing results: #804 Testing
- lim n-> inf (-0.5)^{n} = 0
- lim n-> -inf (-3)^{n} = 0
Just to clarify from reading previous comments - I need to use complex.js (https://github.com/infusion/Complex.js/#complexinfinity) for cases that don't converge to a limit, e.g. (-1)^{+-inf}, so instead of returning NaN + NaNi I simply return complex.INFINITY and update the unit tests accordingly?
Let me know what you think.
Best regards George
Thanks for picking this up @georgemarklow !
Math Notepad is a little bit behind (still at mathjs v7.5.1). To know for sure what the output is it may be best to try out directly in the developer console of https://mathjs.org or checkout the project and use the repl locally. Also, be careful not to look at the output of format but really at the numeric output of math.pow.
It would be nice if you can get a clear list with the edge cases we're talking about, the desired outcome, and the current outcome. (At least I myself do not have the edge cases and desired outcome clear at this moment, and we may want to discuss some of them).
Indeed the calculations are done by Complex.js, in mathjs we want to do as little as possible, though when the output of Complex.js for those edgecases differs from what we want/expect, we have to write code for it in mathjs to cover the edge case.
There is a stalled PR https://github.com/infusion/Complex.js/pull/25. @georgemarklow having a look at this might be a good place to start. It might be that we can patch the Complex infinity handling from within mathjs somehow although I suspect better infinity support from Complex.js will really be needed to make progress.
Hi guys, following infusion/Complex.js#25, I created a branch off develop, wrote unit tests to handle the cases, and refactored the code to understand the problem and get feedback.
Could I have permission to create a new remote branch (see below)?
Thanks
@georgemarklow what we normally do is start with forking the mathjs project to your own github account, create a feature branch there to work on something new, and then create a PR to josdejong/mathjs. When you're involved in the project for a longer time we can discuss becoming a collaborator on the project with access to the project itself, that would be great :).