That's a surprise to me, Oscar. I would not have expected Newton to work with a system involving Max like this. It is interesting, too, that SymPy knows the derivative of Max.
I think you have to watch out for (1) infeasible systems which would cycle and (2) singluar systems. For (1) AI gives as an example, import sympy as sp x, m = sp.symbols('x m', real=True) def step(policy): # policy: 0 -> use m=x, 1 -> use m=-x eqs = [sp.Eq(m, x if policy == 0 else -x), sp.Eq(x, 2*m + 1)] sol = sp.solve(eqs, [x, m], dict=True)[0] next_pol = 0 if sp.Max(sol[x], -sol[x]) == sol[x] else 1 return sol, next_pol policy = 0 for _ in range(4): sol, policy = step(policy) print(f"sol={sol}, next_policy={policy}, m={sol[m]}, Max(x,-x)={sp.Max(sol[x], -sol[x])}") which oscillates between (x,m) = (-1,-1) and (1/3,-1/3) whereas LP would indicate this as being infeasible. As an example of (2) consider `x=max(y,0);y=max(x,0)` which has solution (t,t) with t>=0. LP would give a canonical solution like (0,0). /c On Thursday, August 28, 2025 at 9:26:42 AM UTC-5 Oscar wrote: > On Thu, 28 Aug 2025 at 15:05, Oscar Benjamin <oscar.j....@gmail.com> > wrote: > > > > So Newton iteration has converged exactly after two iterations in this > > case although a third iteration is used to confirm convergence. I > > think what happened is that the initial conditions did not have the > > right branches for all Maxes but after one iteration it has the right > > branches and a single step of Newton solves the linear equations > > exactly. > > > > ... > > > > Once you have an approximate float solution like this you can use it > > to check which branch of each Max holds. Then you can compute the > > exact solution in rational numbers and verify that it holds exactly if > > that is what is needed. > > Actually not even that is needed. Since Newton iteration converges > exactly for this case you can just do Newton with rational numbers: > > In [2]: syms = list(ordered(Tuple(*eqs).atoms(Symbol))) > > In [3]: F = Matrix([e.lhs - e.rhs for e in eqs]) > > In [4]: J = F.jacobian(syms) > > In [5]: x0 = [Rational(1, 2)]*len(syms) > > In [6]: xn = Matrix(x0) > > In [7]: rep = dict(zip(syms, xn.flat())) > > In [8]: xn1 = xn - J.xreplace(rep).solve(F.xreplace(rep)) > > In [9]: print(sum(map(abs, xn - xn1)).evalf(3)); xn = xn1 > 46.0 > > In [10]: rep = dict(zip(syms, xn.flat())) > > In [11]: xn1 = xn - J.xreplace(rep).solve(F.xreplace(rep)) > > In [12]: print(sum(map(abs, xn - xn1)).evalf(3)); xn = xn1 > 0.563 > > In [13]: rep = dict(zip(syms, xn.flat())) > > In [14]: xn1 = xn - J.xreplace(rep).solve(F.xreplace(rep)) > > In [15]: print(sum(map(abs, xn - xn1)).evalf(3)); xn = xn1 > 0 > > In [16]: print([N(e) for e in xn[:10]]) > [0.615451987540876, 0.744935782479303, 0.669554028724062, > 0.507340567903750, 0.507340567903750, 0.779985609984258, > 0.540175431864777, 0.802311871497269, 0.646942367048034, > 0.792756582043413] > > In [17]: print(xn[0]) > > 29111091937059880243204574405054181649424284485394821464265991381502076096979324693736677856978271553874982581817081789333748397161675933583/47300345967485318925827718364372480084013111896344922847317119820923097431308394914046771654831300577107337882031715520231586587481997312000 > > -- > 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+unsubscr...@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/sympy/ba489ce3-cd0c-4281-b0d6-36a671c2a0afn%40googlegroups.com.