Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

power returns invalid complex number for negative base and Infinity as exponent #804

Open
balagge opened this issue Feb 28, 2017 · 25 comments · May be fixed by #2097
Open

power returns invalid complex number for negative base and Infinity as exponent #804

balagge opened this issue Feb 28, 2017 · 25 comments · May be fixed by #2097
Labels
bug dependencies Pull requests that update a dependency file, or issues that lie primarily in a dependency help wanted

Comments

@balagge
Copy link

balagge commented Feb 28, 2017

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 Complex with both Real and Imaginary parts set to NaN, is questionable (after all there is a NaN already, no need to introduce a "second, complex NaN" 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 < -1 the result should not be Infinity (so, unlike javascript), because this is clearly divergent. (I don't understand javascript behavior: Math.pow(-2,Infinity) // Infinity

  • -Infinity as exponent should also be considered (currently these cases also return incorrect NaN + aNi, but convergence / divergence should also be considered separately for -Infinity)

@josdejong
Copy link
Owner

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).

@josdejong josdejong added the bug label Feb 28, 2017
@harrysarson
Copy link
Collaborator

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.

@josdejong
Copy link
Owner

@harrysarson would you like to pick this issue up?

@harrysarson
Copy link
Collaborator

Sure I can have a go :)

@josdejong
Copy link
Owner

👍 thanks

@harrysarson
Copy link
Collaborator

What are your views on the best result for (-2)^Infinity?

  • a) Infinity as javascript, matlab and c++ all go for.
  • b) Not infinity as @balagge says - possibly NaN?.
  • c) Some sort of ComplexInfinity which 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.

@josdejong
Copy link
Owner

I think returning complex infinity for (-2)^Infinity would be most neat, or else complex NaN like it does now.

@infusion
Copy link
Collaborator

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: rawify/Complex.js#5

@infusion
Copy link
Collaborator

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.

@josdejong
Copy link
Owner

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?

@infusion
Copy link
Collaborator

infusion commented Apr 24, 2017

@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?

@balagge
Copy link
Author

balagge commented Apr 24, 2017

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 = I for any z ∈ ℂ, z ≠ 0;
  • z / I = 0 for any z ∈ ℂ, z ≠ 0;

Edge cases:

  • I + I is not defined (NaN), UNLIKE real Infinities!;
  • 0 * I is not defined (NaN?)
  • I / I is not defined (NaN?)
  • I - I is not defined (NaN?)

@balagge
Copy link
Author

balagge commented Apr 24, 2017

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.

@balagge
Copy link
Author

balagge commented Apr 24, 2017

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")

@balagge
Copy link
Author

balagge commented Apr 24, 2017

If the complex infinity is in the exponent, I would say:

  • z ^ I = I, for any z ∈ ℂ, abs(z) >1 (this includes I^I = I as well);
  • z ^ I = 1, if z = 1;
  • z ^ I is undefined for any z ∈ ℂ, abs(z) = 1 and z ≠ 1;
  • z ^ I = 0 for 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.

@harrysarson
Copy link
Collaborator

harrysarson commented Apr 24, 2017

The discussion about ComplexInfinity may be better placed at rawify/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 :)

@josdejong
Copy link
Owner

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.

@harrysarson
Copy link
Collaborator

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. :)

@infusion
Copy link
Collaborator

infusion commented Feb 1, 2018

I thik the concept of complex infinity must be introduced for math.js as well. (with a special symbol or sth)

@josdejong
Copy link
Owner

Great if you can pick this up coming time Harry !

@georgemarklow
Copy link

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

@josdejong
Copy link
Owner

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.

@harrysarson
Copy link
Collaborator

There is a stalled PR rawify/Complex.js#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.

@georgemarklow
Copy link

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

Screenshot 2021-01-10 at 3 47 28 pm

@josdejong
Copy link
Owner

@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 :).

@gwhitney gwhitney added the dependencies Pull requests that update a dependency file, or issues that lie primarily in a dependency label Oct 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug dependencies Pull requests that update a dependency file, or issues that lie primarily in a dependency help wanted
Projects
None yet
6 participants