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

Give deprecation warning when raising negative AlgebraicReal to fractional odd power #38362

Closed
wants to merge 1 commit into from

Conversation

user202729
Copy link
Contributor

@user202729 user202729 commented Jul 14, 2024

Previously, the behavior of AA is inconsistent with most other cases:

sage: AA(-8)^(1/3)
-2
sage: QQbar(-8)^(1/3)
1.000000000000000? + 1.732050807568878?*I

This will give a deprecation warning when the user attempt to rely on that behavior.


Previous description

Intended to fix​ #36735 , but now it doesn't.

This fix tries to be more conservative in how to converts symbolic expression of the form a^b: it still first convert a to self.field then raise the result to power of b, but it checks if a<0 and b is a fraction then raise an error (I think the previously computed result in this case is never expected).

It does not fix​ #12745 though (an alternative, as mentioned in #36942 , is to convert to QQbar first then convert it to AA, then this code wouldn't be necessary. What do you think of that alternative approach?)

📝 Checklist

  • The title is concise and informative.
  • The description explains in detail what this PR is about.
  • I have linked a relevant issue or discussion.
  • I have created tests covering the changes.
  • I have updated the documentation and checked the documentation preview.

Copy link

github-actions bot commented Jul 27, 2024

Documentation preview for this PR (built with commit a5a2fbe; changes) is ready! 🎉
This preview will update shortly after each push to this PR.

@kwankyu
Copy link
Collaborator

kwankyu commented Aug 26, 2024

Would you consider #38564?

@user202729
Copy link
Contributor Author

Would you consider #38564?

@kwankyu I'm happy with that option also, but some other people might have trouble with previous code give different result silently.

#36942 (comment)

@mezzarobba @nbruin ?

@mezzarobba
Copy link
Member

I'm happy with that option also, but some other people might have trouble with previous code give different result silently.

#36942 (comment)

