Could you post an example of a large system? /c
On Wednesday, August 20, 2025 at 9:45:00 AM UTC-5 [email protected] wrote: > > Hi Oscar, > > Sorry for the late reply, I opened the two issues: > > https://github.com/sympy/sympy/issues/28331 > https://github.com/sympy/sympy/issues/28332 > > Not quite sure if the format and the text is fine, let me know if I need > to modify anything. > > Thanks very much for the detailed explanation on solve vs solveset. I > found it very interesting, and I agree with the points. For my usecase I > definitely need to use solve, because I need the solutions afterwards. > > I'm writing an implementation for my very special and restricted usecase > of systems, where I might need to convert between dicts and sets, mainly > to keep track of the potential infinite solutions: > {x[0]: x[2], x[1]: x[2], x[2]: x[2]} == {x[0]: x[1], x[1]: x[1], x[2]: > x[1]} is False for dicts, but they both represent the same FiniteSet if > the domain for x[1] and x[2] are the same. Is there some code in sympy > which converts between these in certain cases, or I can just go with my > (rather naive) approach with something like: > > def subs_to_fset(subs:list[dict], > variables:Iterator[sympy.Symbol]) -> sympy.FiniteSet: > if any(len(d) != len(variables) for d in subs): > raise SubstitutionSizeNotMatcingNumberOfVariablesError > return sympy.FiniteSet(*[tuple(d[v] for v in variables) for d in > subs]) > > def fset_to_subs(fset:sympy.FiniteSet, > variables:Iterator[sympy.Symbol]) -> tuple[tuple]: > if any(len(p) != len(variables) for p in fset): > raise FiniteSetDimensionNotMatcingNumberOfVariablesError > return tuple(tuple(zip(variables, p)) for p in fset) > > > You also mention that there is no benefit using linsolve and nonlinsolve > vs solve. Do you mean that for the performance, as well, or only for > capability? To me performance may matter, and the awkward conversion > between sets and list[dicts] might be worth it in case linsolve is > significantly faster than solve. But if they essentially have the same > underlying code, then solve is far superior for its output. > > > Thanks for the pointers on how to handle systems w Min/Max. For now I > ended up using an extra domain parameter that restricts the potential > solution onto an interval (in my special usecase that's the situation), > and since all non-MinMax equations are linear, I can use some heuristics > to compute upper and lower bounds for each variable based on what > subsolutions / substitutions I found so far, and intersect that with the > domain of interval of possible solutions. In certain cases (apparently a > lot of the cases for my systems) this provides a solution to a lot of > variables, just like in the case for the minimal example I provided. This > way I can significantly reduce the number of equations having MinMax in > them. > For some big systems there still are many of those MinMax equations, > though (many meaning ~20, but that is already >1mil different cases to > solve for). So for those I'll try your suggested approach of recursively > solve one equation and substitute back, but that seems to be less > efficient for now that simply enumerating all potential 2**20-many > possibilities. I must have some silly mistake in my code, which I'll have > to check. > > In any case, thank you very much for all the help! > > Thanks, > Gábor > > > On Sat, 9 Aug 2025 at 17:31, Gábor Horváth <[email protected]> wrote: > >>> On Sat, 9 Aug 2025 at 13:26, Gábor Horváth <[email protected]> > wrote: > >>>> > >>>> So my questions are: > >>>> 1) Is the expected behaviour for sympy.solve for the Piecewise case > to throw a NaN comparison exception, or is this a sign of some underlying > issue in sympy.solve for Piecewise functions? > >>> > >>> No, that is just a bug. > >> > >> Ok, thanks. Should I report it in some official way somewhere (if yes, > >> where and how), or has this email already taken care of the bugreport? > >> Maybe I should open an issue on https://github.com/sympy/sympy/issues ? > > > > Yes, please open an issue there. > > > > I think that the expected behaviour here is to raise an error but it > > should be an explicit error like "equation type not supported". > > > >>>> 2) Is sympy.solve expected to rewrite Min and Max to Piecewise at > some point in the future? I found some piece of code in > solvers/solvers.py:944-957 which does some sort of rewriting for Abs, but > didn't find any for Min or Max. > >>> > >>> It probably does make sense to rewrite Min and Max as Piecewise if > >>> they contain the variables of interest. > >> > >> Ok, thanks. Same question as above: should I open some issue on > >> https://github.com/sympy/sympy/issues or elsewhere, or this is already > >> taken care of by this email? > > > > Yes, please open an issue. It can be the same issue. > > > >>>> 3) From some reading on the sympy docs page, I figured solveset is > expected to be the future solver. Is there a direct way I can apply > solveset on a system, rather than on one equation? > >>> > >>> This documentation needs to be changed. The solveset function is not > >>> going to replace solve. It was originally intended as a replacement > >>> for solve but was then misdesigned as something different that cannot > >>> replace solve because it does not provide comparable functionality or > >>> interface. > >> > >> Ah, I see. This was the page I found first: > >> https://lidavidm.github.io/sympy/modules/solvers/solveset.html > > > > That is an old version hosted from someone's outdated fork. Can you > > open a separate sympy github issue about that? We can tag the owner of > > the fork and ask them to remove this. It isn't possible to open an > > issue in their repo as it has issues disabled I think: > > https://github.com/lidavidm/sympy > > > >> BTW, are there some old threads somewhere maybe where such discussions > >> can be read? I would be curious what you mean by misdesign, and what > were > >> the discussion and decision points around solve an solveset. > > > > Some will be here on the mailing list which you can search. Others > > will be on GitHub. > > > > What I mean by misdesign here is not that solveset is misdesigned in > > general but rather that if the intention was to replace solve then it > > was misdesigned for that purpose. It is not possible to replace solve > > with solveset because they just don't do the same thing. > > > > The purpose of a function like solve is that it turns an implicit > > representation of the solutions (equations to be solved) into an > > explicit representation which is a list of assignments like x = ..., y > > = .... Its return value is therefore a list of dicts each of which > > could be passed to subs. > > > > There are some limitations in this explicit format which mean that it > > cannot always be fully mathematically correct/complete. For example > > your system of equations has the solution set: > > > > {(a, a) for a in [2, oo)} > > > > The best solve can do to represent that solution set is to return > > > > [{x: y}] > > > > but that loses the information that y >= 2 is a constraint on the > > solutions. It could be possible to extend solve to also return the > > condition like > > > > [({x: y}), y >= 2)] > > > > but that would be an incompatible change in its output format. > > > > With solveset the intention is that the output is always > > mathematically correct/complete but then by design it does not > > necessarily return an explicit representation of the solutions which > > contradicts the basic purpose of solve. There is a tradeoff here where > > you have to choose between having explicit representations but with > > some attached explicit or implicit conditions vs not having an > > explicit representation at all. > > > > The purpose of the solve function is to have the explicit > > representations subject to the fact that they may be incomplete or not > > always valid whereas the purpose of solveset is to be always complete > > and valid and therefore not necessarily give an explicit > > representation of the solutions. These are contradictory goals and so > > in a basic way solveset cannot replace solve. > > > > There are other reasons why solveset cannot replace solve. Another > > reason is that solveset returns output in an unstructured form that is > > not easily usable even when the solutions can be given explicitly. > > There are some places in the sympy codebase that try to use solveset > > and they always have awkward scaffolding code that basically tries to > > turn the output of solveset into something that looks like the output > > of solve. This is because the sets that solveset returns are only > > really nice just to "look at". If you actually want to use the > > solutions of the equation for something then you want an explicit > > representation i.e. you basically want solve rather than solveset. > > > > An even more obvious reason why solveset cannot replace solve is that > > it only handles single univariate equations rather than systems of > > equations. It has not even been designed in a way that it could be > > extended to handle systems of equations in future. > > > >> There is also mention in the docs about FiniteSet handling tuples > >> corresponds to a finite subset of points in the multi-dimensional space. > >> That suggests that solveset was intended to be able to solve > multi-variate > >> equations or even systems. Is that indeed the case, or only some special > >> cases (like linsolve) can really handle multi-variate equations/systems? > > > > The linsolve and nonlinsolve functions do handle systems of equations > > and are useful. They also return a somewhat awkward output format and > > do not really handle infinite solution sets in a coherent way which > > undermines the benefit of not simply returning a list of dicts like > > solve does. I am not sure that there is any particular benefit in > > using either of these functions rather than just using solve. They are > > basically just new implementations of the way that solve handles > > systems and perhaps have cleaner code but otherwise do not > > fundamentally increase capability or usefulness. > > > >>> In general I think that what you should really want in a situation > >>> like this is something like Mathematica's Reduce function rather than > >>> Solve. Unfortunately SymPy has several versions of solve but nothing > >>> like reduce. > >> > >> Hm, all are very good points. I have some very crude code to solve these > >> systems exactly as you suggest, by splitting at the various Min and Max > >> occurrences, solving the underlying linear system (with linsolve), and > >> then check if the obtained solutions indeed solve the original system > with > >> Min and Max. > >> > >> That said, somehow my code is rather slow, especially when there are > many > >> Max occurrences. I feel that sympy.solve is faster when it doesn't fail, > >> so somehow sympy handles Piecewise functions pretty fast. I guess I'll > >> have to do some profiling on where I can still gain on performance. > >> > >> Your comment also made me realize that in my systems there are some > >> underlying assumptions, as well, as inequalities. For the above system I > >> quickly checked and the corresponding assumption would be 1<=Min(x,y) > and > >> Max(x,y)<=2. With that assumption the solution becomes unique. I guess > >> I'll need to handle these assumptions in my code, as well. (sympy.solve > >> dies with the same NaN comparison error even with the extended > >> assumptions) > > > > Okay, so your actual system here is: > > > > x = Max(y, 1) > > y = Max(2, x) > > 1<=Min(x,y) > > Max(x,y)<=2 > > > > If I wanted to make a solver for systems like this then I would make > > something that turns it into separate systems of inequalities that > > don't involve Min and Max. For example x = Max(y, 1) can be separated > > into two disjoint cases represented by pairs of inequalities: > > > > (1) > > x = y > > y > 1 > > > > (2) > > x = 1 > > y <= 1 > > > > You can do the same for each Max and Min. That will give you 8 (2^3) > > systems of linear equations and inequalities. To solve those first > > solve the linear equations and then substitute into the inequalities. > > Then Fourier-Motzkin can solve the remaining system of inequalities. > > > > If you have a large number of Max/Min then the approach I just > > described might be inefficient but it depends how many of the Max/Min > > are actually distinct. In the above case Max(x,y) and Min(x,y) can > > both switch in the same condition x>y. That is why we get 2^3 cases > > rather than 2^4. If your 2^n here is large then a better approach is > > something that works by progressively solving and substituting e.g. > > for case (1) above you substitute x = y into all remaining > > inequalities before splitting any of the other Min/Max at which point > > they simplify significantly. > > > > If SymPy were to have a solver for this kind of system it would be one > > of the inequality solvers but I don't think any of those handles a > > multivariate system as is needed here. Ideally SymPy would have a > > reduce function that would be able to handle exactly this kind of > > system but it does not. Much of the effort that went into solveset > > would have been better spent on a reduce function which would have > > then been a good foundation for building a new version of solve. > > > > -- > > 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 [email protected]. > > To view this discussion visit > https://groups.google.com/d/msgid/sympy/CAHVvXxQ%3D0t4J9hqjhW43wYezd5%2BBwxKN8aeG6oBWmjkJU9hapQ%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/1d1c80c9-8b37-48b6-ac1e-1286d2eb83een%40googlegroups.com.
