-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsingleton_form.py
177 lines (147 loc) · 6.65 KB
/
singleton_form.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
from __future__ import division
import sympy
from sympy import Add,Mul,rcollect,Number,NumberSymbol,sin,cos,Pow,Integer,Symbol,fraction,gcd,div,I
from sympy.functions.elementary.trigonometric import TrigonometricFunction
from sympy.functions.elementary.trigonometric import InverseTrigonometricFunction
from sympy.core.numbers import ImaginaryUnit
from .form_output import *
from .form_utils import *
def is_complex_singleton_form(expr, accept_reals=True):
'''Determines if an expression is a complex number.
Args:
expr: A standard sympy expression
accept_reals: Whether or not the function will return true if there \
is no complex part to the number [OPTIONAL]
Returns:
A tuple containing:
[0]: bool containing the result
[1]: string describing the result
'''
if not is_singleton_form(expr)[0]:
return is_singleton_form(expr)
if isinstance(expr, (ImaginaryUnit, complex)):
return True, SingletonOutput.strout("IMAGINARY")
if isinstance(expr, (Number, NumberSymbol, float, int)):
return accept_reals, SingletonOutput.strout("REAL")
expr = mr_flatten(expr)
result = _complex_improper_term(expr)
if result[0]:
return not result[0], result[1]
if isinstance(expr, Add):
for j in expr.args:
if any(isinstance(k, ImaginaryUnit) for k in j.args):
return True, SingletonOutput.strout("COMPLEX")
if any(isinstance(l, Mul) for l in j.args):
if any(isinstance(k, ImaginaryUnit) for k in l.args):
return True, SingletonOutput.strout("COMPLEX")
if isinstance(expr, Mul):
if any(isinstance(k, ImaginaryUnit) for k in expr.args):
return True, SingletonOutput.strout("COMPLEX_NO_REAL")
return accept_reals, SingletonOutput.strout("REAL")
def _complex_improper_term(expr, funcs=(Add,Mul,Pow)):
'''Determines if an expression has I to a power, which can be simplified.
Args:
expr: A standard sympy expression
funcs: List of functions to traverse (OPTIONAL)
Returns:
A tuple containing:
[0]: Boolean containing the result
[1]: String describing the result
'''
if not isinstance(expr, funcs):
return False, SingletonOutput.strout("IMAGINARY_OK_FACTORS")
if isinstance(expr, Pow):
if isinstance(expr.args[0], ImaginaryUnit):
return True, SingletonOutput.strout("IMAGINARY_IMPROPER")
for i in expr.args:
if any(_complex_improper_term(j) for j in i.args):
return True, SingletonOutput.strout("IMAGINARY_IMPROPER")
return False, SingletonOutput.strout("IMAGINARY_OK_FACTORS")
def is_singleton_form(expr):
'''determines if the expression is a singleton.
Note: internally, 0 + 6 will pass as a singleton, because SymPy will
convert 0 + 6 to 6. This is Sympy's fault; if the issue is handled
it won't be here.
Args:
expr: A standard Sympy expression
Returns:
A tuple containing:
[0]: bool containing the result
[1]: string describing the result
'''
if isinstance(expr, (Number, float, int, complex, ImaginaryUnit, NumberSymbol, Symbol)):
return True, SingletonOutput.strout("VALID")
if isinstance(expr, Add):
result = singleton_combinable_terms(expr)
return not result[0], result[1]
if isinstance(expr, (Mul,Pow)):
if is_numerically_reducible_monomial(expr)[0]:
return False, UtilOutput.strout("REDUCIBLE")
return is_singleton_factor_form(expr)
if isinstance(expr, (TrigonometricFunction, InverseTrigonometricFunction)):
return True, SingletonOutput.strout("VALID_TRIG")
return False, SingletonOutput.strout("INVALID")
def is_singleton_factor_form(expr):
'''Determines if a product in a singleton is appropriate.
1. No symbols are allowed in the product.
2. Product cannot contain two rational numbers.
3. Product cannot contain any operations other than Mul.
Args:
expr: A Mul Sympy expression
Returns:
A tuple containing:
[0]: bool containing the result
[1]: string describing the result
'''
if len(expr.free_symbols) > 0 and not isinstance(expr, Symbol):
return False, SingletonOutput.strout("IMPROPER_TERM")
if isinstance(expr,Pow) and isinstance(expr.args[0], Number):
result = const_to_const(expr)
return not result[0], result[1]
for i in expr.args:
#Make sure that anything inside is either a number,
#numbersymbol, multiplication, or addition.
if not isinstance(i, (Number, ImaginaryUnit, NumberSymbol,Pow,Mul)):
return False, SingletonOutput.strout("IMPROPER_TERM")
if isinstance(i,Pow):
result = const_to_const(i)
if result[0]:
return False, result[1]
if isinstance(i,Mul):
if is_numerically_reducible_monomial(i)[0]:
return False, SingletonOutput.strout("IMPROPER_TERM")
if sum(isinstance(j, Number) for j in expr.args) > 1:
return False, SingletonOutput.strout("INVALID_PRODUCT")
return True, SingletonOutput.strout("VALID_PRODUCT")
def singleton_combinable_terms(expr):
'''determines if two terms in a singleton can be combined.
1. Two rational numbers are combinable.
2. Two identical rational numbers are combinable.
3. Two irrational numbers with combinable terms (pi, sqrt(2),etc)
are combinable
Args:
expr: A standard Sympy expression
Returns:
A tuple containing:
[0]: bool containing the result
[1]: string describing the result
'''
#Collect the bases for later comparison
bases = []
#We don't want symbols or subexpressions like Add
for i in expr.args:
if not isinstance(i, (Number, NumberSymbol, Mul)):
return True, SingletonOutput.strout("INVALID_SUM")
if isinstance(i, Mul):
bases += i.args
result = is_singleton_factor_form(i)
if not result[0]:
return True, result[1]
else:
bases.append(i)
#Any two rational numbers can be simplified
if sum(isinstance(i, Number) for i in expr.args) > 1:
return True, SingletonOutput.strout("INVALID_SUM")
if len(bases) != len(set(bases)):
return True, SingletonOutput.strout("INVALID_SUM")
return False, SingletonOutput.strout("VALID_SUM")