I believe we should change the semantics of fractional powers in AA (and everywhere else!) to always use complex principal roots, and use a different function name (#12074) for real nth roots. IMO the current behavior is a bug (even though it is clearly intentional and documented), so I personally would be ok with just fixing it. I would ask on sage-devel before doing it, and draw attention to the change in the release notes. The alternative, I suppose, is to first give a introduce an alternative real rational power function and give a deprecation warning in all cases where the result would differ for a while. I'm not sure which option is the least inconvenient for everyone involved...

@kwankyu
Copy link
Collaborator

kwankyu commented Aug 27, 2024

This PR and my PR #38564 both propose changes in the semantics of AA(symbolic expression). We cannot view the current behavior as a bug. That is just a different interpretation of fractional powers of negative reals, which number theorists find inconvenient

Hence I think we should follow the deprecation rules. For a year, we advertise the upcoming semantics change. Then after a year, we switch to the semantics we want.

To implement that, I suggest to use this PR for the deprecation purpose, and after the deprecation period use my PR to change the semantics. What do you think?

(well, the deprecation period is painful, but that is the rule...)

@kwankyu
Copy link
Collaborator

kwankyu commented Aug 27, 2024

By the way, I think one-year deprecation period is too long, in general. A half year would be enough.

@mezzarobba
Copy link
Member

mezzarobba commented Aug 27, 2024 via email

@kwankyu
Copy link
Collaborator

kwankyu commented Aug 27, 2024

... but I'd argue that the current semantics is wrong because it breaks the assumptions of the coercion system, and, more generally, the overall consistency of the Sage library (one expects methods with the same name applied to mathematically equivalent objects to behave in a consistent way).

I agree with that, why we are working here.

@kwankyu
Copy link
Collaborator

kwankyu commented Aug 27, 2024

Hmm. It seems that the cause of the "bug" in #36735 is elsewhere... See this:

sage: RR(-1)^(1/3)
0.500000000000000 + 0.866025403784439*I
sage: CC(-1)^(1/3)
0.500000000000000 + 0.866025403784439*I
sage: QQbar(-1)^(1/3)
0.500000000000000? + 0.866025403784439?*I
sage: AA(-1)^(1/3)                    # first inconsistency 
-1

and also

sage: QQ(-1)^(1/3)
(-1)^(1/3)
sage: ZZ(-1)^(1/3)
(-1)^(1/3)

Fixing this inconsistency alone might solve #36735.

On the other hand, there is another kind of inconsistency:

sage: RR((-1)^(1/3))
...
TypeError: unable to convert '0.500000000000000+0.866025403784439*I' to a real number
sage: AA((-1)^(1/3))                # second inconsistency
-1

This PR and my PR #38564 both solve the second inconsistency. #36735 is also solved kind-of as a side effect.

@kwankyu
Copy link
Collaborator

kwankyu commented Aug 27, 2024

In accordance with the opinion in https://groups.google.com/g/sage-devel/c/s81ieq2vpVo, I also fixed the first inconsistency in #38564.

@user202729 Are you willing to repurpose this PR for appropriate deprecation warnings?

@user202729
Copy link
Contributor Author

user202729 commented Aug 27, 2024

Alright, I try to change the code accordingly.

If you also want AA(-1)^(1/3) to raise the warning however, you should move the warning here:

diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py
index 5c664d53e71..9d66e65a910 100644
--- a/src/sage/rings/qqbar.py
+++ b/src/sage/rings/qqbar.py
@@ -6430,6 +6430,9 @@ class AlgebraicNumberPowQQAction(Action):
         S = self.codomain()
         if S is AA and d % 2 == 0 and x.sign() < 0:
             S = QQbar
+        elif S is AA and d % 2 != 0 and d != 1 and x.sign() < 0:
+            from sage.misc.superseded import deprecation
+            deprecation(36735, "Raising negative algebraic real number to odd fractional power is deprecated")
 
         # First, check for exact roots.
         if isinstance(x._descr, ANRational):
diff --git a/src/sage/symbolic/expression_conversion_algebraic.py b/src/sage/symbolic/expression_co>
index fd9c9066c9e..b2d842ddbc3 100644
--- a/src/sage/symbolic/expression_conversion_algebraic.py
+++ b/src/sage/symbolic/expression_conversion_algebraic.py
@@ -130,10 +130,6 @@ class AlgebraicConverter(Converter):
                 base, expt = ex.operands()
                 base = self.field(base)
                 expt = Rational(expt)
-                if (isinstance(self.field, AlgebraicRealField) and expt.denom() != 1 and
-                    expt.denom() % 2 != 0 and base < 0):
-                    from sage.misc.superseded import deprecation
-                    deprecation(36735, "Conversion of negative number to fractional power to AA is>
                 return self.field(base**expt)
             else:
                 if operator is add_vararg:

While looking at it there's also this behavior…

sage: AA(-sqrt(27)+1)^(1/2)
2.048451225366773?*I
sage: _.base_ring()
Algebraic Real Field
sage: AA(-1)^(1/2)
I
sage: _.base_ring()
Algebraic Real Field

Surely the element should belong to its base ring? .parent() is correct though.

Though the problem is should it give an error or silently convert to QQbar (current behavior)?

@nbruin
Copy link
Contributor

nbruin commented Aug 27, 2024

Surely the element should belong to its base ring?

No, it does not:

sage: R.<x>=QQ[]
sage: x.base_ring()
Rational Field

It looks like it's a shortcut for .parent().base_ring(). In a way, Qbar is a quadratic extension of AA and AA is apparently considered as a primitive construct, since it's not obviously a finite degree extension of something conveniently represented:

sage: QQbar.base_ring()
Algebraic Real Field
sage: AA.base_ring()
Algebraic Real Field

I'm not sure I agree (I think of QQbar as not particularly related to any complex embedding), but given that the implementation really does realize QQbar as a subfield of CC I guess it makes sense ...

@kwankyu
Copy link
Collaborator

kwankyu commented Aug 28, 2024

Alright, I try to change the code accordingly.

Thanks.

sage: AA((-1)^(1/3))
/Users/kwankyu/GitHub/sage-dev/src/sage/symbolic/expression_conversions.py:212: DeprecationWarning: Conversion of negative number to fractional power to AA is deprecated
See https://github.com/sagemath/sage/issues/36735 for details.
  return self.arithmetic(ex, operator)
-1

Your deprecation warnings should point to this PR #38362.

If you also want AA(-1)^(1/3) to raise the warning however, ...

Yes. That should also be done in this PR. Something like

--- a/src/sage/rings/qqbar.py
+++ b/src/sage/rings/qqbar.py
@@ -6441,6 +6441,10 @@ class AlgebraicNumberPowQQAction(Action):
             if rt is not None:
                 if x._descr._value < 0:
                     if S is AA:
+                        if d % 2 != 0 and d != 1 and x.sign() < 0:
+                            from sage.misc.superseded import deprecation
+                            deprecation(38362, "Raising negative algebraic real number "
+                                        "will give primitive root in future.")
                         return AlgebraicReal(ANRational((-rt)**n))
                     else:
                         z = QQbar.zeta(2 * d)._pow_int(n)

@kwankyu
Copy link
Collaborator

kwankyu commented Aug 28, 2024

Please rebase to the develop branch.

@kwankyu
Copy link
Collaborator

kwankyu commented Aug 28, 2024

Please update the PR title and the description, as the deprecation warnings will point to this PR. To avoid confusion, you may keep the original description below the new one.

@user202729 user202729 changed the title AlgebraicConverter: Avoid raising negative AlgebraicReal to fractional power Give deprecation warning when raising negative AlgebraicReal to fractional odd power Aug 28, 2024
@user202729
Copy link
Contributor Author

user202729 commented Aug 28, 2024

Actually there's one more

sage: ZZ(-1)^(1/3)
(-1)^(1/3)
sage: ZZ(-1).nth_root(3)
-1

Should we also give warning?

(though there's a difference, nth_root for ZZ and QQ currently give error if the result is not in the original field.)

Because of how the current code is implemented, AA(-1).nth_root(3) will give a warning (and have its behavior change once we change the behavior of **) anyway. What's the desired behavior here?


Meanwhile:

sage: AA(-1).nth_root(2)
I

unlike what happens for ZZ(-1).nth_root(2) (gives error).


Any idea why does the AA((-sqrt(27)+1)^(5/3)) test fail? In my local testing it does give the warning.

@kwankyu
Copy link
Collaborator

kwankyu commented Aug 28, 2024

Actually there's one more

sage: ZZ(-1)^(1/3)
(-1)^(1/3)
sage: ZZ(-1).nth_root(3)
-1

Should we also give warning?

I don't think so. The doc of nth_root clearly says that the operation is within reals. nth_root for elements in ZZ and QQ seems to be treated separately in sage, similarly like

sage: ZZ(-1)^(1/3)
(-1)^(1/3)
sage: QQ(-1)^(1/3)
(-1)^(1/3)

Anyway we are not "fix"-ing this.

@kwankyu
Copy link
Collaborator

kwankyu commented Aug 28, 2024

Any idea why does the AA((-sqrt(27)+1)^(5/3)) test fail? In my local testing it does give the warning.

It seems that deprecation warnings are skipped in "TESTS" block...

@kwankyu
Copy link
Collaborator

kwankyu commented Aug 28, 2024

Any idea why does the AA((-sqrt(27)+1)^(5/3)) test fail? In my local testing it does give the warning.

It seems that deprecation warnings are skipped in "TESTS" block...

@mkoeppe ?

@nbruin
Copy link
Contributor

nbruin commented Aug 28, 2024

Actually there's one more

sage: ZZ(-1)^(1/3)
(-1)^(1/3)
sage: ZZ(-1).nth_root(3)
-1

Should we also give warning?

(though there's a difference, nth_root for ZZ and QQ currently give error if the result is not in the original field.)

Because of how the current code is implemented, AA(-1).nth_root(3) will give a warning (and have its behavior change once we change the behavior of **) anyway. What's the desired behavior here?

Meanwhile:

sage: AA(-1).nth_root(2)
I

unlike what happens for ZZ(-1).nth_root(2) (gives error).

Any idea why does the AA((-sqrt(27)+1)^(5/3)) test fail? In my local testing it does give the warning.

It looks like you haven't formatted the expected output exactly as given in the example for deprecation:

doctest:...: DeprecationWarning: the function foo is replaced by bar

Perhaps you get better results if you follow that formatting exactly?

@kwankyu

This comment was marked as resolved.

@kwankyu

This comment was marked as resolved.

@nbruin
Copy link
Contributor

nbruin commented Sep 3, 2024

It would be good to have a really convincing real world example where the changed behaviour is really essential.

There is no such thing. Note that we have lived with the inconsistency more than a decade. I can live and die undisturbed with the inconsistency. Dima claims that (-1)**(1/3) giving the principal root is a design flaw of python. He is living well with it :-)

In that case I think the cons of changing outweigh the pros of the change and then we're better off keeping the current behaviour for the sake of not disturbing user code and/or the annoyance of a deprecation message. Perhaps until someone finds a convincing reason to overcome the cons. @mezzarobba had some more spirited argument for changing rational (odd denominator) exponentiation behaviour on AA.

@kwankyu
Copy link
Collaborator

kwankyu commented Sep 3, 2024

The inconsistency has been repeatedly demonstrated in my comments here and in #38564. I think the inconsistency justifies changing the behavior. How one weighs solving inconsistency vs keeping the status quo depends on one's value system. Each one has his own preference. I can't change that.

@JohnCremona
Copy link
Member

JohnCremona commented Sep 3, 2024 via email

@kwankyu
Copy link
Collaborator

kwankyu commented Sep 3, 2024

I don't think we all agree that there an inconsistency. John —

Yes. That is why we are voting now. I count your comment as -1 vote.

@user202729
Copy link
Contributor Author

There are two parts of the behavior here. One is AA(-1)^(1/3) and the other is AA((-1)^(1/3)).

People seems to be -1 the first one, but what do you think about the second one?

Note that not fixing that one will make the diagram

SR ---> AA
   \    |
    \   |
     v  v
      QQbar

non-commutative.

@nbruin
Copy link
Contributor

nbruin commented Sep 4, 2024

Probably useful for people who try to make their minds up:

sage: parent(AA(-2)^RealField(1000)(1/3))
Complex Field with 1000 bits of precision

The coercion system in sage is so eager that it coerces -2 into RealField(1000) in order to evaluate this. I was expecting this to raise an error, but it does not. So sage forces our hand by having the exponent space QQ embedded in the reals, so it gets a real topology. So unless we curtail the coercion system with respect to the exponentiation operator, I think our hand is forced to follow the branch choice on RealField.

@kwankyu
Copy link
Collaborator

kwankyu commented Sep 4, 2024

The coercion in powering was introduced by Jeroen Demeyer in 2017 #24247.

@hgranath
Copy link
Contributor

hgranath commented Sep 4, 2024

I think certain examples and arguments here are a bit misleading for the discussion at hand. What I, and I assume other number theorists, care so much about is the case x**r where x is in AA and r is a Rational (and of course this is the only [1] case I will ever care about in this context).

Any examples in pure python, or e.g. this example,

sage: parent(AA(-2)^RealField(1000)(1/3))
Complex Field with 1000 bits of precision

is about x**f where f is (some kind of) floating point number. That has absolutely no meaning in AA or QQbar (and Sagemath apparently does the only reasonable thing and casts/coerces x to a float before taking the power, I see no problem with that).

[1] Gelfond–Schneider theorem

@kwankyu
Copy link
Collaborator

kwankyu commented Sep 4, 2024

... What I, and I assume other number theorists, care so much about is the case x**r where x is in AA and r is a Rational

After deprecation, you may use x.pow(r) for the computation you care. What you really care is the notation ** or ^?

@JohnCremona
Copy link
Member

I think certain examples and arguments here are a bit misleading for the discussion at hand. What I, and I assume other number theorists, care so much about is the case x**r where x is in AA and r is a Rational (and of course this is the only [1] case I will ever care about in this context).

Any examples in pure python, or e.g. this example,

sage: parent(AA(-2)^RealField(1000)(1/3))
Complex Field with 1000 bits of precision

is about x**f where f is (some kind of) floating point number. That has absolutely no meaning in AA or QQbar (and Sagemath apparently does the only reasonable thing and casts/coerces x to a float before taking the power, I see no problem with that).

[1] Gelfond–Schneider theorem

I agree with this. When a is something algebraic (not a floating point in RR or CC) and I see a^(1/n) or a**(1/n) I interpret it is "a root of the polynomial X^n-a"; so if there is just one such root in a's parent (e.g. AA) then that's what I expect to see.

@kwankyu
Copy link
Collaborator

kwankyu commented Sep 4, 2024

Do you think the current behavior

sage: AA(-1)^(1/4)
0.7071067811865475? + 0.7071067811865475?*I

should be fixed?

@nbruin
Copy link
Contributor

nbruin commented Sep 4, 2024

I think certain examples and arguments here are a bit misleading for the discussion at hand. What I, and I assume other number theorists, care so much about is the case x**r where x is in AA and r is a Rational (and of course this is the only [1] case I will ever care about in this context).

I agree, but the problem is that sage automatically coerces to floats, so implicitly QQ (for exponents) is imbued with the real metric. That means people can use AA-elements seamlessly in contexts where the continuity in the exponent is expected. To illustrate:

sage: eps=RR(2^-50)
sage: abs(AA(-2)^(1/3)-AA(-2)^(1/3+eps))  #notice the large jump here?
2.18224727194344
sage: abs(AA(-2)^(1/3+eps)-AA(-2)^(1/3+2*eps))
3.53358019796514e-15

The coercion framework is a little too eager. I think it should not just by default coerce into a common parent for exponentiation. It's rare that exponentiation designates a binary operation instead of an action. But that's such an invasive change that we'd need a rather convincing reason to implement that. (Jeroen's argument was: multiplication also often designates an action and there we try common parent as well. I'm not sure I'm convinced by that reasoning. Rings are rather central objects in mathematics)

@kwankyu
Copy link
Collaborator

kwankyu commented Sep 4, 2024

I am curious if number theorists are happy with

sage: a = QQbar(1)
sage: (AA(-2) + a - a)^(1/3)
0.6299605249474365? + 1.091123635971722?*I
sage: AA(-2)^(1/3)
-1.259921049894873?

I guess such a weird example cannot be constructed in sage without using AA.

@JohnCremona
Copy link
Member

I should not presume to speak for all number theorists, but it is likely that they would be happy that for a in any exact field or ring (including AA and QQbar, number fields, fine fields), we would only allow a**e or a^e for integer exponents e. That only uses the ring structure. For rational powers, which may not exist or be unique within the same ring or field, we can have methods like nth_root() or pow() as shortcuts to getting roots of polynomials like X^n-a or X^d-a^n (for rational exponent n/D).

All the issues about choices of branch are relevant over CC (or RR as a subfield of CC perhaps) but not for exact fields.

@dimpase
Copy link
Member

dimpase commented Sep 4, 2024

Mixing QQbar and AA in one expression is asking for 😵‍💫

@kwankyu
Copy link
Collaborator

kwankyu commented Sep 4, 2024

All the issues about choices of branch are relevant over CC (or RR as a subfield of CC perhaps) but not for exact fields.

For a happy way out, see https://groups.google.com/g/sage-devel/c/s81ieq2vpVo/m/GNw9w_jpAAAJ

Summary: we embrace the inconsistency between analytical fields and exact (algebraic) fields as features of sage.

@hgranath
Copy link
Contributor

hgranath commented Sep 4, 2024

sage: eps=RR(2^-50)
sage: abs(AA(-2)^(1/3)-AA(-2)^(1/3+eps)) #notice the large jump here?
2.18224727194344
sage: abs(AA(-2)^(1/3+eps)-AA(-2)^(1/3+2*eps))
3.53358019796514e-15

I guess the problem here is again with the case of x**f for floats f. If x is in AA but is negative, maybe it should not be allowed at all, so maybe throw a ValueError or something?

If something like this happened in my computations I would take it as a sign that I am doing something very stupid, so it would be good to be clearly alerted :-)

@nbruin nbruin closed this Sep 4, 2024
@nbruin nbruin reopened this Sep 4, 2024
@nbruin
Copy link
Contributor

nbruin commented Sep 4, 2024

apologies. I clicked the wrong button which "closed" the PR. I've reopened it. As far as I can see it's now restored to its original state, but let me know if there is something further I should do to restore.

@kwankyu
Copy link
Collaborator

kwankyu commented Sep 4, 2024

No problem. That happens :-)

