Regarding “lower_bound_propagation”: it was offered as a *pre-pruner*. Per 
AI, "It’s most useful when many nodes are Max(const, v) or when you want to 
freeze obvious branches before the loop. In your example it wasn’t 
necessary. Keep it in your toolbox for larger, noisier systems." It also 
observed that "“initial_policy” only affects *how many* flips you need and 
[prefers] the “constant branch first” because it freezes a lot of 
Max(const, ·) immediately, but [0,0,…] is fine."

/c
On Wednesday, August 27, 2025 at 6:18:36 PM UTC-5 Chris Smith wrote:

> I didn't review the AI solution very closely since we had a working LP 
> solution. (And, BTW, I have found the quality of AI depends a lot on the 
> details of the request and subsequent prompts and also how close a problem 
> is to an already known type of problem.)
>
> As far as " Is it that much faster to calculate with floats instead of 
> with rationals?" It depends what you are doing. If computation with 
> Rationals needs to keep making the denominator larger then floats can be 
> much faster. When trying to compare expressions internally in SymPy we will 
> use a low precision Float, if possible, to make an unambiguous decision 
> about expression equality rather than maintain the exact Rationals.
>
> One thing I did notice, however, was the use of nsimplify which made me a 
> little nervous.
>
> I asked the AI if the method is just simplex in disguise and it said,
>
> ```AI
>
> Short answer: *no, it isn’t simplex*—it’s a *policy-iteration / 
> active-set* method for a max-affine system.
>
> What you ran there does this each iteration:
>
>    1. 
>    
>    pick an “active” branch for every Max (a policy),
>    2. 
>    
>    solve the resulting *linear equalities* (with linsolve / SCC),
>    3. 
>    
>    check which Max constraints are violated and flip those branches,
>    4. 
>    
>    repeat until no flips are needed.
>    
> That’s essentially Howard-style policy iteration (very close to an 
> *active-set 
> method*). It doesn’t keep a simplex tableau or walk polytope edges with 
> entering/leaving variables; it repeatedly solves the linear system implied 
> by a chosen active set and updates that set.
>
> How it relates to simplex/LP:
>
>    - 
>    
>    If you add the LP *objective* min Σ m_i with epigraph constraints m_i 
>    ≥ each arg, then your problem *becomes an LP*. Your lpmin run solved 
>    exactly this (SymPy uses a simplex-type solver under the hood).
>    - 
>    
>    The policy loop can be viewed as an *active-set solver for that LP’s 
>    KKT conditions*: a policy = a guess of which inequalities are tight; 
>    the linear solve enforces equalities; the “flip on violation” is an 
>    active-set update. In nondegenerate cases it often lands at the same 
>    solution as the LP—but it’s not literally simplex, and without the 
>    objective you’re just finding a consistent fixed point (when unique, they 
>    coincide).
>    
> So: *policy iteration ≠ simplex*, but it’s *simplex-adjacent* (an 
> active-set scheme). Your final lpmin approach is the canonical LP 
> formulation; the earlier method is a good objective-free solver/certifier 
> that often converges fast on monotone, max-affine systems.
>
> ```
> /c
>
> On Wednesday, August 27, 2025 at 3:32:26 PM UTC-5 hungabo...@gmail.com 
> wrote:
>
>>
>> Hi Chris, 
>>
>> Hmmmmmm, very interesting. First I thought, "No way that an AI can write 
>> a 
>> code that's faster than anyone else's here, including sympy's authors." I 
>> even checked the solution with checksol, and the code below finds the 
>> solution significantly faster than checksol verifies it. :-D 
>> (BTW, that is because I guess checksol uses subs, and here xreplace 
>> suffices for verification.) 
>>
>> It was nagging me all day (especially because I'm quite a non-believer in 
>> AI), so I spent the significant part of the evening to understand in 
>> detail what the code does. Surprisingly, half of the code is not even 
>> used. The lower_bound_propagation is not used at all, which I think is 
>> similar to my original idea, though I used both lower and upper bounds. 
>> Further, initial_policy doesn't really matter I think, it could be set to 
>> [0, 0, ... , 0] by default. This policy is supposed to keep track of 
>> which 
>> argument is selected as currently being "maximal". 
>>
>> The key part is of course the policy_iteration. If I understand 
>> correctly, 
>> what this does is that evaluates all m_i as per the current policy, then 
>> solves the remaining linear equation, then evaluates all Max()_i 
>> expressions, and checks if m_i == Max()_i or m_i < Max()_i. In the latter 
>> case modifies the policy according to which argument Max()_i takes, and 
>> iterates with the new policy. 
>> (And btw, it does this with always evaluating the symbolic rationals to 
>> float, which I'm not quite sure why on Earth to do, but anyway. :-) 
>> Is it that much faster to calculate with floats instead of with 
>> rationals?) 
>>
>> This feels like a simplex algorithm to me (so AI may have stolen your 
>> linear programming idea, as well :-) ), and I'm very much intrigued by 
>> the 
>> concept. Even though I'm not fully convinced that this always finds the 
>> solution. I'm not even fully sure that there is always only one solution. 
>> But I think in the case where each variable is a nonnegative linear 
>> combination of the rest of the variables, then it may work, because it is 
>> (likely) increasing the Max()_i expressions. I'm not fully convinced, 
>> because if, say, m_i = Max(r_i, v_j), and v_j > r_i for the solution 
>> where 
>> m_i = r_i is chosen doesn't necessarily mean that v_j > r_i will hold 
>> when 
>> m_i = v_j is chosen, and we solve the system all over again. Or at least 
>> I 
>> don't see a clear argument for it. 
>>
>> In any case, this is very useful and interesting insight (both into my 
>> problem and into AI's "thinking", as well), thank you very much for this! 
>>
>> Thanks, 
>> Gábor 
>>
>> On Tue, 26 Aug 2025, Chris Smith wrote: 
>>
>> > Here is an AI translation based on Oscar's approach that only takes a 
>> few 
>> > seconds. Does it do what you need? 
>> > 
>> > ```python 
>> > from __future__ import annotations 
>> > from typing import Dict, List, Tuple as _T, Iterable 
>> > from dataclasses import dataclass 
>> > from sympy import * 
>> > eqs=S('''[Eq(v0, v39), Eq(v1, v21), Eq(v2, v160), Eq(v3, 125*v136/1296 
>> + 
>> > 143*v157/648 + 13*v158/162 + 35*v196/324 + 125*v230/1296 + 7*v67/162 + 
>> > 5*v96/81 + 155/648), Eq(v4, v46), Eq(v5, v21), Eq(v6, 7*v103/162 + 
>> > 13*v13/162 + 143*v138/648 + 5*v165/81 + 125*v188/1296 + 35*v56/324 + 
>> > 145/432), Eq(v7, v21), Eq(v8, 125*v136/1296 + 143*v157/648 + 
>> 13*v158/162 + 
>> > 125*v238/1296 + 35*v64/324 + 7*v67/162 + 13*v70/162 + 5*v96/81 + 
>> 103/648), 
>> > Eq(v9, 125*v107/1296 + 5*v114/81 + 35*v169/324 + 125*v176/1296 + 
>> > 143*v234/648 + 13*v77/162 + 155/648), Eq(v10, v152), Eq(v11, 5*v150/81 
>> + 
>> > 35*v182/324 + 125*v23/1296 + 143*v48/648 + 13*v69/162 + 145/432), 
>> Eq(v12, 
>> > Max(11973429987871294032791/15614510052024077690880, v44)), Eq(v13, 
>> v9), 
>> > Eq(v14, 143*v131/648 + 35*v164/324 + 125*v174/1296 + 13*v179/162 + 
>> > 125*v224/1296 + 155/648), Eq(v15, v97), 
>> Eq(v16,Max(22153944431869014019426428453312493667994869174799325454923686985538985
>>  
>>
>> > 
>> 161903624297370947248977440887249367238742791250601673384171676607719/3245 
>> > 
>> 46351760533679939988778738739509904590113414703722845982839194222550664898 
>> > 69933707729610512961907754883836467874521812063002516520960000, v232)), 
>> > Eq(v17, v87), Eq(v18, v236), Eq(v19, 
>> > Max(8153526396622056593187661712693/9765476659969457979924424224000, 
>> v153)), 
>> > Eq(v20, v97), Eq(v21, 35*v104/324 + 7*v115/162 + 125*v163/1296 + 
>> > 125*v190/1296 + 5*v65/81 + 13*v66/162 + 143*v89/648 + 155/648), Eq(v22, 
>> > 
>> Max(7275710867969158635412531309174807/8437371834213611694654702529536000, 
>> > v180)), Eq(v23, v9), 
>> Eq(v24,Max(26145550756326151280641299371487391967659769861071210409727353743624716
>>  
>>
>> > 
>> 63685097832269563604123454373834497709933777759023191743438472863355594852 
>> > 
>> 611/3252479130413442679612535606086476366411081822411880393493248150349958 
>> > 
>> 82111165117291603151138819232376598031033817829448816783208458983414169600 
>> > 0000, v229)), Eq(v25, 5*v168/81 + 7*v202/162 + 13*v36/162 + 35*v43/324 
>> + 
>> > 35*v68/1296 + 143*v7/648 + 125*v78/1296 + 145/432), Eq(v26, 
>> > 
>> Max(3854097996225308316203175283663385/5973659258623237079815529390911488, 
>> > v231)), Eq(v27, v35), Eq(v28, 
>> > 
>> Max(7082014151066409483273977986654807/8437371834213611694654702529536000, 
>> > v218)), Eq(v29, v3), Eq(v30, v156), Eq(v31, v32), Eq(v32, 5*v100/81 + 
>> > 143*v162/648 + 35*v186/324 + 125*v40/1296 + 125*v49/1296 + 13*v52/162 + 
>> > 13*v59/162 + 103/648), Eq(v33, v9), Eq(v34, v145), Eq(v35, 
>> 125*v126/1296 + 
>> > 13*v173/162 + 125*v181/1296 + 13*v206/162 + 35*v211/324 + 143*v98/648 + 
>> > 103/648), Eq(v36, v97), Eq(v37, v39), Eq(v38, 
>> > Max(435458653906733290600307/519997654931407615526400, v106)), Eq(v39, 
>> > 125*v159/1296 + 13*v19/162 + 125*v74/1296 + 35*v82/324 + 143*v91/648 + 
>> > 13*v94/162 + 103/648), 
>> Eq(v40,Max(226542105817153888727102225905133917442673378023193639810034013/2823616
>>  
>>
>> > 52451162721818510415100150396932752441247124690042880000, v25)), 
>> Eq(v41, 
>> > v9), 
>> Eq(v42,Max(2394733571844680723947486046892229995824719990932886027/329363568294983
>>  
>>
>> > 1743806860531669548694292913046945792000, v213)), Eq(v43, 
>> > 
>> Max(7275710867969158635412531309174807/8437371834213611694654702529536000, 
>> > v180)), Eq(v44, 5*v150/81 + 125*v23/1296 + 143*v48/648 + 13*v69/162 + 
>> > 575/1296), Eq(v45, v147), Eq(v46, 143*v134/648 + 125*v204/1296 + 
>> 35*v63/324 
>> > + 125*v75/1296 + 13*v86/162 + 103/648), 
>> Eq(v47,Max(17774053714151175033989655690633134825729039/24642541563136281050949853
>>  
>>
>> > 769599826662195200, v184)), Eq(v48, v97), Eq(v49, v39), Eq(v50, v46), 
>> > Eq(v51, v147), Eq(v52, v35), Eq(v53, v151), 
>> Eq(v54,Max(279093664883899921882792279729481055727296597875364409018874143/3300755
>>  
>>
>> > 42795919385459138332466610996414444404698606408499200000, v201)), 
>> Eq(v55,Max(279093664883899921882792279729481055727296597875364409018874143/3300755
>>  
>>
>> > 42795919385459138332466610996414444404698606408499200000, v201)), 
>> Eq(v56, 
>> > Max(17162041845107476095157951754621/19530953319938915959848848448000, 
>> > v84)), 
>> Eq(v57,Max(134845448633481464957323497536579332465563743749277/1722973259546888336
>>  
>>
>> > 37102978220838496248844582912000, v6)), Eq(v58, v147), Eq(v59, 
>> > 
>> Max(7275710867969158635412531309174807/8437371834213611694654702529536000, 
>> > v180)), Eq(v60, v183), Eq(v61, v229), Eq(v62, 35*v104/324 + 
>> 143*v118/648 + 
>> > 7*v123/162 + 13*v125/162 + 35*v144/1296 + 125*v163/1296 + 125*v18/1296 
>> + 
>> > 5*v93/81 + 155/648), 
>> Eq(v63,Max(17563696924148543381738879070423985529698879922256948084952521486888948
>>  
>>
>> > 
>> 2808102263013043142576463500794632154622270708701/294314475806011059781427 
>> > 
>> 63526764759261509053983555899757396653584187674268689250025297804423224907 
>> > 9792621628328850227200, v160)), 
>> Eq(v64,Max(50614235297508950192247132102295309194886265945114262526659419000890748
>>  
>>
>> > 
>> 60550587856332801957246398173667897955595061549032234894743025570361086403 
>> > 
>> /6440552733491965702203040804131636369130855093884911670283659703663284794 
>> > 
>> 280497372110953487897410542110852099679560979184490756603148186419200000, 
>> > v183)), Eq(v65, v156), Eq(v66, v152), Eq(v67, v46), Eq(v68, v147), 
>> Eq(v69, 
>> > v87), Eq(v70, 
>> > Max(17162041845107476095157951754621/19530953319938915959848848448000, 
>> > v84)), Eq(v71, 125*v141/1296 + 143*v178/648 + 13*v45/162 + 575/1296), 
>> > Eq(v72,Max(253802678761938692393280149165758641400287017359935136027687/
>> 3268074681 <(326)%20807-4681> 
>> > 14771668771424091551099996449944955147135057920000, v111)), Eq(v73, 
>> v236), 
>> > Eq(v74, v156), Eq(v75, 
>> > 
>> Max(3854097996225308316203175283663385/5973659258623237079815529390911488, 
>> > v231)), Eq(v76, 
>> > 
>> Max(7275710867969158635412531309174807/8437371834213611694654702529536000, 
>> > v180)), Eq(v77, v35), Eq(v78, v183), Eq(v79, 
>> > 
>> Max(8850883678872758268947173651427717/9863131426569152559723668466240000, 
>> > v189)), Eq(v80, 
>> > Max(8153526396622056593187661712693/9765476659969457979924424224000, 
>> v153)), 
>> > Eq(v81, v97), 
>> Eq(v82,Max(23585840544971487927101484002610534998978812145164931920689248019165344
>>  
>>
>> > 
>> 08118507179932080824564044765382257088703238080044388691345123909081442699 
>> > 
>> /3220276366745982851101520402065818184565427546942455835141829851831642397 
>> > 
>> 140248686055476743948705271055426049839780489592245378301574093209600000, 
>> > v21)), 
>> Eq(v83,Max(3636449405556959850095431375189982793388139699327/517375088626358847920
>>  
>>
>> > 9023698635022807381206630400, v11)), Eq(v84, 7*v112/162 + 13*v139/162 + 
>> > 5*v155/81 + 35*v198/1296 + 125*v5/1296 + 143*v60/648 + 575/1296), 
>> Eq(v85, 
>> > Max(11973429987871294032791/15614510052024077690880, v44)), Eq(v86, 
>> > Max(16191931/22535817, v71)), Eq(v87, 125*v12/1296 + 35*v214/324 + 
>> > 143*v50/648 + 155/648), Eq(v88, 7*v112/162 + 13*v139/162 + 5*v155/81 + 
>> > 35*v198/1296 + 35*v205/324 + 125*v5/1296 + 143*v60/648 + 145/432), 
>> Eq(v89, 
>> > v236), Eq(v90, v8), Eq(v91, v152), Eq(v92, 
>> > 
>> Max(8850883678872758268947173651427717/9863131426569152559723668466240000, 
>> > v189)), Eq(v93, v152), Eq(v94, v46), Eq(v95, 
>> > 
>> Max(8850883678872758268947173651427717/9863131426569152559723668466240000, 
>> > v189)), Eq(v96, v122), Eq(v97, 35*v108/324 + 125*v175/1296 + 
>> 125*v74/1296 + 
>> > 143*v91/648 + 13*v94/162 + 155/648), Eq(v98, v35), Eq(v99, v152), 
>> Eq(v100, 
>> > v46), Eq(v101, v46), 
>> Eq(v102,Max(220238025723283192366108519240454879220580984891661788157350013/2823616
>>  
>>
>> > 52451162721818510415100150396932752441247124690042880000, v154)), 
>> Eq(v103, 
>> > v147), 
>> Eq(v104,Max(226542105817153888727102225905133917442673378023193639810034013/2823616
>>  
>>
>> > 52451162721818510415100150396932752441247124690042880000, v25)), 
>> Eq(v105, 
>> > v14), Eq(v106, 7*v103/162 + 13*v13/162 + 143*v138/648 + 5*v165/81 + 
>> > 125*v188/1296 + 575/1296), Eq(v107, 
>> > Max(435458653906733290600307/519997654931407615526400, v106)), 
>> Eq(v108,Max(253802678761938692393280149165758641400287017359935136027687/
>> 3268074681 <(326)%20807-4681> 
>> > 14771668771424091551099996449944955147135057920000, v111)), Eq(v109, 
>> v87), 
>> > Eq(v110, v35), Eq(v111, 5*v109/81 + 143*v194/648 + 125*v20/1296 + 
>> 13*v41/162 
>> > + 35*v43/324 + 7*v58/162 + 145/432), Eq(v112, v160), 
>> Eq(v113,Max(12583422138536213670326411376863680918580376272095805723302205041429835
>>  
>>
>> > 
>> 07324061383727708018843022293004047445501873262810166895090483369/16977733 
>> > 
>> 40450584222326787919746492518856403606479931590531402171972287877510455635 
>> > 787179828965942765628524996676494796077216101826560000, v3)), Eq(v114, 
>> > v46), Eq(v115, v46), Eq(v116, 
>> > Max(8153526396622056593187661712693/9765476659969457979924424224000, 
>> v153)), 
>> > Eq(v117, v152), Eq(v118, v151), Eq(v119, v46), Eq(v120, 125*v105/1296 + 
>> > 143*v133/648 + 5*v187/81 + 13*v209/162 + 575/1296), Eq(v121, 
>> 13*v191/162 + 
>> > 125*v33/1296 + 143*v34/648 + 5*v51/81 + 575/1296), Eq(v122, 
>> 143*v110/648 + 
>> > 35*v142/324 + 125*v195/1296 + 125*v227/1296 + 13*v80/162 + 103/648), 
>> > Eq(v123, v156), 
>> Eq(v124,Max(2394733571844680723947486046892229995824719990932886027/329363568294983
>>  
>>
>> > 1743806860531669548694292913046945792000, v213)), Eq(v125, v39), 
>> Eq(v126, 
>> > v223), Eq(v127, v151), Eq(v128, v46), Eq(v129, Max(16191931/22535817, 
>> v71)), 
>> > Eq(v130, v35), Eq(v131, v152), Eq(v132, v151), Eq(v133, v9), Eq(v134, 
>> v46), 
>> > Eq(v135, Max(11269204546127/14346413781285, v120)), Eq(v136, v32), 
>> Eq(v137, 
>> > 
>> Max(7275710867969158635412531309174807/8437371834213611694654702529536000, 
>> > v180)), Eq(v138, v3), Eq(v139, v3), Eq(v140, v207), Eq(v141, v221), 
>> Eq(v142,Max(23585840544971487927101484002610534998978812145164931920689248019165344
>>  
>>
>> > 
>> 08118507179932080824564044765382257088703238080044388691345123909081442699 
>> > 
>> /3220276366745982851101520402065818184565427546942455835141829851831642397 
>> > 
>> 140248686055476743948705271055426049839780489592245378301574093209600000, 
>> > v21)), 
>> Eq(v143,Max(226542105817153888727102225905133917442673378023193639810034013/2823616
>>  
>>
>> > 52451162721818510415100150396932752441247124690042880000, v25)), 
>> Eq(v144, 
>> > v46), Eq(v145, 5*v100/81 + 125*v137/1296 + 35*v143/324 + 143*v162/648 + 
>> > 125*v49/1296 + 13*v52/162 + 155/648), Eq(v146, 125*v102/1296 + 
>> 143*v131/648 
>> > + 125*v174/1296 + 13*v179/162 + 35*v192/324 + 13*v212/162 + 103/648), 
>> > Eq(v147, 125*v129/1296 + 143*v134/648 + 125*v204/1296 + 35*v26/324 + 
>> > 155/648), Eq(v148, v156), 
>> Eq(v149,Max(279093664883899921882792279729481055727296597875364409018874143/3300755
>>  
>>
>> > 42795919385459138332466610996414444404698606408499200000, v201)), 
>> Eq(v150, 
>> > v147), Eq(v151, 7*v115/162 + 125*v167/1296 + 35*v171/324 + 
>> 125*v190/1296 + 
>> > 5*v65/81 + 13*v66/162 + 13*v76/162 + 143*v89/648 + 103/648), Eq(v152, 
>> > 35*v113/324 + 5*v114/81 + 125*v176/1296 + 13*v200/162 + 143*v234/648 + 
>> > 125*v57/1296 + 13*v77/162 + 103/648), Eq(v153, 5*v109/81 + 143*v194/648 
>> + 
>> > 125*v20/1296 + 13*v41/162 + 7*v58/162 + 575/1296), Eq(v154, 143*v1/648 
>> + 
>> > 7*v161/162 + 5*v2/81 + 35*v22/324 + 125*v29/1296 + 13*v81/162 + 
>> 145/432), 
>> > Eq(v155, v97), Eq(v156, 35*v203/324 + 143*v50/648 + 125*v83/1296 + 
>> > 13*v85/162 + 103/648), Eq(v157, v8), Eq(v158, v152), 
>> Eq(v159,Max(253802678761938692393280149165758641400287017359935136027687/3268074681
>>  
>>
>> > 14771668771424091551099996449944955147135057920000, v111)), Eq(v160, 
>> > 125*v126/1296 + 125*v135/1296 + 13*v206/162 + 35*v47/324 + 143*v98/648 
>> + 
>> > 155/648), Eq(v161, v147), Eq(v162, v8), Eq(v163, 
>> > 
>> Max(7275710867969158635412531309174807/8437371834213611694654702529536000, 
>> > v180)), 
>> Eq(v164,Max(220238025723283192366108519240454879220580984891661788157350013/2823616
>>  
>>
>> > 52451162721818510415100150396932752441247124690042880000, v154)), 
>> Eq(v165, 
>> > v193), Eq(v166, v39), 
>> Eq(v167,Max(226542105817153888727102225905133917442673378023193639810034013/2823616
>>  
>>
>> > 52451162721818510415100150396932752441247124690042880000, v25)), 
>> Eq(v168, 
>> > v9), 
>> Eq(v169,Max(134845448633481464957323497536579332465563743749277/1722973259546888336
>>  
>>
>> > 37102978220838496248844582912000, v6)), Eq(v170, v8), 
>> Eq(v171,Max(21052007697378165051723826206019870332035542881830001467977218809182362
>>  
>>
>> > 
>> 99783478163617023469627750000504871656527720958235556227417449073238133508 
>> > 
>> 201/2782318780868529183351713627384866911464529400558281841562540991982539 
>> > 
>> 03112917486475193190677168135419188810706157034300770000685256001653309440 
>> > 0000, v62)), Eq(v172, v9), Eq(v173, Max(11269204546127/14346413781285, 
>> > v120)), Eq(v174, v122), Eq(v175, 
>> > Max(8153526396622056593187661712693/9765476659969457979924424224000, 
>> v153)), 
>> > Eq(v176, v146), 
>> Eq(v177,Max(279093664883899921882792279729481055727296597875364409018874143/3300755
>>  
>>
>> > 42795919385459138332466610996414444404698606408499200000, v201)), 
>> Eq(v178, 
>> > v160), Eq(v179, v46), Eq(v180, 5*v168/81 + 7*v202/162 + 13*v36/162 + 
>> > 35*v68/1296 + 143*v7/648 + 125*v78/1296 + 575/1296), 
>> Eq(v181,Max(17774053714151175033989655690633134825729039/24642541563136281050949853
>>  
>>
>> > 769599826662195200, v184)), Eq(v182, 
>> > Max(8153526396622056593187661712693/9765476659969457979924424224000, 
>> v153)), 
>> > Eq(v183, 13*v170/162 + 35*v177/324 + 125*v215/1296 + 7*v217/162 + 
>> 5*v237/81 
>> > + 35*v4/1296 + 125*v53/1296 + 143*v73/648 + 155/648), Eq(v184, 
>> 125*v105/1296 
>> > + 143*v133/648 + 5*v187/81 + 13*v209/162 + 35*v38/324 + 145/432), 
>> Eq(v185, 
>> > Max(7839007286424827564645117347/9940275171668787978402662400, v121)), 
>> > 
>> Eq(v186,Max(21052007697378165051723826206019870332035542881830001467977218809182362
>>  
>>
>> > 
>> 99783478163617023469627750000504871656527720958235556227417449073238133508 
>> > 
>> 201/2782318780868529183351713627384866911464529400558281841562540991982539 
>> > 
>> 03112917486475193190677168135419188810706157034300770000685256001653309440 
>> > 0000, v62)), Eq(v187, v147), Eq(v188, v145), Eq(v189, 5*v15/81 + 
>> > 35*v17/1296 + 7*v172/162 + 5*v220/324 + 13*v225/162 + 125*v235/1296 + 
>> > 143*v61/648 + 575/1296), Eq(v190, v39), Eq(v191, v193), 
>> Eq(v192,Max(20450781603653344993969493488748925619296556039860031043667400541934685
>>  
>>
>> > 
>> 46098285839528846751540950480006451342487403164371269925361888435218161080 
>> > 
>> 201/2782318780868529183351713627384866911464529400558281841562540991982539 
>> > 
>> 03112917486475193190677168135419188810706157034300770000685256001653309440 
>> > 0000, v216)), Eq(v193, 143*v110/648 + 125*v116/1296 + 125*v227/1296 + 
>> > 35*v72/324 + 155/648), Eq(v194, v183), 
>> Eq(v195,Max(253802678761938692393280149165758641400287017359935136027687/3268074681
>>  
>>
>> > 14771668771424091551099996449944955147135057920000, v111)), 
>> Eq(v196,Max(540603656426932533425269180020889323149790647994708176949439/6536149362
>>  
>>
>> > 29543337542848183102199992899889910294270115840000, v88)), Eq(v197, 
>> > Max(7839007286424827564645117347/9940275171668787978402662400, v121)), 
>> > Eq(v198, v147), Eq(v199, 
>> > 
>> Max(8850883678872758268947173651427717/9863131426569152559723668466240000, 
>> > v189)), Eq(v200, Max(435458653906733290600307/519997654931407615526400, 
>> > v106)), Eq(v201, 5*v15/81 + 35*v17/1296 + 7*v172/162 + 5*v220/324 + 
>> > 13*v225/162 + 125*v235/1296 + 143*v61/648 + 35*v79/324 + 145/432), 
>> Eq(v202, 
>> > v87), 
>> Eq(v203,Max(33555258956212545159265306236629059868117420803200021272171253837071413
>>  
>>
>> > 093415619201625551702480684238035240126327735818814535013747339/
>> 5098080470 <(509)%20808-0470> 
>> > 
>> 00155009684166443459821254788432565585730119691147319286249009456254191118 
>> > 63289811598969388796638160975604215819955863552000, v208)), Eq(v204, 
>> v46), 
>> > Eq(v205, 
>> > 
>> Max(8850883678872758268947173651427717/9863131426569152559723668466240000, 
>> > v189)), Eq(v206, v46), Eq(v207, 7*v10/162 + 5*v128/324 + 125*v132/1296 
>> + 
>> > 143*v140/648 + 35*v148/1296 + 35*v226/324 + 13*v228/162 + 5*v37/81 + 
>> > 125*v54/1296 + 13*v92/162 + 103/648), Eq(v208, 143*v0/648 + 5*v101/81 + 
>> > 35*v108/324 + 125*v117/1296 + 125*v175/1296 + 13*v30/162 + 155/648), 
>> > Eq(v209, v160), Eq(v210, v122), 
>> Eq(v211,Max(16430325978942857467643435742418968622860616830940400122449521303235159
>>  
>>
>> > 
>> 5510640234280468301450122934132127132665995958212872858503/242821238664149 
>> > 
>> 42924295383871543079122296170819317278220314515664830485513653320336011702 
>> > 5851618319372030931646164857757106176000, v9)), Eq(v212, 
>> > 
>> Max(7082014151066409483273977986654807/8437371834213611694654702529536000, 
>> > v218)), Eq(v213, 13*v191/162 + 35*v28/324 + 125*v33/1296 + 143*v34/648 
>> + 
>> > 5*v51/81 + 145/432), 
>> Eq(v214,Max(3636449405556959850095431375189982793388139699327/517375088626358847920
>>  
>>
>> > 9023698635022807381206630400, v11)), Eq(v215, 
>> > 
>> Max(8850883678872758268947173651427717/9863131426569152559723668466240000, 
>> > v189)), Eq(v216, 143*v127/648 + 5*v130/81 + 125*v137/1296 + 35*v143/324 
>> + 
>> > 13*v166/162 + 7*v219/162 + 125*v90/1296 + 155/648), Eq(v217, v35), 
>> Eq(v218, 
>> > 143*v1/648 + 7*v161/162 + 5*v2/81 + 125*v29/1296 + 13*v81/162 + 
>> 575/1296), 
>> > Eq(v219, v46), Eq(v220, v147), Eq(v221, 35*v124/324 + 125*v197/1296 + 
>> > 125*v233/1296 + 143*v27/648 + 155/648), Eq(v222, 
>> > Max(11269204546127/14346413781285, v120)), Eq(v223, 35*v16/324 + 
>> 13*v185/162 
>> > + 125*v233/1296 + 143*v27/648 + 125*v42/1296 + 103/648), Eq(v224, 
>> > 
>> Max(7082014151066409483273977986654807/8437371834213611694654702529536000, 
>> > v218)), Eq(v225, v183), 
>> Eq(v226,Max(26145550756326151280641299371487391967659769861071210409727353743624716
>>  
>>
>> > 
>> 63685097832269563604123454373834497709933777759023191743438472863355594852 
>> > 
>> 611/3252479130413442679612535606086476366411081822411880393493248150349958 
>> > 
>> 82111165117291603151138819232376598031033817829448816783208458983414169600 
>> > 0000, v229)), Eq(v227, v46), Eq(v228, v236), Eq(v229, 7*v10/162 + 
>> > 5*v128/324 + 125*v132/1296 + 143*v140/648 + 35*v148/1296 + 35*v149/324 
>> + 
>> > 125*v199/1296 + 13*v228/162 + 5*v37/81 + 155/648), Eq(v230, 
>> > Max(17162041845107476095157951754621/19530953319938915959848848448000, 
>> > v84)), Eq(v231, 125*v141/1296 + 143*v178/648 + 35*v222/324 + 13*v45/162 
>> + 
>> > 145/432), Eq(v232, 5*v119/81 + 35*v164/324 + 13*v210/162 + 
>> 125*v224/1296 + 
>> > 143*v31/648 + 125*v99/1296 + 155/648), Eq(v233, v46), Eq(v234, v152), 
>> > Eq(v235, v21), Eq(v236, 13*v170/162 + 7*v217/162 + 5*v237/81 + 
>> 35*v24/324 + 
>> > 35*v4/1296 + 125*v53/1296 + 125*v55/1296 + 143*v73/648 + 13*v95/162 + 
>> > 103/648), Eq(v237, v39), 
>> Eq(v238,Max(540603656426932533425269180020889323149790647994708176949439/6536149362
>>  
>>
>> > 29543337542848183102199992899889910294270115840000, v88))]''') 
>> > 
>> > import sympy as sp 
>> > from sympy import Eq, Expr, Max, Tuple, symbols, linsolve, Matrix, S, 
>> Symbol 
>> > 
>> > @dataclass(frozen=True) 
>> > class MaxSpec: 
>> >     m: Symbol                 # the new epigraph variable (m_i) 
>> >     args: _T[Expr, ...]    # the Max arguments (already xreplaced in 
>> eqs2) 
>> >     raw: Max                  # original Max expression (before 
>> replace) 
>> > 
>> > def build_max_epigraph(eqs: Iterable[Eq]): 
>> >     eqs = list(eqs) 
>> >     maxes = Tuple(*eqs).atoms(Max) 
>> >     syms_max = symbols(f'm0:{len(maxes)}', real=True) 
>> >     rep_m = dict(zip(maxes, syms_max)) 
>> >     eqs2 = [e.xreplace(rep_m) for e in eqs]  # linear (affine) now 
>> > 
>> >     # Record each Max’s argument list after replacement 
>> >     max_specs: List[MaxSpec] = [] 
>> >     for raw, m in zip(maxes, syms_max): 
>> >         args = Tuple(*[a.xreplace(rep_m) for a in raw.args]) 
>> >         max_specs.append(MaxSpec(m=m, args=args, raw=raw)) 
>> > 
>> >     # Symbols present 
>> >     syms_all = Tuple(*eqs2).free_symbols 
>> >     return eqs2, max_specs, syms_max, syms_all, rep_m 
>> > 
>> > def lower_bound_propagation(eqs2: List[Eq], max_specs: List[MaxSpec]) 
>> -> 
>> > Dict[Symbol, sRational]: 
>> >     # Start with bounds from Max(const, x, …) where a pure numeric 
>> const is 
>> > present 
>> >     L: Dict[Symbol, Rational] = {} 
>> >     def upd(v, val): 
>> >         if v not in L or val > L[v]: 
>> >             L[v] = val 
>> > 
>> >     for ms in max_specs: 
>> >         consts = [a for a in ms.args if a.is_Number] 
>> >         if consts: 
>> >             c = max(consts)  # “epigraph” lower bound 
>> >             upd(ms.m, nsimplify(c)) 
>> > 
>> >     # Push through obvious affine equalities v = sum(a_i * w_i) + b 
>> with a_i 
>> > >= 0 
>> >     # (Very conservative: only pick up clearly nonnegative 
>> coefficients.) 
>> >     # Repeat to a fixpoint (few iterations usually suffice) 
>> >     unknowns = list(Tuple(*eqs2).free_symbols) 
>> >     for _ in range(3):  # small, safe number of passes; increase if you 
>> like 
>> >         pushed = False 
>> >         for e in eqs2: 
>> >             lhs, rhs = e.lhs, e.rhs 
>> >             if lhs.is_Symbol: 
>> >                 # collect nonnegative linear part and constant 
>> >                 rhs_lin = expand(rhs) 
>> >                 b = rhs_lin.as_independent(*unknowns, as_Add=True)[0] 
>> >                 rest = rhs_lin - b 
>> >                 lb = nsimplify(b) if b.is_Number else 0 
>> >                 ok = True 
>> >                 contrib = 0 
>> >                 for term in rest.as_ordered_terms(): 
>> >                     coeff, sym = term.as_independent(*unknowns, 
>> > as_Add=False) 
>> >                     if not sym.is_Symbol: 
>> >                         ok = False; break 
>> >                     coeff = nsimplify(coeff) 
>> >                     if coeff.is_Number and coeff >= 0 and sym in L: 
>> >                         contrib += coeff * L[sym] 
>> >                     elif coeff == 0: 
>> >                         continue 
>> >                     else: 
>> >                         ok = False; break 
>> >                 if ok: 
>> >                     newL = lb + contrib 
>> >                     if lhs not in L or newL > L[lhs]: 
>> >                         L[lhs] = newL; pushed = True 
>> >         if not pushed: 
>> >             break 
>> >     return L 
>> > 
>> > def solve_linear(eqs: List[Eq], unknowns: List[Symbol]) -> Dict[Symbol, 
>> > Expr]: 
>> >     # SymPy returns a FiniteSet with a Tuple solution (possibly 
>> parametric). 
>> >     solset = linsolve(eqs, unknowns) 
>> >     if not solset:  # no solution 
>> >         return {} 
>> >     (sol_tuple,) = tuple(solset) 
>> >     return {u: s for u, s in zip(unknowns, sol_tuple)} 
>> > 
>> > def initial_policy(max_specs: List[MaxSpec], L: Dict[Symbol, Expr] | 
>> None = 
>> > None): 
>> >     policy = [] 
>> >     for ms in max_specs: 
>> >         # Prefer a numeric const if present (freezes many Maxes 
>> immediately) 
>> >         const_ix = None 
>> >         best_val = None 
>> >         for j, a in enumerate(ms.args): 
>> >             if a.is_Number: 
>> >                 if best_val is None or a > best_val: 
>> >                     best_val, const_ix = a, j 
>> >         if const_ix is not None: 
>> >             policy.append(const_ix) 
>> >         else: 
>> >             policy.append(0) 
>> >     return policy 
>> > 
>> > def policy_iteration(eqs2: List[Eq], max_specs: List[MaxSpec], 
>> >                      syms_max: List[Symbol], 
>> >                      syms_all: Iterable[Symbol], 
>> >                      seed_numeric: Dict[Symbol, float] | None = None, 
>> >                      max_iters: int = 20, 
>> >                      verbose: bool = True): 
>> >     # Unknown ordering: m’s first, then “don’t care”, then “specials” 
>> >     # (Your earlier ordering works well; we’ll keep it simple: all 
>> symbols.) 
>> >     unknowns = list(syms_max) + [s for s in syms_all if s not in 
>> syms_max] 
>> > 
>> >     # Precompute bound-based initial policy 
>> >     L = lower_bound_propagation(eqs2, max_specs) 
>> >     policy = initial_policy(max_specs, L) 
>> > 
>> >     for it in range(max_iters): 
>> >         # Enforce chosen branches: add equalities m_i = args[policy[i]] 
>> >         eqs_pol = list(eqs2) 
>> >         for ms, j in zip(max_specs, policy): 
>> >             eqs_pol.append(Eq(ms.m, ms.args[j])) 
>> > 
>> >         sol = solve_linear(eqs_pol, unknowns) 
>> >         if not sol: 
>> >             if verbose: print(f"[policy {it}] infeasible under current 
>> > branch choices") 
>> >             return None  # infeasible policy (should be rare if system 
>> is 
>> > well-posed) 
>> > 
>> >         # Evaluate to decide if any Max branch is violated 
>> >         changed = False 
>> >         for i, ms in enumerate(max_specs): 
>> >             # Compute numeric-ish value of each arg under current 
>> solution 
>> >             # Substitute symbolic solution first; then optionally a 
>> numeric 
>> > seed 
>> >             def val(expr): 
>> >                 v = expr.xreplace(sol) 
>> >                 if seed_numeric: 
>> >                     v = v.xreplace({k: nsimplify(vv) for k, vv in 
>> > seed_numeric.items()}) 
>> >                 try: 
>> >                     return N(v, 40) 
>> >                 except Exception: 
>> >                     return v 
>> >             vals = [val(a) for a in ms.args] 
>> >             mval = val(ms.m.xreplace(sol)) 
>> >             # pick the arg with the (numerically) largest value 
>> >             # if ties, keep the current policy choice (stable) 
>> >             try: 
>> >                 best_ix = max(range(len(vals)), key=lambda j: vals[j]) 
>> >                 violated = (vals[best_ix] > mval + 1e-30)  # tiny slack 
>> to 
>> > avoid noise 
>> >             except TypeError: 
>> >                 # Non-numeric comparison; fall back to symbolic max 
>> check 
>> >                 violated = False 
>> >                 best_ix = policy[i] 
>> >                 for j, a in enumerate(ms.args): 
>> >                     if simplify((a - ms.args[policy[i]]).xreplace(sol)) 
>> > 0: 
>> >                         best_ix = j; violated = True; break 
>> > 
>> >             if violated and best_ix != policy[i]: 
>> >                 policy[i] = best_ix 
>> >                 changed = True 
>> > 
>> >         if verbose: 
>> >             print(f"[policy {it}] {'switch' if changed else 'fixed'}") 
>> >         if not changed: 
>> >             # Fixed point: return the full solved map (you can also 
>> project) 
>> >             return sol 
>> > 
>> >     if verbose: 
>> >         print("[policy] exceeded iteration cap.") 
>> >     return None 
>> > 
>> > # eqs = [...]  # your big list of Eq(...) from the prompt 
>> > 
>> > eqs2, max_specs, syms_max, syms_all, rep_m = build_max_epigraph(eqs) 
>> > 
>> > # Optional: seed a few core symbols with plausible numerics to break 
>> ties 
>> > consistently. 
>> > seed = {}  # e.g., {Symbol('v21'): 0.73, Symbol('v111'): 0.78} 
>> > 
>> > solution = policy_iteration(eqs2, max_specs, list(syms_max), syms_all, 
>> > seed_numeric=seed, verbose=True) 
>> > 
>> > if solution is None: 
>> >     print("No fixed policy found (under current setup).") 
>> > else: 
>> >     # You now have values/affine forms for ALL vars (including m_i). 
>> >     # If you want only the original v-variables: 
>> >     sol_orig = {s: e for s, e in solution.items() if 
>> s.name.startswith('v')} 
>> >     print("Solved entries:", len(sol_orig)) 
>> > ``` 
>> > 
>> > /c 
>> > On Tuesday, August 26, 2025 at 3:25:01 PM UTC-5 hungabo...@gmail.com 
>> wrote: 
>> > 
>> > Hi Oscar, Chris, 
>> > 
>> > Thank you for both for your suggestions. I very much like the 
>> > ideas from 
>> > both of you, and while I was doing something similar, this is 
>> > more 
>> > clear-cut and understandable. I can follow it easier that my 
>> > original 
>> > thoughts on it. :-) 
>> > 
>> > 
>> > Chris, 
>> > about the linear programming: This is wizard! I haven't thought 
>> > about 
>> > rewriting to a linear programming problem, and even though it 
>> > likely is 
>> > not equivalent, as long as there is only one solution satisfying 
>> > all of 
>> > these conditions, it will find it. 
>> > 
>> > Your solution needed a bit of tweaking: even though it is super 
>> > fast, but 
>> > it's output is not correct, because it doesn't satisfy the Max 
>> > equations, 
>> > it only satisfies that all of the args of Max is at most the Max 
>> > variable. 
>> > I think this is due to the fact that it's not an equivalent 
>> > rewrite. 
>> > However, when I add also that variables are supposed to be from 
>> > the 
>> > Interval(0,1), then it spits out an opt_val of 
>> > 23.138180070450467, and 
>> > that provides a solution that does satisfy all of the equations, 
>> > even the 
>> > Max ones. 
>> > 
>> > I did then the same computation with lpmax (with the conditions 
>> > of being 
>> > in Interval(0,1), and that provided the same solution 
>> > dictionary, and the 
>> > same opt_val value. And it was reasonably fast (within 
>> > minutes!). Doing 
>> > these together can probably be used, and if opt from lpmin is 
>> > the same as 
>> > opt from lpmax, together with the solution, then I can be 
>> > reasonably 
>> > confident that that is the only solution. (The only way to be 
>> > fully sure 
>> > is to compute for 30 linearly independent objective functions, 
>> > but that 
>> > might be a bit of an overkill. :-) ) 
>> > 
>> > 
>> > Oscar, 
>> > I extended your approach with some heuristics, namely knowing if 
>> > v_i is 
>> > from Interval(a_i, b_i) then sum(c_iv_i) + sum(-d_jv_j) is from 
>> > Interval(sum a_iv_i-d_jb_j, b_iv_i -d_ja_j), and also that 
>> > Max(v_i, v_j) 
>> > is then from the Interval(Max(a_i, a_j), Max(b_i, b_j)). 
>> > Applying this 
>> > over and over solves 10 out of the 30 Max variables. Then I'll 
>> > need to 
>> > start branching, and now I'm looking if I can apply the 
>> > heuristic at every 
>> > recursive branching step, then maybe I can quickly discard those 
>> > branches 
>> > where no solution lies, and arrive to the final solution. 
>> > 
>> > My gut feeling is that this should be faster than the linear 
>> > programming 
>> > approach, as this is a tailored solution to this specific 
>> > problem, but 
>> > seeing my previous failed attempts at the brancinh and how fast 
>> > the linear 
>> > programming is, I'm not so sure. I'll check both approaches for 
>> > timing, 
>> > and report back. 
>> > 
>> > Thanks again for all the help and pointers! 
>> > 
>> > Gábor 
>> > 
>> > 
>> > 
>> > On Mon, 25 Aug 2025, Chris Smith wrote: 
>> > 
>> > > That system is helpful and you've got some good advice 
>> > already. I would add 
>> > > that this sort of system can be reduced to a linear 
>> > programming form and 
>> > > sympy has a function that handles your system: 
>> > > ``` 
>> > > from sympy import * 
>> > > from sympy.solvers.simplex import lpmin 
>> > > 
>> > > 
>> > > def lpsys(eqs): 
>> > >     eqs = list(eqs) 
>> > >     maxes = Tuple(*eqs).atoms(Max) 
>> > >     syms_max = symbols(f'm0:{len(maxes)}', real=True) 
>> > >     rep_m = dict(zip(maxes, syms_max)) 
>> > >     eqs2 = [e.xreplace(rep_m) for e in eqs]  # linear now 
>> > after replacing 
>> > > Max with symbols 
>> > > 
>> > >     max_eq=[Eq(*z) for z in zip(syms_max, maxes)] 
>> > >     return eqs2, max_eq 
>> > > 
>> > > eqs2, max_eq =lpsys(eqs)  # your list of 239 equations is eqs 
>> > > obj = sum(ms.lhs for ms in max_eq) # minimize the sum of m 
>> > variables used to 
>> > > represent the Max functions 
>> > > 
>> > > cons = list(eqs2) 
>> > > 
>> > > # m_i >= each argument for Max's 
>> > > for meq in max_eq: 
>> > >     for a in meq.rhs.args: 
>> > >         cons.append(meq.lhs >= a) 
>> > > 
>> > > opt_val, sol = lpmin(obj, cons) 
>> > > ``` 
>> > > 
>> > > This gives 22.9893360923961 for the opt_val and solutions for 
>> > the 269 
>> > > equations (30 maxes and 239 others). 
>> > > 
>> > > /c 
>> > > On Sunday, August 24, 2025 at 9:30:38 AM UTC-5 Oscar wrote: 
>> > > On Sun, 24 Aug 2025 at 12:01, Gábor Horváth 
>> > > <hungabo...@gmail.com> wrote: 
>> > > > 
>> > > > 
>> > > > > Could you post an example of a large system? 
>> > > > 
>> > > > Attached one with 239 equations to this email. Not sure if 
>> > > attached files 
>> > > > go through, though. If not, I can paste it directly into the 
>> > > email body, 
>> > > > it's size is 17K. 
>> > > 
>> > > Yes, it has come through. 
>> > > 
>> > > Okay looking at this you definitely want a special algorithm 
>> > for 
>> > > these 
>> > > kinds of systems rather than just using a general purpose 
>> > > solver. 
>> > > Ideally SymPy would have a reduce function and if it did we 
>> > > could 
>> > > consider whether it should be able to handle larger systems of 
>> > > this 
>> > > kind efficiently. 
>> > > 
>> > > First find the Max expressions and any symbols that are in 
>> > them: 
>> > > 
>> > > maxes = Tuple(*eqs).atoms(Max) 
>> > > special = Tuple(*maxes).free_symbols 
>> > > 
>> > > Then replace all Max expressions with symbols themselves: 
>> > > 
>> > > syms_max = symbols(f'm:{len(maxes)}') 
>> > > rep_m = dict(zip(maxes, syms_max)) 
>> > > eqs2 = [eq.xreplace(rep_m) for eq in eqs] 
>> > > 
>> > > Now you have a linear system of equations for the original 
>> > > variables 
>> > > v0, v1 etc and also new variables m0, m1 representing the 
>> > maxes. 
>> > > We 
>> > > want to get the smallest possible system involving only the 
>> > > maxes, the 
>> > > symbols that are in the maxes (special) and as few of the 
>> > other 
>> > > symbols as possible. 
>> > > 
>> > > Choose the ordering of the symbols carefully and ask linsolve 
>> > to 
>> > > reduce the underdetermined system: 
>> > > 
>> > > dontcare = [s for s in syms if s not in special] 
>> > > unknowns = list(syms_max) + dontcare + list(special) 
>> > > [sol] = linsolve(eqs2, unknowns) 
>> > > 
>> > > In so far as possible this tries to solve for syms_max in 
>> > terms 
>> > > of 
>> > > everything else and to solve for everything in terms of the 
>> > > symbols in 
>> > > special. It succeeds in expressing all of the maxes in terms 
>> > of 
>> > > everything else: 
>> > > 
>> > > >>> sol.atoms(Symbol) - set(syms) 
>> > > set() 
>> > > 
>> > > Mostly this expresses everything in terms of the special 
>> > symbols 
>> > > although a few others still appear as free parameters: 
>> > > 
>> > > >>> print(Tuple(*sol).free_symbols) 
>> > > {v232, v238, v121, v223, v183, v197, v62, v3, v218, v189, 
>> > v106, 
>> > > v214, 
>> > > v216, v231, v11, v213, v6, v88, v71, v120, v111, v180, v21, 
>> > > v129, 
>> > > v208, v44, v153, v192, v201, v184} 
>> > > 
>> > > >>> print(Tuple(*sol).free_symbols - special) 
>> > > {v238, v197, v129, v223, v192, v214} 
>> > > 
>> > > Everything is now expressed in terms of these 30 symbols of 
>> > > which 24 
>> > > are symbols appearing in the maxes and 6 are not. These 6 
>> > > symbols 
>> > > appearing in maxes have been solved purely from the linear 
>> > > equations 
>> > > but are given in terms of the 30 symbols left over: 
>> > > 
>> > > >>> print(special - Tuple(*sol).free_symbols) 
>> > > {v25, v9, v154, v160, v229, v84} 
>> > > 
>> > > I think at this point then you can translate the solution into 
>> > > 30 
>> > > equations for the remaining 30 unknowns: 
>> > > 
>> > > rep = dict(zip(unknowns[30:], sol[30:])) 
>> > > eqs3 = [Eq(m.xreplace(rep), s) for s, m in zip(sol[:30], 
>> > maxes)] 
>> > > 
>> > > Now you have 30 equations for 30 unknowns involving 30 maxes 
>> > and 
>> > > the 
>> > > other equations and variables are all eliminated and solved 
>> > > 
>> > > The other equations after these 30 can be inspected to give 
>> > > bounds on 
>> > > the 30 variables left over. You have a parametric solution for 
>> > > the 209 
>> > > other variables in terms of the 30 symbols. You can substitute 
>> > > that 
>> > > solution into any auxiliary inequality constraints to further 
>> > > constraint the values of the 30 symbols (or prove 
>> > > unsatisfiability). 
>> > > 
>> > > These are the remaining Maxes: 
>> > > 
>> > > In [222]: for m in Tuple(*eqs3).atoms(Max): print(m.n(3)) 
>> > > Max(0.802, v111 - v153 + v180) 
>> > > Max(0.757, v62) 
>> > > Max(0.597, 49.8*v153 - 30.8*v180 - 54.3*v183 + 210.0*v189 - 
>> > > 210.0*v201 
>> > > + 15.4*v21 - 130.0*v218 - 4.23*v3 - 21.7*v44 + 210.0*v88 - 
>> > 33.9) 
>> > > Max(0.879, v189 - v201 + v88) 
>> > > Max(0.732, v21) 
>> > > Max(0.735, v216) 
>> > > Max(0.767, v44) 
>> > > Max(0.645, v231) 
>> > > Max(0.721, v184) 
>> > > Max(0.789, v121) 
>> > > Max(0.897, v189) 
>> > > Max(0.741, v3) 
>> > > Max(0.835, v153) 
>> > > Max(0.827, v88) 
>> > > Max(0.677, -100.0*v153 + 164.0*v180 + 46.8*v183 - 183.0*v189 + 
>> > > 183.0*v201 - 46.9*v21 + 128.0*v218 + 2.33*v3 - 11.2*v44 - 
>> > > 183.0*v88 + 
>> > > 1.07) 
>> > > Max(0.846, v201) 
>> > > Max(0.786, v183) 
>> > > Max(0.783, v6) 
>> > > Max(0.727, v213) 
>> > > Max(0.719, v71) 
>> > > Max(0.78, v111 - v153 + v218) 
>> > > Max(0.658, v208) 
>> > > Max(0.804, 2.92*v153 - 6.78*v180 - 0.734*v183 + 6.25*v189 - 
>> > > 1.72*v201 
>> > > + 1.16*v21 - 1.2*v218 - 0.0219*v3 - 0.123*v44 + 1.72*v88 - 
>> > > 0.473) 
>> > > Max(0.703, v11) 
>> > > Max(0.786, v120) 
>> > > Max(0.683, v232) 
>> > > Max(0.839, v218) 
>> > > Max(0.837, v106) 
>> > > Max(0.862, v180) 
>> > > Max(0.777, v111) 
>> > > 
>> > > These are given linearly in terms of the same symbols e.g.: 
>> > > 
>> > > >>> print(eqs3[0].n(3)) 
>> > > Eq(Max(0.757, v62), 474.0*v106 + 0.503*v111 - 81.4*v120 - 
>> > > 312.0*v121 
>> > > - 953.0*v153 + 1.8e+3*v180 + 506.0*v183 - 2.1e+3*v189 + 
>> > > 2.03e+3*v201 - 
>> > > 1.91*v208 - 543.0*v21 - 0.416*v216 + 1.53e+3*v218 + 7.85*v232 
>> > - 
>> > > 1.58*v238 - 71.3*v3 - 213.0*v44 - 13.0*v6 + 28.1*v62 - 
>> > > 2.03e+3*v88 - 
>> > > 61.2) 
>> > > 
>> > > Maybe we are actually putting this the wrong way round. Keep 
>> > the 
>> > > m 
>> > > symbols and invert to solve for the remaining symbols in terms 
>> > > of the 
>> > > Maxes: 
>> > > 
>> > > eqs4 = [Eq(m, s) for s, m in zip(sol[:30], syms_max)] 
>> > > [sol2] = linsolve(eqs4, syms2) 
>> > > 
>> > > Yes, that's better. Now you have solved for 209 symbols in 
>> > terms 
>> > > of 
>> > > these 30 and you have solutions for these 30 in terms of the 
>> > > maxes. 
>> > > Now each time you pick a branch for a Max it directly tells 
>> > you 
>> > > the 
>> > > value of some symbol which you can then eliminate from 
>> > > everything 
>> > > else. There is no further need to call linsolve because the 
>> > > linear 
>> > > equations are all solved and it is just a case of considering 
>> > > the 
>> > > maxes in turn. 
>> > > 
>> > > Or something like that... 
>> > > 
>> > > I guess this is already where you are at and you just need a 
>> > > more 
>> > > efficient way of pruning the 2^30 branches. 
>> > > 
>> > > -- 
>> > > 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 discussionvisithttps://
>> groups.google.com/d/msgid/sympy/403209cd-1bf6-4284-aef8-bc3a31 
>> > 3a23e 
>> > > bn%40googlegroups.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 sympy+un...@googlegroups.com. 
>> > To view this discussion visithttps://
>> groups.google.com/d/msgid/sympy/1c1fd924-ceda-45c9-8519-9a8d7c6f375 
>> > 3n%40googlegroups.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 sympy+unsubscr...@googlegroups.com.
To view this discussion visit 
https://groups.google.com/d/msgid/sympy/3ccc7b06-5633-4ac9-927f-9b3b8db24f86n%40googlegroups.com.

Reply via email to