Hi Aaron,

Thank you for the reply, and for the explanation on max behaviour. It makes full sense. I guess the interesting part is how much sympy can (or does) evaluate >, how much simplification it is willing to do on its own, before one needs to specifically ask for it with simplify or doit. I also understand to not wanting to do these automatic evaluations as they can be expensive. That is fine, but then where was the line drawn on when to simplify and when not to?

I'm not sure I understand why multiple arguments poses a problem. Even in the current implementation, the documentation says that Max does evaluation on > as much as possible, and return Max(x_1, x_2, ...) where x_1, x_2, ... are top elements in the poset defined by the partial evaluation of >. The very same thing can be done regardless of how you compute the poset of >, the difference is that in case instead of trying to evaluate a > b, you do simplify(a-b) > 0, or something like that, then your poset of > will possibly contain more comparisons.

So with this approach one can do a better evaluating Max with some additional parameter. Like Max(expr_1, expr_2, .... , simplify=True) would mean that instead of evaluating expr_i > expr_j, it would resort to simplify(expr_i - expr_j) > 0. And default of simplify can be False, that would not alter current behaviour.

Thanks,
Gábor

On Tue, 9 Sep 2025, Aaron Meurer wrote:

The built-in max() is just comparing the objects using <. It works
because in some cases SymPy is able to compute the value of <. Python
lets objects override < but not max().

t = sympy.Symbol('t', nonnegative=True)
t/(t+1) < 1
True

The reason Max() is better is because it can remain symbolic. max()
cannot do that. It has to return one argument or the other. max(a, b)
is basically doing something like

if bool(a >= b):
   return a
else:
   return b

The bool() part in particular means that a >= b cannot be symbolic. It
has to evaluate to either True or False. This is why SymPy raises a
TypeError in the cases where it cannot be determined to be True or
False.

For what it's worth, I don't think it's good that the inequalities do
automatic simplification based on assumptions like this. I would
prefer to remove it, because it's an expensive operation to be done in
an automatic evaluation. It should be done only when calling
ask(t/(t+1) < 1) or something like that.

Similarly, if Max can be improved to compute things better that would
be good, but it would be better to put that code in doit() or
_eval_simplify() rather than happening automatically. The main issue I
see with your suggestion is how it would work when there are more than
just two arguments to Max. This also starts to get into questions
about inequalities and the assumptions in SymPy, which can be a deeper
rabbit hole than you might initially expect.

Aaron Meurer

On Mon, Sep 8, 2025 at 4:17 PM Gábor Horváth <[email protected]> wrote:


Hi Team!

Opening a new topic for this.

I played around with sympy's Min/Max, and then once I accidentally
mistyped to use min/max, ie the built-in function. To my big surprise,
it didn't throw an error, but actually gave the correct answer:

import sympy
t = sympy.Symbol('t', nonnegative=True)
max(t/(t+1), 1)
1

I started playing around with it, and apparently, it can even simplify in
some cases, but not in others:

max(t/(t+1), t**2/(t*(t+1)))
t/(t + 1)
max(t/(t+1), t**2/(t**2+t))
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
   File
"/home/ghorvath/work/python_venv/lib/python3.9/site-packages/sympy/core/relational.py",
line 519, in __bool__
     raise TypeError(
TypeError: cannot determine truth value of Relational: t**2/(t**2 + t) >
t/(t + 1)

Now, in the latter, sympy.Max actually works, even though it chooses the
'less simple' representation in my view:

sympy.Max(t/(t+1), t**2/(t**2+t))
t**2/(t**2 + t)

And then there is the case, where both of them fails:

max(t/(t+1), (t+1)/(t+2))
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
   File
"/home/ghorvath/work/python_venv/lib/python3.9/site-packages/sympy/core/relational.py",
line 519, in __bool__
     raise TypeError(
TypeError: cannot determine truth value of Relational: (t + 1)/(t + 2) >
t/(t + 1)
sympy.Max(t/(t+1), (t+1)/(t+2))
Max(t/(t + 1), (t + 1)/(t + 2))

Well, technically sympy.Max doesn't fail, it just can't figure out without
help which is maximal out of the two. That said, with a bit of help, both
of them are able to find the correct answer:

max(sympy.simplify(t/(t+1) - (t+1)/(t+2)), 0)
0
sympy.Max(sympy.simplify(t/(t+1) - (t+1)/(t+2)), 0)
0

So I have a few questions here:

1a) How come the built-in max knows about sympy's expressions and symbol
assumptions? Is there some hidden place in sympy's code which overrides
the built-in behaviour of max? Or the built-in max is actually prepared
for sympy in some way? Or something else?
Hm, thinking about it from the error messages above, is the case that > is
overridden, or actually defined for sympy expressions, and the built-in
max is simply using that?

1b) If max is really using sympy's >, how come Max is superior over it?
(max fails on max(t/(t+1), t**2/(t**2+t)), but Max does not, so it must
be doing some extra over simply comparing by >)

2) Would it not make sense for sympy.Max (and correspondingly, Min),
that whenever it runs to a relational error on a>b, it would try to do
simplify(a-b)>0? Better yet, shouldn't it be somewhere in the relational
code? That is, if a>b throws a TypeError, then it tries to do
simplify(a-b)>0 before caving? If you think it makes sense, I'm happy to
open an issue on this. Especially if Max is already doing something extra
over just simply comparing via >, it might make sense to fall back on
simplify before throwing the TypeError?

Thanks,
Gábor

--
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 [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/sympy/90a86786-fda8-15ad-dacb-3b5c3545a038%40gmail.com.

--
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 [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/sympy/CAKgW%3D6%2BbbaUJH7U8Dj5kge4ELeTpSmQnV5ys-rHLvXoCqeMBrQ%40mail.gmail.com.


--
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 [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/sympy/1948c8be-e4ff-f6f3-36d1-48d5123015eb%40SamsungLaptop.WORKGROUP.

Reply via email to