10
10
"""
11
11
12
12
from itertools import product
13
- from typing import Optional , Union
13
+ from typing import Optional
14
14
15
15
import numpy as np
16
16
import sympy
71
71
from mathics .core .systemsymbols import (
72
72
SymbolAnd ,
73
73
SymbolAutomatic ,
74
+ SymbolComplex ,
74
75
SymbolConditionalExpression ,
75
76
SymbolD ,
76
77
SymbolDerivative ,
77
78
SymbolInfinity ,
78
79
SymbolInfix ,
80
+ SymbolInteger ,
79
81
SymbolIntegrate ,
80
82
SymbolLeft ,
81
83
SymbolLog ,
82
84
SymbolNIntegrate ,
83
85
SymbolO ,
86
+ SymbolReal ,
84
87
SymbolRule ,
85
88
SymbolSequence ,
86
89
SymbolSeries ,
87
90
SymbolSeriesData ,
88
91
SymbolSimplify ,
89
92
SymbolUndefined ,
90
93
)
94
+ from mathics .eval .calculus import solve_sympy
91
95
from mathics .eval .makeboxes import format_element
92
96
from mathics .eval .nevaluator import eval_N
93
97
@@ -2208,105 +2212,38 @@ class Solve(Builtin):
2208
2212
messages = {
2209
2213
"eqf" : "`1` is not a well-formed equation." ,
2210
2214
"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." ,
2211
2216
}
2212
2217
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.
2216
2218
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]"
2224
2220
}
2225
2221
summary_text = "find generic solutions for variables"
2226
2222
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_ ]"
2229
2225
2230
- vars_original = vars
2231
- head_name = vars .get_head_name ()
2226
+ variables = vars
2227
+ head_name = variables .get_head_name ()
2232
2228
if head_name == "System`List" :
2233
- vars = vars .elements
2229
+ variables = variables .elements
2234
2230
else :
2235
- vars = [vars ]
2236
- for var in vars :
2231
+ variables = [variables ]
2232
+ for var in variables :
2237
2233
if (
2238
2234
(isinstance (var , Atom ) and not isinstance (var , Symbol )) or
2239
2235
head_name in ("System`Plus" , "System`Times" , "System`Power" ) or # noqa
2240
2236
A_CONSTANT & var .get_attributes (evaluation .definitions )
2241
2237
):
2242
2238
2243
- evaluation .message ("Solve" , "ivar" , vars_original )
2239
+ evaluation .message ("Solve" , "ivar" , vars )
2244
2240
return
2245
2241
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 :
2248
2244
evaluation .message ("Solve" , "ivar" )
2249
2245
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 ))
2310
2247
2311
2248
def solve_recur (expression : Expression ):
2312
2249
'''solve And, Or and List within the scope of sympy,
@@ -2334,7 +2271,7 @@ def solve_recur(expression: Expression):
2334
2271
inequations .append (sub_condition )
2335
2272
else :
2336
2273
inequations .append (child .to_sympy ())
2337
- solutions .extend (solve_sympy (equations ))
2274
+ solutions .extend (solve_sympy (evaluation , equations , variables , domain ))
2338
2275
conditions = sympy .And (* inequations )
2339
2276
result = [sol for sol in solutions if conditions .subs (sol )]
2340
2277
return result , None if solutions else conditions
@@ -2344,7 +2281,7 @@ def solve_recur(expression: Expression):
2344
2281
conditions = []
2345
2282
for child in expression .elements :
2346
2283
if child .has_form ("Equal" , 2 ):
2347
- solutions .extend (solve_sympy (child ))
2284
+ solutions .extend (solve_sympy (evaluation , child , variables , domain ))
2348
2285
elif child .get_head_name () in ('System`And' , 'System`Or' ): # I don't believe List would be in here
2349
2286
sub_solution , sub_condition = solve_recur (child )
2350
2287
solutions .extend (sub_solution )
@@ -2363,8 +2300,8 @@ def solve_recur(expression: Expression):
2363
2300
if conditions is not None :
2364
2301
evaluation .message ("Solve" , "fulldim" )
2365
2302
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 )
2368
2305
else :
2369
2306
evaluation .message ("Solve" , "fulldim" )
2370
2307
return ListExpression (ListExpression ())
@@ -2374,7 +2311,7 @@ def solve_recur(expression: Expression):
2374
2311
return ListExpression (ListExpression ())
2375
2312
2376
2313
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
2378
2315
):
2379
2316
evaluation .message ("Solve" , "svars" )
2380
2317
@@ -2383,7 +2320,7 @@ def solve_recur(expression: Expression):
2383
2320
ListExpression (
2384
2321
* (
2385
2322
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
2387
2324
if var_sympy in sol
2388
2325
),
2389
2326
)
0 commit comments