Subs and custom expression

82 views
Skip to first unread message

Paul Royik

unread,
Jan 26, 2025, 9:11:00 AM1/26/25
to sympy
from sympy import Expr, log, Abs, sqrt
from sympy.abc import x, u

class CustomExpr(Expr):
pass

expr = log(Abs(sqrt(1+2/x)-1))

print(expr.subs(x, u))
print(expr.subs(x, CustomExpr(u)))

The first one prints, as expected, log(|sqrt(1 + 2/u) - 1|)

But the second prints something different:
log(sqrt(i*sin(atan2(-2*im(CustomExpr(re(u) + i*im(u)))/(re(CustomExpr(re(u) + i*im(u)))**2 + im(CustomExpr(re(u) + i*im(u)))**2), 1 + 2*re(CustomExpr(re(u) + i*im(u)))/(re(CustomExpr(re(u) + i*im(u)))**2 + im(CustomExpr(re(u) + i*im(u)))**2))/2)*conjugate(((1 + 2*re(CustomExpr(re(u) + i*im(u)))/(re(CustomExpr(re(u) + i*im(u)))**2 + im(CustomExpr(re(u) + i*im(u)))**2))**2 + 4*im(CustomExpr(re(u) + i*im(u)))**2/(re(CustomExpr(re(u) + i*im(u)))**2 + im(CustomExpr(re(u) + i*im(u)))**2)**2)**(1/4)) - i*sin(atan2(-2*im(CustomExpr(re(u) + i*im(u)))/(re(CustomExpr(re(u) + i*im(u)))**2 + im(CustomExpr(re(u) + i*im(u)))**2), 1 + 2*re(CustomExpr(re(u) + i*im(u)))/(re(CustomExpr(re(u) + i*im(u)))**2 + im(CustomExpr(re(u) + i*im(u)))**2))/2)*conjugate(((1 + 2*re(CustomExpr(re(u) + i*im(u)))/(re(CustomExpr(re(u) + i*im(u)))**2 + im(CustomExpr(re(u) + i*im(u)))**2))**2 + 4*im(CustomExpr(re(u) + i*im(u)))**2/(re(CustomExpr(re(u) + i*im(u)))**2 + im(CustomExpr(re(u) + i*im(u)))**2)**2)**(1/4))*sqrt(1 + 2*CustomExpr(u)**(-1)) - cos(atan2(-2*im(CustomExpr(re(u) + i*im(u)))/(re(CustomExpr(re(u) + i*im(u)))**2 + im(CustomExpr(re(u) + i*im(u)))**2), 1 + 2*re(CustomExpr(re(u) + i*im(u)))/(re(CustomExpr(re(u) + i*im(u)))**2 + im(CustomExpr(re(u) + i*im(u)))**2))/2)*conjugate(((1 + 2*re(CustomExpr(re(u) + i*im(u)))/(re(CustomExpr(re(u) + i*im(u)))**2 + im(CustomExpr(re(u) + i*im(u)))**2))**2 + 4*im(CustomExpr(re(u) + i*im(u)))**2/(re(CustomExpr(re(u) + i*im(u)))**2 + im(CustomExpr(re(u) + i*im(u)))**2)**2)**(1/4)) + cos(atan2(-2*im(CustomExpr(re(u) + i*im(u)))/(re(CustomExpr(re(u) + i*im(u)))**2 + im(CustomExpr(re(u) + i*im(u)))**2), 1 + 2*re(CustomExpr(re(u) + i*im(u)))/(re(CustomExpr(re(u) + i*im(u)))**2 + im(CustomExpr(re(u) + i*im(u)))**2))/2)*conjugate(((1 + 2*re(CustomExpr(re(u) + i*im(u)))/(re(CustomExpr(re(u) + i*im(u)))**2 + im(CustomExpr(re(u) + i*im(u)))**2))**2 + 4*im(CustomExpr(re(u) + i*im(u)))**2/(re(CustomExpr(re(u) + i*im(u)))**2 + im(CustomExpr(re(u) + i*im(u)))**2)**2)**(1/4))*sqrt(1 + 2*CustomExpr(u)**(-1)) + 1 - sqrt(1 + 2*CustomExpr(u)**(-1))))

Is this a bug or it works as expected?

How to make print(expr.subs(x, CustomExpr(u))) output log(|sqrt(1 + 2/u) - 1|)?

Thank you.

Oscar Benjamin

unread,
Jan 26, 2025, 10:24:47 AM1/26/25
to sy...@googlegroups.com
Hi Paul,

This is a bug. I assume that the result is mathematically correct but
it is not a good transformation of the expression.

Can you open a GitHub issue for this and link back to this mailing
list thread (and send the link to the issue back here as well)?

The problem is with Pow.conjugate

In [1]: class CustomExpr(Expr): pass

In [2]: e = sqrt(1+2*CustomExpr(x))

In [3]: print(e)
sqrt(1 + 2*CustomExpr(x))

In [4]: print(e.conjugate())
-I*((2*re(CustomExpr(re(x) + I*im(x))) + 1)**2 + 4*im(CustomExpr(re(x)
+ I*im(x)))**2)**(1/4)*sin(atan2(2*im(CustomExpr(re(x) + I*im(x))),
2*re(CustomExpr(re(x) + I*im(x))) + 1)/2) + ((2*re(CustomExpr(re(x) +
I*im(x))) + 1)**2 + 4*im(CustomExpr(re(x) +
I*im(x)))**2)**(1/4)*cos(atan2(2*im(CustomExpr(re(x) + I*im(x))),
2*re(CustomExpr(re(x) + I*im(x))) + 1)/2)

This is because Pow.conjugate calls expand_complex which it should
not. Actually though the call to expand_complex is gated on the base
being not a positive number which exposes a bug here:

In [5]: (1 + 2*CustomExpr(x)).is_positive
Out[5]: False

That should return None rather than False so there is a bug in the
assumptions system.

The cause of this is that is_commutative is part of the assumptions
system (which it really shouldn't be) and CustomExpr has
is_commutative = None. A workaround is to define CustomExpr like this:

class CustomExpr(Expr):
is_commutative = True

--
Oscar
> --
> You received this message because you are subscribed to the Google Groups "sympy" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to sympy+un...@googlegroups.com.
> To view this discussion visit https://groups.google.com/d/msgid/sympy/a93b9e4e-a9e5-4233-8417-c68121430c33n%40googlegroups.com.

Paul Royik

unread,
Feb 10, 2025, 3:03:13 PM2/10/25
to sympy
Reply all
Reply to author
Forward
0 new messages