On Tue, Sep 9, 2025 at 1:22 PM Gábor Horváth <[email protected]> wrote:
>
>
> 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?

Right now SymPy doesn't have a clear line which is why it can be slow
sometimes. Ideally the line should be drawn that automatic
simplifications are not done if the require the assumptions system.

>
> 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.

OK, so maybe Max is already handling this.

The real issue here is that the logic for this should be handled
separately from Max. The idea place that should be doing this logic is
ask(). But presently ask doesn't do very much with inequalities, which
is a major issue that we want to improve. Really Max() should just be
calling ask(a > b) instead of just checking if a > b auto-evaluates to
True or False. Then inside of ask(), there should be a handler (or a
theory solver, if it's sophisticated enough), that can handle these
kinds of nonlinear inequalities. Presently we only have a theory
solver for handling systems of linear inequalities. This may require
something like the CAD algorithm to handle in full generality,
although there might be simpler algorithms for handling expressions
with only one variable.

Aaron Meurer

>
> 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.

-- 
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%3D6LBG2gDu6xkwKTfZzhWF-rjffSgE9A4agOX7UknVD36sA%40mail.gmail.com.

Reply via email to