Repository: sympy/sympy

Issue:
ask(Q.algebraic(...)) wrongly returns False for 1 to the power of an algebraic number
```python
>>>from sympy import Symbol, Pow, ask, Q
>>>x = Symbol('x', algebraic=True)
>>>ask(Q.algebraic(Pow(1, x, evaluate=False)))
False
```

 1 to the power of anything  is usually algebraic number.

Discussion:
sympy-bot: 

Hi, I am the [SymPy bot](https://github.com/sympy/sympy-bot). I'm here to help you write a release notes entry. Please read the [guide on how to write release notes](https://github.com/sympy/sympy/wiki/Writing-Release-Notes).

:x: There was an issue with the release notes. **Please do not close this pull request;** instead edit the description after reading the [guide on how to write release notes](https://github.com/sympy/sympy/wiki/Writing-Release-Notes).

* The `<!-- BEGIN RELEASE NOTES -->` block was not found

<details><summary>Click here to see the pull request description that was parsed.</summary>



    <!-- Your title above should be a short description of what
    was changed. Do not include the issue number in the title. -->

    #### References to other Issues or PRs
    Fixes #27445


    #### Brief description of what is fixed or changed
    This pull request fixes issue #27445, where ask(Q.algebraic(Pow(1, e, evaluate=False))) incorrectly returns False. The problem arises because the current implementation of the Q.algebraic handler for Pow expressions does not account for the special case where the base is 1.

    #### Other comments
    This fix ensures that the mathematical properties of \( 1^b \) are respected, which always evaluates to 1, and therefore should be considered algebraic.


    #### Release Notes

    NO ENTRY



</details><p>

TiloRC: You should add a release notes entry.
https://github.com/sympy/sympy/wiki/Writing-Release-Notes
TiloRC: See Oscar's comment for the right fix: https://github.com/sympy/sympy/issues/27445#issuecomment-2614147352.

You should add Oscar as a coauthor. See https://docs.sympy.org/latest/contributing/new-contributors-guide/workflow-process.html#co-author.
oscarbenjamin: > You should add Oscar as a coauthor.

It isn't necessary to add me as a co-author. I don't need any acknowledgement.
TiloRC: Oh, you should also add a regression test. See https://docs.sympy.org/latest/contributing/new-contributors-guide/workflow-process.html#workflow-process-add-tests. 
krishnavbajoria02:  I have added the regression test and fixed the code issue. Please review it.
TiloRC: pi**n is known to be algebraic for positive integers:
- https://math.stackexchange.com/questions/1875840/references-on-pin-is-transcendental?noredirect=1&lq=1
- https://math.stackexchange.com/questions/31798/prove-that-pi-is-a-transcendental-number

We can add a check for this like the existing check for e
krishnavbajoria02: > pi**n is known to be algebraic for positive integers:

@TiloRC 
You mean not algebraic right?


TiloRC: I would recommend running the tests locally on your computer: https://docs.sympy.org/latest/contributing/new-contributors-guide/workflow-process.html#workflow-process-run-tests
krishnavbajoria02: @TiloRC 
I am sorry for this.. I am relatively new to open source so have been facing this issue. I was trying to run locally only by trying to follow these instructions but I think every time I make changes to this branch the test automatically gets called here..do I have to create another branch to test it (using pytest) as I am not sure about this.
krishnavbajoria02: @TiloRC 
Any more changes required ?
oscarbenjamin: Please use commit messages that explain what is actually changed. None of these commit messages explains what is changed:
https://github.com/sympy/sympy/pull/27511/commits
krishnavbajoria02: @oscarbenjamin @TiloRC 
I have updated the commit messages. Please review them.
oscarbenjamin: It would be better at this point to squash all the commits into one commit with a single commit message.

Patch:
diff --git a/.github/workflows/emscripten.yml b/.github/workflows/emscripten.yml
index 5bbffd4ed5..e952a94530 100644
--- a/.github/workflows/emscripten.yml
+++ b/.github/workflows/emscripten.yml
@@ -27,12 +27,12 @@ jobs:
         splits: [4] # keep in sync with number of groups below
         group: [1, 2, 3, 4]
     env:
-      PYODIDE_VERSION: 0.27.2
+      PYODIDE_VERSION: 0.27.0a2
       # PYTHON_VERSION and EMSCRIPTEN_VERSION are determined by PYODIDE_VERSION.
       # The appropriate versions can be found in the Pyodide repodata.json
       # "info" field, or in Makefile.envs:
-      # https://github.com/pyodide/pyodide/blob/d85f9cda735e47f16b3f032832fab73bd1c12a30/Makefile.envs#L1-L3
-      PYTHON_VERSION: 3.12 # any Python 3.12.x version works
+      # https://github.com/pyodide/pyodide/blob/main/Makefile.envs#L2
+      PYTHON_VERSION: 3.12.1
       EMSCRIPTEN_VERSION: 3.1.58
       NODE_VERSION: 20
     steps:
@@ -54,7 +54,7 @@ jobs:
         run: pip install pyodide-build
 
       - name: Set up Node.js
-        uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
+        uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
         with:
           node-version: ${{ env.NODE_VERSION }}
 
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index c9e1a9531f..a91ee99f97 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -142,7 +142,7 @@ jobs:
         # It is recommended to pin a commit hash here for security but it
         # should be kept up to date. Probably all actions and dependencies
         # used by the build script should be pinned...
-        uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc  # v1.12.4
+        uses: pypa/gh-action-pypi-publish@67339c736fd9354cd4f8cb0b744f2b82a74b5c70  # v1.12.3
 
   # -------------------- Make a GitHub release --------------------- #
 
diff --git a/.mailmap b/.mailmap
index 46892edcb4..4432cb2b22 100644
--- a/.mailmap
+++ b/.mailmap
@@ -620,7 +620,6 @@ G. D. McBain <gdmcbain@protonmail.com>
 Gabriel Bernardino <gabriel.bernardino@upf.edu>
 Gabriel Orisaka <orisaka@gmail.com>
 Gaetano Guerriero <x.guerriero@tin.it>
-Gagan Mishra <simonsimple305@gmail.com> Gagan Mishra <70949548+GaganMishra305@users.noreply.github.com>
 Gagandeep Singh <singh.23@iitj.ac.in> Gagandeep Singh <36567889+czgdp1807@users.noreply.github.com>
 Gagandeep Singh <singh.23@iitj.ac.in> Gagandeep Singh <czgdp1807@gmail.com>
 Gagandeep Singh <singh.23@iitj.ac.in> Gagandeep Singh <gdp.1807@gmail.com>
@@ -849,7 +848,6 @@ Kalevi Suominen <jksuom@gmail.com> <kalevi.suominen@helsinki.fi>
 Kamlesh Joshi <72374645+kamleshjoshi8102@users.noreply.github.com>
 Karan <grgkaran03@gmail.com>
 Karan <grgkaran03@gmail.com>
-Karan Anand <anandkarancompsci@gmail.com> Karan Anand <119553199+anandkaranubc@users.noreply.github.com>
 Karan Sharma <karan1276@gmail.com>
 Karthik Chintapalli <karthik.chintapalli@students.iiit.ac.in> <karthik.chintapalli@gmail.com>
 Kartik Sethi <kartiks31416@gmail.com> Kartik Sethi <37146279+ks147@users.noreply.github.com>
@@ -883,6 +881,7 @@ Konrad Meyer <konrad.meyer@gmail.com>
 Konstantin Togoi <konstantin.togoi@gmail.com> <togoi.konstantin@outlook.com>
 Konstantinos Riganas <kostasriganas24@gmail.com> kostas-rigan <t8200145@aueb.gr>
 Kris Katterjohn <katterjohn@gmail.com>
+Krishnav Bajoria <bajoriakrishnav@gmail.com> Krishnav Bajoria <127018567+krishnavbajoria02@users.noreply.github.com>
 Kristian Brünn <hello@kristianbrunn.com> Kristianmitk <hello@kristianbrunn.com>
 Krit Karan <kritkaran.b13@iiits.in> <kritkaran94@users.noreply.github.com>
 Kshitij <kshitijparwani.mat18@itbhu.ac.in> Kshitij Parwani <44468674+P-Kshitij@users.noreply.github.com>
diff --git a/sympy/assumptions/handlers/ntheory.py b/sympy/assumptions/handlers/ntheory.py
index 38cedecd49..ccb91f726e 100644
--- a/sympy/assumptions/handlers/ntheory.py
+++ b/sympy/assumptions/handlers/ntheory.py
@@ -106,11 +106,8 @@ def _(expr, assumptions):
                 return
             # Positive integer which is not prime is not
             # necessarily composite
-            _is_one = ask(Q.eq(expr, 1), assumptions)
-            if _is_one:
+            if expr.equals(1):
                 return False
-            if _is_one is None:
-                return None
             return not _prime
         else:
             return _integer
diff --git a/sympy/assumptions/handlers/sets.py b/sympy/assumptions/handlers/sets.py
index 094a026838..1bc1f25d76 100644
--- a/sympy/assumptions/handlers/sets.py
+++ b/sympy/assumptions/handlers/sets.py
@@ -262,7 +262,6 @@ def _(expr, assumptions):
     """
     * Real**Integer              -> Real
     * Positive**Real             -> Real
-    * Negative**Real             -> ?
     * Real**(Integer/Even)       -> Real if base is nonnegative
     * Real**(Integer/Odd)        -> Real
     * Imaginary**(Integer/Even)  -> Real
@@ -319,6 +318,8 @@ def _(expr, assumptions):
                 return True
             elif ask(Q.positive(expr.base), assumptions):
                 return True
+            elif ask(Q.negative(expr.base), assumptions):
+                return False
 
 @RealPredicate.register_many(cos, sin)
 def _(expr, assumptions):
@@ -740,7 +741,21 @@ def _(expr, assumptions):
         if ask(Q.algebraic(expr.exp), assumptions):
             return ask(~Q.nonzero(expr.exp), assumptions)
         return
-    return expr.exp.is_Rational and ask(Q.algebraic(expr.base), assumptions)
+    if expr.base == pi:
+        if ask(Q.integer(expr.exp), assumptions) and ask(Q.positive(expr.exp), assumptions):
+            return False
+        return
+    exp_rational = ask(Q.rational(expr.exp), assumptions)
+    base_algebraic = ask(Q.algebraic(expr.base), assumptions)
+    exp_algebraic = ask(Q.algebraic(expr.exp),assumptions)
+    if base_algebraic and exp_algebraic:
+        if exp_rational:
+            return True
+        # Check based on the Gelfond-Schneider theorem:
+        # If the base is algebraic and not equal to 0 or 1, and the exponent
+        # is irrational,then the result is transcendental.
+        if ask(Q.ne(expr.base,0) & Q.ne(expr.base,1)) and exp_rational is False:
+            return False
 
 @AlgebraicPredicate.register(Rational) # type:ignore
 def _(expr, assumptions):
diff --git a/sympy/core/relational.py b/sympy/core/relational.py
index 28bf039c9b..d09ca82388 100644
--- a/sympy/core/relational.py
+++ b/sympy/core/relational.py
@@ -52,8 +52,6 @@ def _canonical_coeff(rel):
     rel = rel.canonical
     if not rel.is_Relational or rel.rhs.is_Boolean:
         return rel  # Eq(x, True)
-    if not isinstance(rel.lhs, Expr):
-        return rel.reversed  # e.g.: Eq(True, x) -> Eq(x, True)
     b, l = rel.lhs.as_coeff_Add(rational=True)
     m, lhs = l.as_coeff_Mul(rational=True)
     rhs = (rel.rhs - b)/m
diff --git a/sympy/parsing/latex/lark/grammar/latex.lark b/sympy/parsing/latex/lark/grammar/latex.lark
index 43e8d0e910..5db1dfa5fb 100644
--- a/sympy/parsing/latex/lark/grammar/latex.lark
+++ b/sympy/parsing/latex/lark/grammar/latex.lark
@@ -346,7 +346,7 @@ log: FUNC_LOG _expression
 square_root: FUNC_SQRT group_curly_parentheses
     | FUNC_SQRT group_square_brackets group_curly_parentheses
 
-factorial: _expression_func BANG
+factorial: _expression BANG
 
 conjugate: CMD_OVERLINE group_curly_parentheses
     | CMD_OVERLINE DIGIT
@@ -400,4 +400,4 @@ matrix_row: _expression (MATRIX_COL_DELIM _expression)*
 determinant: (CMD_DETERMINANT_BEGIN matrix_body CMD_DETERMINANT_END)
     | FUNC_DETERMINANT _expression
 trace: FUNC_MATRIX_TRACE _expression
-adjugate: FUNC_MATRIX_ADJUGATE _expression
+adjugate: FUNC_MATRIX_ADJUGATE _expression
\ No newline at end of file
diff --git a/sympy/printing/precedence.py b/sympy/printing/precedence.py
index d22d5746ae..563a04b349 100644
--- a/sympy/printing/precedence.py
+++ b/sympy/printing/precedence.py
@@ -59,11 +59,6 @@
 
 
 def precedence_Mul(item):
-    from sympy.core.function import Function
-    if any(hasattr(arg, 'precedence') and isinstance(arg, Function) and
-           arg.precedence < PRECEDENCE["Mul"] for arg in item.args):
-        return PRECEDENCE["Mul"]
-
     if item.could_extract_minus_sign():
         return PRECEDENCE["Add"]
     return PRECEDENCE["Mul"]


Test patch:
diff --git a/sympy/assumptions/tests/test_query.py b/sympy/assumptions/tests/test_query.py
index 14f1d88c94..60fd89f59c 100644
--- a/sympy/assumptions/tests/test_query.py
+++ b/sympy/assumptions/tests/test_query.py
@@ -420,7 +420,7 @@ def test_pi():
     assert ask(Q.commutative(z)) is True
     assert ask(Q.integer(z)) is False
     assert ask(Q.rational(z)) is False
-    assert ask(Q.algebraic(z)) is False
+    assert ask(Q.algebraic(z)) is None
     assert ask(Q.real(z)) is True
     assert ask(Q.complex(z)) is True
     assert ask(Q.irrational(z)) is True
@@ -1107,11 +1107,6 @@ def test_bounded():
     assert ask(Q.finite(cos(x) + sin(x))) is True
 
 
-def test_issue_27441():
-    # https://github.com/sympy/sympy/issues/27441
-    assert ask(Q.composite(y), Q.integer(y) & Q.positive(y) & ~Q.prime(y)) is None
-
-
 @XFAIL
 def test_bounded_xfail():
     """We need to support relations in ask for this to work"""
@@ -1912,19 +1907,16 @@ def test_real_pow():
     assert ask(Q.real(x**y), Q.imaginary(x) & Q.odd(y)) is False
     assert ask(Q.real(x**y), Q.imaginary(x) & Q.even(y)) is True
     assert ask(Q.real(x**(y/z)), Q.real(x) & Q.real(y/z) & Q.rational(y/z) & Q.even(z) & Q.positive(x)) is True
-    assert ask(Q.real(x**(y/z)), Q.real(x) & Q.rational(y/z) & Q.even(z) & Q.negative(x)) is None
+    assert ask(Q.real(x**(y/z)), Q.real(x) & Q.rational(y/z) & Q.even(z) & Q.negative(x)) is False
     assert ask(Q.real(x**(y/z)), Q.real(x) & Q.integer(y/z)) is True
     assert ask(Q.real(x**(y/z)), Q.real(x) & Q.real(y/z) & Q.positive(x)) is True
-    assert ask(Q.real(x**(y/z)), Q.real(x) & Q.real(y/z) & Q.negative(x)) is None
+    assert ask(Q.real(x**(y/z)), Q.real(x) & Q.real(y/z) & Q.negative(x)) is False
     assert ask(Q.real((-I)**i), Q.imaginary(i)) is True
     assert ask(Q.real(I**i), Q.imaginary(i)) is True
     assert ask(Q.real(i**i), Q.imaginary(i)) is None  # i might be 2*I
     assert ask(Q.real(x**i), Q.imaginary(i)) is None  # x could be 0
     assert ask(Q.real(x**(I*pi/log(x))), Q.real(x)) is True
 
-    # https://github.com/sympy/sympy/issues/27485
-    assert ask(Q.real(n**p), Q.negative(n) & Q.positive(p)) is None
-
 
 @_both_exp_pow
 def test_real_functions():
@@ -2000,7 +1992,7 @@ def test_algebraic():
     assert ask(Q.algebraic(sqrt(1 + I*sqrt(3)))) is True
 
     assert ask(Q.algebraic(1 + I*sqrt(3)**Rational(17, 31))) is True
-    assert ask(Q.algebraic(1 + I*sqrt(3)**(17/pi))) is False
+    assert ask(Q.algebraic(1 + I*sqrt(3)**(17/pi))) is None
 
     for f in [exp, sin, tan, asin, atan, cos]:
         assert ask(Q.algebraic(f(7))) is False
@@ -2021,7 +2013,7 @@ def test_algebraic():
         assert ask(Q.algebraic(h(7, evaluate=False))) is False
         assert ask(Q.algebraic(h(x)), Q.algebraic(x)) is False
 
-    assert ask(Q.algebraic(sqrt(sin(7)))) is False
+    assert ask(Q.algebraic(sqrt(sin(7)))) is None
     assert ask(Q.algebraic(sqrt(y + I*sqrt(7)))) is None
 
     assert ask(Q.algebraic(2.47)) is True
@@ -2029,6 +2021,14 @@ def test_algebraic():
     assert ask(Q.algebraic(x), Q.transcendental(x)) is False
     assert ask(Q.transcendental(x), Q.algebraic(x)) is False
 
+    #https://github.com/sympy/sympy/issues/27445
+    assert ask(Q.algebraic(Pow(1, x, evaluate=False)), Q.algebraic(x)) is None
+    assert ask(Q.algebraic(Pow(x, y))) is None
+    assert ask(Q.algebraic(Pow(1, x, evaluate=False))) is None
+    assert ask(Q.algebraic(x**(pi*I))) is None
+    assert ask(Q.algebraic(pi**n),Q.integer(n) & Q.positive(n)) is False
+    assert ask(Q.algebraic(x**y),Q.algebraic(x) & Q.rational(y)) is True
+
 
 def test_global():
     """Test ask with global assumptions"""
diff --git a/sympy/functions/elementary/tests/test_piecewise.py b/sympy/functions/elementary/tests/test_piecewise.py
index 7d07280955..d06dc832e1 100644
--- a/sympy/functions/elementary/tests/test_piecewise.py
+++ b/sympy/functions/elementary/tests/test_piecewise.py
@@ -1335,17 +1335,6 @@ def _ITE(*args):
     raises(ValueError, lambda: _ITE((a, x < 2), (b, x > 3)))
 
 
-def test_Piecewise_replace_relational_27538():
-    x, y = symbols('x, y')
-    p1 = Piecewise(
-        (0, Eq(x, True)),
-        (1, True),
-    )
-    p2 = p1.xreplace({x: y < 1})
-    assert p2.subs(y, 0) == 0
-    assert p2.subs(y, 1) == 1
-
-
 def test_issue_14052():
     assert integrate(abs(sin(x)), (x, 0, 2*pi)) == 4
 
diff --git a/sympy/matrices/expressions/tests/test_matmul.py b/sympy/matrices/expressions/tests/test_matmul.py
index 813926e2c8..7a7b0e5e08 100644
--- a/sympy/matrices/expressions/tests/test_matmul.py
+++ b/sympy/matrices/expressions/tests/test_matmul.py
@@ -184,10 +184,3 @@ def test_shape_error():
     A = MatrixSymbol('A', 2, 2)
     B = MatrixSymbol('B', 3, 3)
     raises(ShapeError, lambda: MatMul(A, B))
-
-
-def test_matmul_transpose():
-    # https://github.com/sympy/sympy/issues/9503
-    M = Matrix(2, 2, [1, 2 + I, 3, 4])
-    a = Symbol('a')
-    assert (MatMul(a, M).T).expand() == (a*Matrix([[1, 3],[2 + I, 4]])).expand()
diff --git a/sympy/parsing/tests/test_latex_lark.py b/sympy/parsing/tests/test_latex_lark.py
index dd1f72a66c..ebf60ba034 100644
--- a/sympy/parsing/tests/test_latex_lark.py
+++ b/sympy/parsing/tests/test_latex_lark.py
@@ -386,8 +386,7 @@ def _MatMul(a, b):
     (r"(x + 1)!", factorial(x + 1)),
     (r"(x!)!", factorial(factorial(x))),
     (r"x!!!", factorial(factorial(factorial(x)))),
-    (r"5!7!", factorial(5) * factorial(7)),
-    (r"24! \times 24!", factorial(24) * factorial(24))
+    (r"5!7!", factorial(5) * factorial(7))
 ]
 
 UNEVALUATED_SUM_EXPRESSION_PAIRS = [
diff --git a/sympy/printing/tests/test_precedence.py b/sympy/printing/tests/test_precedence.py
index d08ea07483..372a5b0356 100644
--- a/sympy/printing/tests/test_precedence.py
+++ b/sympy/printing/tests/test_precedence.py
@@ -1,6 +1,6 @@
 from sympy.concrete.products import Product
 from sympy.concrete.summations import Sum
-from sympy.core.function import Derivative, Function
+from sympy.core.function import Derivative
 from sympy.core.numbers import Integer, Rational, Float, oo
 from sympy.core.relational import Rel
 from sympy.core.symbol import symbols
@@ -87,42 +87,3 @@ def test_And_Or():
     assert precedence(x & y) == PRECEDENCE["And"]
     assert precedence(x | y) == PRECEDENCE["Or"]
     assert precedence(~y) == PRECEDENCE["Not"]
-
-
-def test_custom_function_precedence_comparison():
-    """
-    Test cases for custom functions with different precedence values,
-    specifically handling:
-    1. Functions with precedence < PRECEDENCE["Mul"] (50)
-    2. Functions with precedence = Func (70)
-
-    Key distinction:
-    1. Lower precedence functions (45) need parentheses: -2*(x F y)
-    2. Higher precedence functions (70) don't: -2*x F y
-    """
-    class LowPrecedenceF(Function):
-        precedence = PRECEDENCE["Mul"] - 5
-        def _sympystr(self, printer):
-            return f"{printer._print(self.args[0])} F {printer._print(self.args[1])}"
-
-    class HighPrecedenceF(Function):
-        precedence = PRECEDENCE["Func"]
-        def _sympystr(self, printer):
-            return f"{printer._print(self.args[0])} F {printer._print(self.args[1])}"
-
-    def test_low_precedence():
-        expr1 = 2 * LowPrecedenceF(x, y)
-        assert str(expr1) == "2*(x F y)"
-
-        expr2 = -2 * LowPrecedenceF(x, y)
-        assert str(expr2) == "-2*(x F y)"
-
-    def test_high_precedence():
-        expr1 = 2 * HighPrecedenceF(x, y)
-        assert str(expr1) == "2*x F y"
-
-        expr2 = -2 * HighPrecedenceF(x, y)
-        assert str(expr2) == "-2*x F y"
-
-    test_low_precedence()
-    test_high_precedence()