@kwankyu
Copy link
Collaborator

kwankyu commented Sep 5, 2024

The tally is:

negative votes: @JohnCremona @dimpase @hgranath
positive votes: @kwankyu @user202729

Thank you for your participation in the discussion. It seems that algebraists like the current behavior of AA(-1)^(1/3) and think how other analytical fields behave as irrelevant. That is understandable.

@user202729 Sorry for the outcome. It is wise that you try to solve your problem at hand in another PR #38606. I hope that number theorists here pay attention to the PR.

@nbruin
Copy link
Contributor

nbruin commented Sep 5, 2024

I'm still on the fence and I think other people might change their opinion as well, depending on the further evidence that arises. I think the discussion here shows that the majority of the participants prefer a branch choice in the algebraic reals that does not extend the base field if possible, and this is also the documented behaviour for AA. That's for AA in isolation.

Note that for interactive work or for intentional code designed specifically for a particular computation, one can freely choose to use nth_root or ^, depending on what gives the desired result. The change is the painful part in those cases; particular in code that has been written for sage and is not part of sage, since that won't be picked up with doctests.

The real complications from design choices around ^ come from examples that implicitly use the coercion framework -- coercion usually only gets complicated from the second level onwards, at least for unintended consequences.

For instance, people may not have good control over whether an element gets created in AA or inQQbar: for a polynomial f=x^3+x+1 over QQ, the command f.roots(QQbar) gives back one root that has parent AA and two others that lie in QQbar.
EDIT thanks to @hgranath 's comment below: no this is not the case. "Algebraic Field" is QQbar, whereas AA prints as "Algebraic Real Field". Roots does what it needs to do. (We shouldn't have used AA for the algebraic reals, though. Something like AR would have been less prone to confusion)

If someone came up with a reason to evaluate, say, Puiseux polynomials (an expression like x^(1/5)+x^(2/5)) at the result, they would be exposed to the different branch cuts between AA and QQbar without asking for it. I'm currently not convinced by this example because I don't see a mathematical purpose in doing so and if there were I would expect one would be better off doing this with floats anyway, but I can imagine that an example along these lines that does make mathematical sense, or something else, would show quite nasty complications.

@hgranath
Copy link
Contributor

hgranath commented Sep 5, 2024 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c: number fields disputed PR is waiting for community vote, see https://groups.google.com/g/sage-devel/c/IgBYUJl33SQ v: minimal
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants