/
expr.py
4101 lines (3407 loc) · 137 KB
/
expr.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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
from typing import Tuple as tTuple
from collections.abc import Iterable
from functools import reduce
from .sympify import sympify, _sympify, SympifyError
from .basic import Basic, Atom
from .singleton import S
from .evalf import EvalfMixin, pure_complex
from .decorators import call_highest_priority, sympify_method_args, sympify_return
from .cache import cacheit
from .compatibility import as_int, default_sort_key
from .kind import NumberKind
from sympy.utilities.misc import func_name
from mpmath.libmp import mpf_log, prec_to_dps
from collections import defaultdict
@sympify_method_args
class Expr(Basic, EvalfMixin):
"""
Base class for algebraic expressions.
Explanation
===========
Everything that requires arithmetic operations to be defined
should subclass this class, instead of Basic (which should be
used only for argument storage and expression manipulation, i.e.
pattern matching, substitutions, etc).
If you want to override the comparisons of expressions:
Should use _eval_is_ge for inequality, or _eval_is_eq, with multiple dispatch.
_eval_is_ge return true if x >= y, false if x < y, and None if the two types
are not comparable or the comparison is indeterminate
See Also
========
sympy.core.basic.Basic
"""
__slots__ = () # type: tTuple[str, ...]
is_scalar = True # self derivative is 1
@property
def _diff_wrt(self):
"""Return True if one can differentiate with respect to this
object, else False.
Explanation
===========
Subclasses such as Symbol, Function and Derivative return True
to enable derivatives wrt them. The implementation in Derivative
separates the Symbol and non-Symbol (_diff_wrt=True) variables and
temporarily converts the non-Symbols into Symbols when performing
the differentiation. By default, any object deriving from Expr
will behave like a scalar with self.diff(self) == 1. If this is
not desired then the object must also set `is_scalar = False` or
else define an _eval_derivative routine.
Note, see the docstring of Derivative for how this should work
mathematically. In particular, note that expr.subs(yourclass, Symbol)
should be well-defined on a structural level, or this will lead to
inconsistent results.
Examples
========
>>> from sympy import Expr
>>> e = Expr()
>>> e._diff_wrt
False
>>> class MyScalar(Expr):
... _diff_wrt = True
...
>>> MyScalar().diff(MyScalar())
1
>>> class MySymbol(Expr):
... _diff_wrt = True
... is_scalar = False
...
>>> MySymbol().diff(MySymbol())
Derivative(MySymbol(), MySymbol())
"""
return False
@cacheit
def sort_key(self, order=None):
coeff, expr = self.as_coeff_Mul()
if expr.is_Pow:
if expr.base is S.Exp1:
# If we remove this, many doctests will go crazy:
# (keeps E**x sorted like the exp(x) function,
# part of exp(x) to E**x transition)
expr, exp = Function("exp")(expr.exp), S.One
else:
expr, exp = expr.args
else:
expr, exp = expr, S.One
if expr.is_Dummy:
args = (expr.sort_key(),)
elif expr.is_Atom:
args = (str(expr),)
else:
if expr.is_Add:
args = expr.as_ordered_terms(order=order)
elif expr.is_Mul:
args = expr.as_ordered_factors(order=order)
else:
args = expr.args
args = tuple(
[ default_sort_key(arg, order=order) for arg in args ])
args = (len(args), tuple(args))
exp = exp.sort_key(order=order)
return expr.class_key(), args, exp, coeff
def __hash__(self) -> int:
# hash cannot be cached using cache_it because infinite recurrence
# occurs as hash is needed for setting cache dictionary keys
h = self._mhash
if h is None:
h = hash((type(self).__name__,) + self._hashable_content())
self._mhash = h
return h
def _hashable_content(self):
"""Return a tuple of information about self that can be used to
compute the hash. If a class defines additional attributes,
like ``name`` in Symbol, then this method should be updated
accordingly to return such relevant attributes.
Defining more than _hashable_content is necessary if __eq__ has
been defined by a class. See note about this in Basic.__eq__."""
return self._args
def __eq__(self, other):
try:
other = _sympify(other)
if not isinstance(other, Expr):
return False
except (SympifyError, SyntaxError):
return False
# check for pure number expr
if not (self.is_Number and other.is_Number) and (
type(self) != type(other)):
return False
a, b = self._hashable_content(), other._hashable_content()
if a != b:
return False
# check number *in* an expression
for a, b in zip(a, b):
if not isinstance(a, Expr):
continue
if a.is_Number and type(a) != type(b):
return False
return True
# ***************
# * Arithmetics *
# ***************
# Expr and its sublcasses use _op_priority to determine which object
# passed to a binary special method (__mul__, etc.) will handle the
# operation. In general, the 'call_highest_priority' decorator will choose
# the object with the highest _op_priority to handle the call.
# Custom subclasses that want to define their own binary special methods
# should set an _op_priority value that is higher than the default.
#
# **NOTE**:
# This is a temporary fix, and will eventually be replaced with
# something better and more powerful. See issue 5510.
_op_priority = 10.0
@property
def _add_handler(self):
return Add
@property
def _mul_handler(self):
return Mul
def __pos__(self):
return self
def __neg__(self):
# Mul has its own __neg__ routine, so we just
# create a 2-args Mul with the -1 in the canonical
# slot 0.
c = self.is_commutative
return Mul._from_args((S.NegativeOne, self), c)
def __abs__(self):
from sympy import Abs
return Abs(self)
@sympify_return([('other', 'Expr')], NotImplemented)
@call_highest_priority('__radd__')
def __add__(self, other):
return Add(self, other)
@sympify_return([('other', 'Expr')], NotImplemented)
@call_highest_priority('__add__')
def __radd__(self, other):
return Add(other, self)
@sympify_return([('other', 'Expr')], NotImplemented)
@call_highest_priority('__rsub__')
def __sub__(self, other):
return Add(self, -other)
@sympify_return([('other', 'Expr')], NotImplemented)
@call_highest_priority('__sub__')
def __rsub__(self, other):
return Add(other, -self)
@sympify_return([('other', 'Expr')], NotImplemented)
@call_highest_priority('__rmul__')
def __mul__(self, other):
return Mul(self, other)
@sympify_return([('other', 'Expr')], NotImplemented)
@call_highest_priority('__mul__')
def __rmul__(self, other):
return Mul(other, self)
@sympify_return([('other', 'Expr')], NotImplemented)
@call_highest_priority('__rpow__')
def _pow(self, other):
return Pow(self, other)
def __pow__(self, other, mod=None):
if mod is None:
return self._pow(other)
try:
_self, other, mod = as_int(self), as_int(other), as_int(mod)
if other >= 0:
return pow(_self, other, mod)
else:
from sympy.core.numbers import mod_inverse
return mod_inverse(pow(_self, -other, mod), mod)
except ValueError:
power = self._pow(other)
try:
return power%mod
except TypeError:
return NotImplemented
@sympify_return([('other', 'Expr')], NotImplemented)
@call_highest_priority('__pow__')
def __rpow__(self, other):
return Pow(other, self)
@sympify_return([('other', 'Expr')], NotImplemented)
@call_highest_priority('__rtruediv__')
def __truediv__(self, other):
denom = Pow(other, S.NegativeOne)
if self is S.One:
return denom
else:
return Mul(self, denom)
@sympify_return([('other', 'Expr')], NotImplemented)
@call_highest_priority('__truediv__')
def __rtruediv__(self, other):
denom = Pow(self, S.NegativeOne)
if other is S.One:
return denom
else:
return Mul(other, denom)
@sympify_return([('other', 'Expr')], NotImplemented)
@call_highest_priority('__rmod__')
def __mod__(self, other):
return Mod(self, other)
@sympify_return([('other', 'Expr')], NotImplemented)
@call_highest_priority('__mod__')
def __rmod__(self, other):
return Mod(other, self)
@sympify_return([('other', 'Expr')], NotImplemented)
@call_highest_priority('__rfloordiv__')
def __floordiv__(self, other):
from sympy.functions.elementary.integers import floor
return floor(self / other)
@sympify_return([('other', 'Expr')], NotImplemented)
@call_highest_priority('__floordiv__')
def __rfloordiv__(self, other):
from sympy.functions.elementary.integers import floor
return floor(other / self)
@sympify_return([('other', 'Expr')], NotImplemented)
@call_highest_priority('__rdivmod__')
def __divmod__(self, other):
from sympy.functions.elementary.integers import floor
return floor(self / other), Mod(self, other)
@sympify_return([('other', 'Expr')], NotImplemented)
@call_highest_priority('__divmod__')
def __rdivmod__(self, other):
from sympy.functions.elementary.integers import floor
return floor(other / self), Mod(other, self)
def __int__(self):
# Although we only need to round to the units position, we'll
# get one more digit so the extra testing below can be avoided
# unless the rounded value rounded to an integer, e.g. if an
# expression were equal to 1.9 and we rounded to the unit position
# we would get a 2 and would not know if this rounded up or not
# without doing a test (as done below). But if we keep an extra
# digit we know that 1.9 is not the same as 1 and there is no
# need for further testing: our int value is correct. If the value
# were 1.99, however, this would round to 2.0 and our int value is
# off by one. So...if our round value is the same as the int value
# (regardless of how much extra work we do to calculate extra decimal
# places) we need to test whether we are off by one.
from sympy import Dummy
if not self.is_number:
raise TypeError("can't convert symbols to int")
r = self.round(2)
if not r.is_Number:
raise TypeError("can't convert complex to int")
if r in (S.NaN, S.Infinity, S.NegativeInfinity):
raise TypeError("can't convert %s to int" % r)
i = int(r)
if not i:
return 0
# off-by-one check
if i == r and not (self - i).equals(0):
isign = 1 if i > 0 else -1
x = Dummy()
# in the following (self - i).evalf(2) will not always work while
# (self - r).evalf(2) and the use of subs does; if the test that
# was added when this comment was added passes, it might be safe
# to simply use sign to compute this rather than doing this by hand:
diff_sign = 1 if (self - x).evalf(2, subs={x: i}) > 0 else -1
if diff_sign != isign:
i -= isign
return i
def __float__(self):
# Don't bother testing if it's a number; if it's not this is going
# to fail, and if it is we still need to check that it evalf'ed to
# a number.
result = self.evalf()
if result.is_Number:
return float(result)
if result.is_number and result.as_real_imag()[1]:
raise TypeError("can't convert complex to float")
raise TypeError("can't convert expression to float")
def __complex__(self):
result = self.evalf()
re, im = result.as_real_imag()
return complex(float(re), float(im))
@sympify_return([('other', 'Expr')], NotImplemented)
def __ge__(self, other):
from .relational import GreaterThan
return GreaterThan(self, other)
@sympify_return([('other', 'Expr')], NotImplemented)
def __le__(self, other):
from .relational import LessThan
return LessThan(self, other)
@sympify_return([('other', 'Expr')], NotImplemented)
def __gt__(self, other):
from .relational import StrictGreaterThan
return StrictGreaterThan(self, other)
@sympify_return([('other', 'Expr')], NotImplemented)
def __lt__(self, other):
from .relational import StrictLessThan
return StrictLessThan(self, other)
def __trunc__(self):
if not self.is_number:
raise TypeError("can't truncate symbols and expressions")
else:
return Integer(self)
@staticmethod
def _from_mpmath(x, prec):
from sympy import Float
if hasattr(x, "_mpf_"):
return Float._new(x._mpf_, prec)
elif hasattr(x, "_mpc_"):
re, im = x._mpc_
re = Float._new(re, prec)
im = Float._new(im, prec)*S.ImaginaryUnit
return re + im
else:
raise TypeError("expected mpmath number (mpf or mpc)")
@property
def is_number(self):
"""Returns True if ``self`` has no free symbols and no
undefined functions (AppliedUndef, to be precise). It will be
faster than ``if not self.free_symbols``, however, since
``is_number`` will fail as soon as it hits a free symbol
or undefined function.
Examples
========
>>> from sympy import Integral, cos, sin, pi
>>> from sympy.core.function import Function
>>> from sympy.abc import x
>>> f = Function('f')
>>> x.is_number
False
>>> f(1).is_number
False
>>> (2*x).is_number
False
>>> (2 + Integral(2, x)).is_number
False
>>> (2 + Integral(2, (x, 1, 2))).is_number
True
Not all numbers are Numbers in the SymPy sense:
>>> pi.is_number, pi.is_Number
(True, False)
If something is a number it should evaluate to a number with
real and imaginary parts that are Numbers; the result may not
be comparable, however, since the real and/or imaginary part
of the result may not have precision.
>>> cos(1).is_number and cos(1).is_comparable
True
>>> z = cos(1)**2 + sin(1)**2 - 1
>>> z.is_number
True
>>> z.is_comparable
False
See Also
========
sympy.core.basic.Basic.is_comparable
"""
return all(obj.is_number for obj in self.args)
def _random(self, n=None, re_min=-1, im_min=-1, re_max=1, im_max=1):
"""Return self evaluated, if possible, replacing free symbols with
random complex values, if necessary.
Explanation
===========
The random complex value for each free symbol is generated
by the random_complex_number routine giving real and imaginary
parts in the range given by the re_min, re_max, im_min, and im_max
values. The returned value is evaluated to a precision of n
(if given) else the maximum of 15 and the precision needed
to get more than 1 digit of precision. If the expression
could not be evaluated to a number, or could not be evaluated
to more than 1 digit of precision, then None is returned.
Examples
========
>>> from sympy import sqrt
>>> from sympy.abc import x, y
>>> x._random() # doctest: +SKIP
0.0392918155679172 + 0.916050214307199*I
>>> x._random(2) # doctest: +SKIP
-0.77 - 0.87*I
>>> (x + y/2)._random(2) # doctest: +SKIP
-0.57 + 0.16*I
>>> sqrt(2)._random(2)
1.4
See Also
========
sympy.testing.randtest.random_complex_number
"""
free = self.free_symbols
prec = 1
if free:
from sympy.testing.randtest import random_complex_number
a, c, b, d = re_min, re_max, im_min, im_max
reps = dict(list(zip(free, [random_complex_number(a, b, c, d, rational=True)
for zi in free])))
try:
nmag = abs(self.evalf(2, subs=reps))
except (ValueError, TypeError):
# if an out of range value resulted in evalf problems
# then return None -- XXX is there a way to know how to
# select a good random number for a given expression?
# e.g. when calculating n! negative values for n should not
# be used
return None
else:
reps = {}
nmag = abs(self.evalf(2))
if not hasattr(nmag, '_prec'):
# e.g. exp_polar(2*I*pi) doesn't evaluate but is_number is True
return None
if nmag._prec == 1:
# increase the precision up to the default maximum
# precision to see if we can get any significance
from mpmath.libmp.libintmath import giant_steps
from sympy.core.evalf import DEFAULT_MAXPREC as target
# evaluate
for prec in giant_steps(2, target):
nmag = abs(self.evalf(prec, subs=reps))
if nmag._prec != 1:
break
if nmag._prec != 1:
if n is None:
n = max(prec, 15)
return self.evalf(n, subs=reps)
# never got any significance
return None
def is_constant(self, *wrt, **flags):
"""Return True if self is constant, False if not, or None if
the constancy could not be determined conclusively.
Explanation
===========
If an expression has no free symbols then it is a constant. If
there are free symbols it is possible that the expression is a
constant, perhaps (but not necessarily) zero. To test such
expressions, a few strategies are tried:
1) numerical evaluation at two random points. If two such evaluations
give two different values and the values have a precision greater than
1 then self is not constant. If the evaluations agree or could not be
obtained with any precision, no decision is made. The numerical testing
is done only if ``wrt`` is different than the free symbols.
2) differentiation with respect to variables in 'wrt' (or all free
symbols if omitted) to see if the expression is constant or not. This
will not always lead to an expression that is zero even though an
expression is constant (see added test in test_expr.py). If
all derivatives are zero then self is constant with respect to the
given symbols.
3) finding out zeros of denominator expression with free_symbols.
It won't be constant if there are zeros. It gives more negative
answers for expression that are not constant.
If neither evaluation nor differentiation can prove the expression is
constant, None is returned unless two numerical values happened to be
the same and the flag ``failing_number`` is True -- in that case the
numerical value will be returned.
If flag simplify=False is passed, self will not be simplified;
the default is True since self should be simplified before testing.
Examples
========
>>> from sympy import cos, sin, Sum, S, pi
>>> from sympy.abc import a, n, x, y
>>> x.is_constant()
False
>>> S(2).is_constant()
True
>>> Sum(x, (x, 1, 10)).is_constant()
True
>>> Sum(x, (x, 1, n)).is_constant()
False
>>> Sum(x, (x, 1, n)).is_constant(y)
True
>>> Sum(x, (x, 1, n)).is_constant(n)
False
>>> Sum(x, (x, 1, n)).is_constant(x)
True
>>> eq = a*cos(x)**2 + a*sin(x)**2 - a
>>> eq.is_constant()
True
>>> eq.subs({x: pi, a: 2}) == eq.subs({x: pi, a: 3}) == 0
True
>>> (0**x).is_constant()
False
>>> x.is_constant()
False
>>> (x**x).is_constant()
False
>>> one = cos(x)**2 + sin(x)**2
>>> one.is_constant()
True
>>> ((one - 1)**(x + 1)).is_constant() in (True, False) # could be 0 or 1
True
"""
def check_denominator_zeros(expression):
from sympy.solvers.solvers import denoms
retNone = False
for den in denoms(expression):
z = den.is_zero
if z is True:
return True
if z is None:
retNone = True
if retNone:
return None
return False
simplify = flags.get('simplify', True)
if self.is_number:
return True
free = self.free_symbols
if not free:
return True # assume f(1) is some constant
# if we are only interested in some symbols and they are not in the
# free symbols then this expression is constant wrt those symbols
wrt = set(wrt)
if wrt and not wrt & free:
return True
wrt = wrt or free
# simplify unless this has already been done
expr = self
if simplify:
expr = expr.simplify()
# is_zero should be a quick assumptions check; it can be wrong for
# numbers (see test_is_not_constant test), giving False when it
# shouldn't, but hopefully it will never give True unless it is sure.
if expr.is_zero:
return True
# Don't attempt subsitution or differentiation with non-number symbols
wrt_number = {sym for sym in wrt if sym.kind is NumberKind}
# try numerical evaluation to see if we get two different values
failing_number = None
if wrt_number == free:
# try 0 (for a) and 1 (for b)
try:
a = expr.subs(list(zip(free, [0]*len(free))),
simultaneous=True)
if a is S.NaN:
# evaluation may succeed when substitution fails
a = expr._random(None, 0, 0, 0, 0)
except ZeroDivisionError:
a = None
if a is not None and a is not S.NaN:
try:
b = expr.subs(list(zip(free, [1]*len(free))),
simultaneous=True)
if b is S.NaN:
# evaluation may succeed when substitution fails
b = expr._random(None, 1, 0, 1, 0)
except ZeroDivisionError:
b = None
if b is not None and b is not S.NaN and b.equals(a) is False:
return False
# try random real
b = expr._random(None, -1, 0, 1, 0)
if b is not None and b is not S.NaN and b.equals(a) is False:
return False
# try random complex
b = expr._random()
if b is not None and b is not S.NaN:
if b.equals(a) is False:
return False
failing_number = a if a.is_number else b
# now we will test each wrt symbol (or all free symbols) to see if the
# expression depends on them or not using differentiation. This is
# not sufficient for all expressions, however, so we don't return
# False if we get a derivative other than 0 with free symbols.
for w in wrt_number:
deriv = expr.diff(w)
if simplify:
deriv = deriv.simplify()
if deriv != 0:
if not (pure_complex(deriv, or_real=True)):
if flags.get('failing_number', False):
return failing_number
elif deriv.free_symbols:
# dead line provided _random returns None in such cases
return None
return False
cd = check_denominator_zeros(self)
if cd is True:
return False
elif cd is None:
return None
return True
def equals(self, other, failing_expression=False):
"""Return True if self == other, False if it doesn't, or None. If
failing_expression is True then the expression which did not simplify
to a 0 will be returned instead of None.
Explanation
===========
If ``self`` is a Number (or complex number) that is not zero, then
the result is False.
If ``self`` is a number and has not evaluated to zero, evalf will be
used to test whether the expression evaluates to zero. If it does so
and the result has significance (i.e. the precision is either -1, for
a Rational result, or is greater than 1) then the evalf value will be
used to return True or False.
"""
from sympy.simplify.simplify import nsimplify, simplify
from sympy.solvers.solvers import solve
from sympy.polys.polyerrors import NotAlgebraic
from sympy.polys.numberfields import minimal_polynomial
other = sympify(other)
if self == other:
return True
# they aren't the same so see if we can make the difference 0;
# don't worry about doing simplification steps one at a time
# because if the expression ever goes to 0 then the subsequent
# simplification steps that are done will be very fast.
diff = factor_terms(simplify(self - other), radical=True)
if not diff:
return True
if not diff.has(Add, Mod):
# if there is no expanding to be done after simplifying
# then this can't be a zero
return False
constant = diff.is_constant(simplify=False, failing_number=True)
if constant is False:
return False
if not diff.is_number:
if constant is None:
# e.g. unless the right simplification is done, a symbolic
# zero is possible (see expression of issue 6829: without
# simplification constant will be None).
return
if constant is True:
# this gives a number whether there are free symbols or not
ndiff = diff._random()
# is_comparable will work whether the result is real
# or complex; it could be None, however.
if ndiff and ndiff.is_comparable:
return False
# sometimes we can use a simplified result to give a clue as to
# what the expression should be; if the expression is *not* zero
# then we should have been able to compute that and so now
# we can just consider the cases where the approximation appears
# to be zero -- we try to prove it via minimal_polynomial.
#
# removed
# ns = nsimplify(diff)
# if diff.is_number and (not ns or ns == diff):
#
# The thought was that if it nsimplifies to 0 that's a sure sign
# to try the following to prove it; or if it changed but wasn't
# zero that might be a sign that it's not going to be easy to
# prove. But tests seem to be working without that logic.
#
if diff.is_number:
# try to prove via self-consistency
surds = [s for s in diff.atoms(Pow) if s.args[0].is_Integer]
# it seems to work better to try big ones first
surds.sort(key=lambda x: -x.args[0])
for s in surds:
try:
# simplify is False here -- this expression has already
# been identified as being hard to identify as zero;
# we will handle the checking ourselves using nsimplify
# to see if we are in the right ballpark or not and if so
# *then* the simplification will be attempted.
sol = solve(diff, s, simplify=False)
if sol:
if s in sol:
# the self-consistent result is present
return True
if all(si.is_Integer for si in sol):
# perfect powers are removed at instantiation
# so surd s cannot be an integer
return False
if all(i.is_algebraic is False for i in sol):
# a surd is algebraic
return False
if any(si in surds for si in sol):
# it wasn't equal to s but it is in surds
# and different surds are not equal
return False
if any(nsimplify(s - si) == 0 and
simplify(s - si) == 0 for si in sol):
return True
if s.is_real:
if any(nsimplify(si, [s]) == s and simplify(si) == s
for si in sol):
return True
except NotImplementedError:
pass
# try to prove with minimal_polynomial but know when
# *not* to use this or else it can take a long time. e.g. issue 8354
if True: # change True to condition that assures non-hang
try:
mp = minimal_polynomial(diff)
if mp.is_Symbol:
return True
return False
except (NotAlgebraic, NotImplementedError):
pass
# diff has not simplified to zero; constant is either None, True
# or the number with significance (is_comparable) that was randomly
# calculated twice as the same value.
if constant not in (True, None) and constant != 0:
return False
if failing_expression:
return diff
return None
def _eval_is_positive(self):
finite = self.is_finite
if finite is False:
return False
extended_positive = self.is_extended_positive
if finite is True:
return extended_positive
if extended_positive is False:
return False
def _eval_is_negative(self):
finite = self.is_finite
if finite is False:
return False
extended_negative = self.is_extended_negative
if finite is True:
return extended_negative
if extended_negative is False:
return False
def _eval_is_extended_positive_negative(self, positive):
from sympy.core.numbers import pure_complex
from sympy.polys.numberfields import minimal_polynomial
from sympy.polys.polyerrors import NotAlgebraic
if self.is_number:
if self.is_extended_real is False:
return False
# check to see that we can get a value
try:
n2 = self._eval_evalf(2)
# XXX: This shouldn't be caught here
# Catches ValueError: hypsum() failed to converge to the requested
# 34 bits of accuracy
except ValueError:
return None
if n2 is None:
return None
if getattr(n2, '_prec', 1) == 1: # no significance
return None
if n2 is S.NaN:
return None
f = self.evalf(2)
if f.is_Float:
match = f, S.Zero
else:
match = pure_complex(f)
if match is None:
return False
r, i = match
if not (i.is_Number and r.is_Number):
return False
if r._prec != 1 and i._prec != 1:
return bool(not i and ((r > 0) if positive else (r < 0)))
elif r._prec == 1 and (not i or i._prec == 1) and \
self.is_algebraic and not self.has(Function):
try:
if minimal_polynomial(self).is_Symbol:
return False
except (NotAlgebraic, NotImplementedError):
pass
def _eval_is_extended_positive(self):
return self._eval_is_extended_positive_negative(positive=True)
def _eval_is_extended_negative(self):
return self._eval_is_extended_positive_negative(positive=False)
def _eval_interval(self, x, a, b):
"""
Returns evaluation over an interval. For most functions this is:
self.subs(x, b) - self.subs(x, a),
possibly using limit() if NaN is returned from subs, or if
singularities are found between a and b.
If b or a is None, it only evaluates -self.subs(x, a) or self.subs(b, x),
respectively.
"""
from sympy.series import limit, Limit
from sympy.solvers.solveset import solveset
from sympy.sets.sets import Interval
from sympy.functions.elementary.exponential import log
from sympy.calculus.util import AccumBounds
if (a is None and b is None):
raise ValueError('Both interval ends cannot be None.')
def _eval_endpoint(left):
c = a if left else b
if c is None:
return 0
else:
C = self.subs(x, c)
if C.has(S.NaN, S.Infinity, S.NegativeInfinity,
S.ComplexInfinity, AccumBounds):
if (a < b) != False:
C = limit(self, x, c, "+" if left else "-")
else:
C = limit(self, x, c, "-" if left else "+")
if isinstance(C, Limit):
raise NotImplementedError("Could not compute limit")
return C
if a == b:
return 0
A = _eval_endpoint(left=True)
if A is S.NaN:
return A
B = _eval_endpoint(left=False)
if (a and b) is None:
return B - A
value = B - A
if a.is_comparable and b.is_comparable:
if a < b:
domain = Interval(a, b)
else:
domain = Interval(b, a)
# check the singularities of self within the interval
# if singularities is a ConditionSet (not iterable), catch the exception and pass
singularities = solveset(self.cancel().as_numer_denom()[1], x,
domain=domain)
for logterm in self.atoms(log):
singularities = singularities | solveset(logterm.args[0], x,
domain=domain)
try:
for s in singularities:
if value is S.NaN:
# no need to keep adding, it will stay NaN
break
if not s.is_comparable:
continue
if (a < s) == (s < b) == True:
value += -limit(self, x, s, "+") + limit(self, x, s, "-")
elif (b < s) == (s < a) == True:
value += limit(self, x, s, "+") - limit(self, x, s, "-")
except TypeError:
pass
return value
def _eval_power(self, other):
# subclass to compute self**other for cases when