sympy / sympy

A computer algebra system written in pure Python
https://sympy.org/
Other
12.49k stars 4.32k forks source link

factor should use the same domain for numerator and denominator #26577

Open oscarbenjamin opened 3 weeks ago

oscarbenjamin commented 3 weeks ago

After fixing gh-26497 I find the following for the OP example:

In [16]: expr
Out[16]: 
    4  2        4               4           3  2         3                     3           2        ↪
  aₙ ⋅m     2⋅aₙ ⋅m     4     aₙ      2⋅ⅈ⋅aₙ ⋅m    4⋅ⅈ⋅aₙ ⋅m         3   2⋅ⅈ⋅aₙ    2⋅ⅈ⋅aₙ⋅m    4⋅ⅈ⋅ ↪
- ─────── - ─────── - aₙ  - ─────── + ────────── + ───────── + 2⋅ⅈ⋅aₙ  + ─────── + ───────── + ──── ↪
    2         2               2          2            2                    2          2          2  ↪
  aₙ  + 1   aₙ  + 1         aₙ  + 1    aₙ  + 1      aₙ  + 1              aₙ  + 1    aₙ  + 1    aₙ   ↪

↪                              2                           
↪ aₙ⋅m            2⋅ⅈ⋅aₙ      m         2⋅m            1   
↪ ──── + 2⋅ⅈ⋅aₙ + ─────── + ─────── + ─────── + 1 + ───────
↪                   2         2         2             2    
↪ + 1             aₙ  + 1   aₙ  + 1   aₙ  + 1       aₙ  + 1

In [17]: expr.factor()
Out[17]: 
         3          ⎛  2    2          ⎞ 
-(aₙ - ⅈ) ⋅(aₙ + ⅈ)⋅⎝aₙ  + m  + 2⋅m + 2⎠ 
─────────────────────────────────────────
                   2                     
                 aₙ  + 1                 

In [18]: expr.cancel()
Out[18]: 
    4         3     2  2       2       2           2                        2          
- aₙ  + 2⋅ⅈ⋅aₙ  - aₙ ⋅m  - 2⋅aₙ ⋅m - aₙ  + 2⋅ⅈ⋅aₙ⋅m  + 4⋅ⅈ⋅aₙ⋅m + 4⋅ⅈ⋅aₙ + m  + 2⋅m + 2

In [19]: expr.cancel().factor()
Out[19]: 
         2 ⎛  2    2          ⎞
-(aₙ - ⅈ) ⋅⎝aₙ  + m  + 2⋅m + 2⎠

I believe that all of these expressions are equivalent but ideally expr.cancel.factor() and expr.factor() would give the same result. In other words factor should make cancel redundant and both should be able to eliminate any cancelable demoninators. The fact that factor does not remove the denominator is because the numerator and denominator are not being factored over the same field. Otherwise an**2 + 1 in the denominator would factor:

In [21]: factor(a_n**2 + 1)
Out[21]: 
  2    
aₙ  + 1

In [22]: factor(a_n**2 + 1, domain=QQ_I)
Out[22]: (aₙ - ⅈ)⋅(aₙ + ⅈ)

This is also a bug although much less serious.

Reproducer:

import sympy
a_n, m = sympy.symbols('a_n m', real=True, positive=True)
expr = (-a_n**4 *m**2 / (a_n**2 + 1) - 2 * a_n**4 *m / (a_n**2 + 1) - a_n**4 - a_n**4 / (a_n**2 + 1) + 2 * sympy.I * a_n**3 *m**2 / (a_n**2 + 1) + 4 * sympy.I * a_n**3 *m / (a_n**2 + 1) + 2 * sympy.I * a_n**3 + 2 * sympy.I * a_n**3 / (a_n**2 + 1) + 2 * sympy.I * a_n *m**2 / (a_n**2 + 1) + 4 * sympy.I * a_n *m / (a_n**2 + 1) + 2 * sympy.I * a_n + 2 * sympy.I * a_n / (a_n**2 + 1) +m**2 / (a_n**2 + 1) + 2 *m / (a_n**2 + 1) + 1 + 1 / (a_n**2 + 1))
expr.factor()