1010"""
1111
1212from itertools import product
13- from typing import Optional , Union
13+ from typing import Optional
1414
1515import numpy as np
1616import sympy
7171from mathics .core .systemsymbols import (
7272 SymbolAnd ,
7373 SymbolAutomatic ,
74+ SymbolComplex ,
7475 SymbolConditionalExpression ,
7576 SymbolD ,
7677 SymbolDerivative ,
7778 SymbolInfinity ,
7879 SymbolInfix ,
80+ SymbolInteger ,
7981 SymbolIntegrate ,
8082 SymbolLeft ,
8183 SymbolLog ,
8284 SymbolNIntegrate ,
8385 SymbolO ,
86+ SymbolReal ,
8487 SymbolRule ,
8588 SymbolSequence ,
8689 SymbolSeries ,
8790 SymbolSeriesData ,
8891 SymbolSimplify ,
8992 SymbolUndefined ,
9093)
94+ from mathics .eval .calculus import solve_sympy
9195from mathics .eval .makeboxes import format_element
9296from mathics .eval .nevaluator import eval_N
9397
@@ -2208,105 +2212,38 @@ class Solve(Builtin):
22082212 messages = {
22092213 "eqf" : "`1` is not a well-formed equation." ,
22102214 "svars" : 'Equations may not give solutions for all "solve" variables.' ,
2215+ "fulldim" : "The solution set contains a full-dimensional component; use Reduce for complete solution information." ,
22112216 }
22122217
2213- # FIXME: the problem with removing the domain parameter from the outside
2214- # is that the we can't make use of this information inside
2215- # the evaluation method where it is may be needed.
22162218 rules = {
2217- "Solve[eqs_, vars_, Complexes]" : "Solve[eqs, vars]" ,
2218- "Solve[eqs_, vars_, Reals]" : (
2219- "Cases[Solve[eqs, vars], {Rule[x_,y_?RealValuedNumberQ]}]"
2220- ),
2221- "Solve[eqs_, vars_, Integers]" : (
2222- "Cases[Solve[eqs, vars], {Rule[x_,y_Integer]}]"
2223- ),
2219+ "Solve[eqs_, vars_]" : "Solve[eqs, vars, Complexes]"
22242220 }
22252221 summary_text = "find generic solutions for variables"
22262222
2227- def eval (self , eqs , vars , evaluation : Evaluation ):
2228- "Solve[eqs_, vars_]"
2223+ def eval (self , eqs , vars , domain , evaluation : Evaluation ):
2224+ "Solve[eqs_, vars_, domain_ ]"
22292225
2230- vars_original = vars
2231- head_name = vars .get_head_name ()
2226+ variables = vars
2227+ head_name = variables .get_head_name ()
22322228 if head_name == "System`List" :
2233- vars = vars .elements
2229+ variables = variables .elements
22342230 else :
2235- vars = [vars ]
2236- for var in vars :
2231+ variables = [variables ]
2232+ for var in variables :
22372233 if (
22382234 (isinstance (var , Atom ) and not isinstance (var , Symbol )) or
22392235 head_name in ("System`Plus" , "System`Times" , "System`Power" ) or # noqa
22402236 A_CONSTANT & var .get_attributes (evaluation .definitions )
22412237 ):
22422238
2243- evaluation .message ("Solve" , "ivar" , vars_original )
2239+ evaluation .message ("Solve" , "ivar" , vars )
22442240 return
22452241
2246- vars_sympy = [var .to_sympy () for var in vars ]
2247- if None in vars_sympy :
2242+ sympy_variables = [var .to_sympy () for var in variables ]
2243+ if None in sympy_variables :
22482244 evaluation .message ("Solve" , "ivar" )
22492245 return
2250- all_var_tuples = list (zip (vars , vars_sympy ))
2251-
2252- def cut_var_dimension (expressions : Union [Expression , list [Expression ]]):
2253- '''delete unused variables to avoid SymPy's PolynomialError
2254- : Not a zero-dimensional system in e.g. Solve[x^2==1&&z^2==-1,{x,y,z}]'''
2255- if not isinstance (expressions , list ):
2256- expressions = [expressions ]
2257- subset_vars = set ()
2258- subset_vars_sympy = set ()
2259- for var , var_sympy in all_var_tuples :
2260- pattern = Pattern .create (var )
2261- for equation in expressions :
2262- if not equation .is_free (pattern , evaluation ):
2263- subset_vars .add (var )
2264- subset_vars_sympy .add (var_sympy )
2265- return subset_vars , subset_vars_sympy
2266-
2267- def solve_sympy (equations : Union [Expression , list [Expression ]]):
2268- if not isinstance (equations , list ):
2269- equations = [equations ]
2270- equations_sympy = []
2271- denoms_sympy = []
2272- subset_vars , subset_vars_sympy = cut_var_dimension (equations )
2273- for equation in equations :
2274- if equation is SymbolTrue :
2275- continue
2276- elif equation is SymbolFalse :
2277- return []
2278- elements = equation .elements
2279- for left , right in [(elements [index ], elements [index + 1 ]) for index in range (len (elements ) - 1 )]:
2280- # ↑ to deal with things like a==b==c==d
2281- left = left .to_sympy ()
2282- right = right .to_sympy ()
2283- if left is None or right is None :
2284- return []
2285- equation_sympy = left - right
2286- equation_sympy = sympy .together (equation_sympy )
2287- equation_sympy = sympy .cancel (equation_sympy )
2288- equations_sympy .append (equation_sympy )
2289- numer , denom = equation_sympy .as_numer_denom ()
2290- denoms_sympy .append (denom )
2291- try :
2292- results = sympy .solve (equations_sympy , subset_vars_sympy , dict = True ) # no transform_dict needed with dict=True
2293- # Filter out results for which denominator is 0
2294- # (SymPy should actually do that itself, but it doesn't!)
2295- results = [
2296- sol
2297- for sol in results
2298- if all (sympy .simplify (denom .subs (sol )) != 0 for denom in denoms_sympy )
2299- ]
2300- return results
2301- except sympy .PolynomialError :
2302- # raised for e.g. Solve[x^2==1&&z^2==-1,{x,y,z}] when not deleting
2303- # unused variables beforehand
2304- return []
2305- except NotImplementedError :
2306- return []
2307- except TypeError as exc :
2308- if str (exc ).startswith ("expected Symbol, Function or Derivative" ):
2309- evaluation .message ("Solve" , "ivar" , vars_original )
2246+ variable_tuples = list (zip (variables , sympy_variables ))
23102247
23112248 def solve_recur (expression : Expression ):
23122249 '''solve And, Or and List within the scope of sympy,
@@ -2334,7 +2271,7 @@ def solve_recur(expression: Expression):
23342271 inequations .append (sub_condition )
23352272 else :
23362273 inequations .append (child .to_sympy ())
2337- solutions .extend (solve_sympy (equations ))
2274+ solutions .extend (solve_sympy (evaluation , equations , variables , domain ))
23382275 conditions = sympy .And (* inequations )
23392276 result = [sol for sol in solutions if conditions .subs (sol )]
23402277 return result , None if solutions else conditions
@@ -2344,7 +2281,7 @@ def solve_recur(expression: Expression):
23442281 conditions = []
23452282 for child in expression .elements :
23462283 if child .has_form ("Equal" , 2 ):
2347- solutions .extend (solve_sympy (child ))
2284+ solutions .extend (solve_sympy (evaluation , child , variables , domain ))
23482285 elif child .get_head_name () in ('System`And' , 'System`Or' ): # I don't believe List would be in here
23492286 sub_solution , sub_condition = solve_recur (child )
23502287 solutions .extend (sub_solution )
@@ -2363,8 +2300,8 @@ def solve_recur(expression: Expression):
23632300 if conditions is not None :
23642301 evaluation .message ("Solve" , "fulldim" )
23652302 else :
2366- if eqs .has_form ( " Equal", 2 ) :
2367- solutions = solve_sympy (eqs )
2303+ if eqs .get_head_name () == "System` Equal" :
2304+ solutions = solve_sympy (evaluation , eqs , variables , domain )
23682305 else :
23692306 evaluation .message ("Solve" , "fulldim" )
23702307 return ListExpression (ListExpression ())
@@ -2374,7 +2311,7 @@ def solve_recur(expression: Expression):
23742311 return ListExpression (ListExpression ())
23752312
23762313 if any (
2377- sol and any (var not in sol for var in vars_sympy ) for sol in solutions
2314+ sol and any (var not in sol for var in sympy_variables ) for sol in solutions
23782315 ):
23792316 evaluation .message ("Solve" , "svars" )
23802317
@@ -2383,7 +2320,7 @@ def solve_recur(expression: Expression):
23832320 ListExpression (
23842321 * (
23852322 Expression (SymbolRule , var , from_sympy (sol [var_sympy ]))
2386- for var , var_sympy in all_var_tuples
2323+ for var , var_sympy in variable_tuples
23872324 if var_sympy in sol
23882325 ),
23892326 )
0 commit comments