1"""
2Handlers for predicates related to set membership: integer, rational, etc.
3"""
4
5from sympy.assumptions import Q, ask
6from sympy.core import Add, Basic, Expr, Mul, Pow
7from sympy.core.numbers import (AlgebraicNumber, ComplexInfinity, Exp1, Float,
8    GoldenRatio, ImaginaryUnit, Infinity, Integer, NaN, NegativeInfinity,
9    Number, NumberSymbol, Pi, pi, Rational, TribonacciConstant, E)
10from sympy.core.logic import fuzzy_bool
11from sympy.functions import (Abs, acos, acot, asin, atan, cos, cot, exp, im,
12    log, re, sin, tan)
13from sympy import I, Eq, conjugate
14from sympy.matrices import Determinant, MatrixBase, Trace
15from sympy.matrices.expressions.matexpr import MatrixElement
16
17from sympy.multipledispatch import MDNotImplementedError
18
19from .common import test_closed_group
20from ..predicates.sets import (IntegerPredicate, RationalPredicate,
21    IrrationalPredicate, RealPredicate, ExtendedRealPredicate,
22    HermitianPredicate, ComplexPredicate, ImaginaryPredicate,
23    AntihermitianPredicate, AlgebraicPredicate)
24
25
26# IntegerPredicate
27
28def _IntegerPredicate_number(expr, assumptions):
29    # helper function
30        try:
31            i = int(expr.round())
32            if not (expr - i).equals(0):
33                raise TypeError
34            return True
35        except TypeError:
36            return False
37
38@IntegerPredicate.register_many(int, Integer)
39def _(expr, assumptions):
40    return True
41
42@IntegerPredicate.register_many(Exp1, GoldenRatio, ImaginaryUnit, Infinity,
43    NegativeInfinity, Pi, Rational, TribonacciConstant)
44def _(expr, assumptions):
45    return False
46
47@IntegerPredicate.register(Expr)
48def _(expr, assumptions):
49    ret = expr.is_integer
50    if ret is None:
51        raise MDNotImplementedError
52    return ret
53
54@IntegerPredicate.register_many(Add, Pow)
55def _(expr, assumptions):
56    """
57    * Integer + Integer       -> Integer
58    * Integer + !Integer      -> !Integer
59    * !Integer + !Integer -> ?
60    """
61    if expr.is_number:
62        return _IntegerPredicate_number(expr, assumptions)
63    return test_closed_group(expr, assumptions, Q.integer)
64
65@IntegerPredicate.register(Mul)
66def _(expr, assumptions):
67    """
68    * Integer*Integer      -> Integer
69    * Integer*Irrational   -> !Integer
70    * Odd/Even             -> !Integer
71    * Integer*Rational     -> ?
72    """
73    if expr.is_number:
74        return _IntegerPredicate_number(expr, assumptions)
75    _output = True
76    for arg in expr.args:
77        if not ask(Q.integer(arg), assumptions):
78            if arg.is_Rational:
79                if arg.q == 2:
80                    return ask(Q.even(2*expr), assumptions)
81                if ~(arg.q & 1):
82                    return None
83            elif ask(Q.irrational(arg), assumptions):
84                if _output:
85                    _output = False
86                else:
87                    return
88            else:
89                return
90
91    return _output
92
93@IntegerPredicate.register(Abs)
94def _(expr, assumptions):
95    return ask(Q.integer(expr.args[0]), assumptions)
96
97@IntegerPredicate.register_many(Determinant, MatrixElement, Trace)
98def _(expr, assumptions):
99    return ask(Q.integer_elements(expr.args[0]), assumptions)
100
101
102# RationalPredicate
103
104@RationalPredicate.register(Rational)
105def _(expr, assumptions):
106    return True
107
108@RationalPredicate.register(Float)
109def _(expr, assumptions):
110    return None
111
112@RationalPredicate.register_many(Exp1, GoldenRatio, ImaginaryUnit, Infinity,
113    NegativeInfinity, Pi, TribonacciConstant)
114def _(expr, assumptions):
115    return False
116
117@RationalPredicate.register(Expr)
118def _(expr, assumptions):
119    ret = expr.is_rational
120    if ret is None:
121        raise MDNotImplementedError
122    return ret
123
124@RationalPredicate.register_many(Add, Mul)
125def _(expr, assumptions):
126    """
127    * Rational + Rational     -> Rational
128    * Rational + !Rational    -> !Rational
129    * !Rational + !Rational   -> ?
130    """
131    if expr.is_number:
132        if expr.as_real_imag()[1]:
133            return False
134    return test_closed_group(expr, assumptions, Q.rational)
135
136@RationalPredicate.register(Pow)
137def _(expr, assumptions):
138    """
139    * Rational ** Integer      -> Rational
140    * Irrational ** Rational   -> Irrational
141    * Rational ** Irrational   -> ?
142    """
143    if expr.base == E:
144        x = expr.exp
145        if ask(Q.rational(x), assumptions):
146            return ask(~Q.nonzero(x), assumptions)
147        return
148
149    if ask(Q.integer(expr.exp), assumptions):
150        return ask(Q.rational(expr.base), assumptions)
151    elif ask(Q.rational(expr.exp), assumptions):
152        if ask(Q.prime(expr.base), assumptions):
153            return False
154
155@RationalPredicate.register_many(asin, atan, cos, sin, tan)
156def _(expr, assumptions):
157    x = expr.args[0]
158    if ask(Q.rational(x), assumptions):
159        return ask(~Q.nonzero(x), assumptions)
160
161@RationalPredicate.register(exp)
162def _(expr, assumptions):
163    x = expr.exp
164    if ask(Q.rational(x), assumptions):
165        return ask(~Q.nonzero(x), assumptions)
166
167@RationalPredicate.register_many(acot, cot)
168def _(expr, assumptions):
169    x = expr.args[0]
170    if ask(Q.rational(x), assumptions):
171        return False
172
173@RationalPredicate.register_many(acos, log)
174def _(expr, assumptions):
175    x = expr.args[0]
176    if ask(Q.rational(x), assumptions):
177        return ask(~Q.nonzero(x - 1), assumptions)
178
179
180# IrrationalPredicate
181
182@IrrationalPredicate.register(Expr)
183def _(expr, assumptions):
184    ret = expr.is_irrational
185    if ret is None:
186        raise MDNotImplementedError
187    return ret
188
189@IrrationalPredicate.register(Basic)
190def _(expr, assumptions):
191    _real = ask(Q.real(expr), assumptions)
192    if _real:
193        _rational = ask(Q.rational(expr), assumptions)
194        if _rational is None:
195            return None
196        return not _rational
197    else:
198        return _real
199
200
201# RealPredicate
202
203def _RealPredicate_number(expr, assumptions):
204    # let as_real_imag() work first since the expression may
205    # be simpler to evaluate
206    i = expr.as_real_imag()[1].evalf(2)
207    if i._prec != 1:
208        return not i
209    # allow None to be returned if we couldn't show for sure
210    # that i was 0
211
212@RealPredicate.register_many(Abs, Exp1, Float, GoldenRatio, im, Pi, Rational,
213    re, TribonacciConstant)
214def _(expr, assumptions):
215    return True
216
217@RealPredicate.register_many(ImaginaryUnit, Infinity, NegativeInfinity)
218def _(expr, assumptions):
219    return False
220
221@RealPredicate.register(Expr)
222def _(expr, assumptions):
223    ret = expr.is_real
224    if ret is None:
225        raise MDNotImplementedError
226    return ret
227
228@RealPredicate.register(Add)
229def _(expr, assumptions):
230    """
231    * Real + Real              -> Real
232    * Real + (Complex & !Real) -> !Real
233    """
234    if expr.is_number:
235        return _RealPredicate_number(expr, assumptions)
236    return test_closed_group(expr, assumptions, Q.real)
237
238@RealPredicate.register(Mul)
239def _(expr, assumptions):
240    """
241    * Real*Real               -> Real
242    * Real*Imaginary          -> !Real
243    * Imaginary*Imaginary     -> Real
244    """
245    if expr.is_number:
246        return _RealPredicate_number(expr, assumptions)
247    result = True
248    for arg in expr.args:
249        if ask(Q.real(arg), assumptions):
250            pass
251        elif ask(Q.imaginary(arg), assumptions):
252            result = result ^ True
253        else:
254            break
255    else:
256        return result
257
258@RealPredicate.register(Pow)
259def _(expr, assumptions):
260    """
261    * Real**Integer              -> Real
262    * Positive**Real             -> Real
263    * Real**(Integer/Even)       -> Real if base is nonnegative
264    * Real**(Integer/Odd)        -> Real
265    * Imaginary**(Integer/Even)  -> Real
266    * Imaginary**(Integer/Odd)   -> not Real
267    * Imaginary**Real            -> ? since Real could be 0 (giving real)
268                                    or 1 (giving imaginary)
269    * b**Imaginary               -> Real if log(b) is imaginary and b != 0
270                                    and exponent != integer multiple of
271                                    I*pi/log(b)
272    * Real**Real                 -> ? e.g. sqrt(-1) is imaginary and
273                                    sqrt(2) is not
274    """
275    if expr.is_number:
276        return _RealPredicate_number(expr, assumptions)
277
278    if expr.base == E:
279        return ask(
280            Q.integer(expr.exp/I/pi) | Q.real(expr.exp), assumptions
281        )
282
283    if expr.base.func == exp or (expr.base.is_Pow and expr.base.base == E):
284        if ask(Q.imaginary(expr.base.exp), assumptions):
285            if ask(Q.imaginary(expr.exp), assumptions):
286                return True
287        # If the i = (exp's arg)/(I*pi) is an integer or half-integer
288        # multiple of I*pi then 2*i will be an integer. In addition,
289        # exp(i*I*pi) = (-1)**i so the overall realness of the expr
290        # can be determined by replacing exp(i*I*pi) with (-1)**i.
291        i = expr.base.exp/I/pi
292        if ask(Q.integer(2*i), assumptions):
293            return ask(Q.real(((-1)**i)**expr.exp), assumptions)
294        return
295
296    if ask(Q.imaginary(expr.base), assumptions):
297        if ask(Q.integer(expr.exp), assumptions):
298            odd = ask(Q.odd(expr.exp), assumptions)
299            if odd is not None:
300                return not odd
301            return
302
303    if ask(Q.imaginary(expr.exp), assumptions):
304        imlog = ask(Q.imaginary(log(expr.base)), assumptions)
305        if imlog is not None:
306            # I**i -> real, log(I) is imag;
307            # (2*I)**i -> complex, log(2*I) is not imag
308            return imlog
309
310    if ask(Q.real(expr.base), assumptions):
311        if ask(Q.real(expr.exp), assumptions):
312            if expr.exp.is_Rational and \
313                    ask(Q.even(expr.exp.q), assumptions):
314                return ask(Q.positive(expr.base), assumptions)
315            elif ask(Q.integer(expr.exp), assumptions):
316                return True
317            elif ask(Q.positive(expr.base), assumptions):
318                return True
319            elif ask(Q.negative(expr.base), assumptions):
320                return False
321
322@RealPredicate.register_many(cos, sin)
323def _(expr, assumptions):
324    if ask(Q.real(expr.args[0]), assumptions):
325            return True
326
327@RealPredicate.register(exp)
328def _(expr, assumptions):
329    return ask(
330        Q.integer(expr.exp/I/pi) | Q.real(expr.exp), assumptions
331    )
332
333@RealPredicate.register(log)
334def _(expr, assumptions):
335    return ask(Q.positive(expr.args[0]), assumptions)
336
337@RealPredicate.register_many(Determinant, MatrixElement, Trace)
338def _(expr, assumptions):
339    return ask(Q.real_elements(expr.args[0]), assumptions)
340
341
342# ExtendedRealPredicate
343
344@ExtendedRealPredicate.register(object)
345def _(expr, assumptions):
346    return ask(Q.negative_infinite(expr)
347               | Q.negative(expr)
348               | Q.zero(expr)
349               | Q.positive(expr)
350               | Q.positive_infinite(expr),
351            assumptions)
352
353@ExtendedRealPredicate.register_many(Infinity, NegativeInfinity)
354def _(expr, assumptions):
355    return True
356
357@ExtendedRealPredicate.register_many(Add, Mul, Pow)
358def _(expr, assumptions):
359    return test_closed_group(expr, assumptions, Q.extended_real)
360
361
362# HermitianPredicate
363
364@HermitianPredicate.register(object)
365def _(expr, assumptions):
366    if isinstance(expr, MatrixBase):
367        return None
368    return ask(Q.real(expr), assumptions)
369
370@HermitianPredicate.register(Add)
371def _(expr, assumptions):
372    """
373    * Hermitian + Hermitian  -> Hermitian
374    * Hermitian + !Hermitian -> !Hermitian
375    """
376    if expr.is_number:
377        raise MDNotImplementedError
378    return test_closed_group(expr, assumptions, Q.hermitian)
379
380@HermitianPredicate.register(Mul)
381def _(expr, assumptions):
382    """
383    As long as there is at most only one noncommutative term:
384
385    * Hermitian*Hermitian         -> Hermitian
386    * Hermitian*Antihermitian     -> !Hermitian
387    * Antihermitian*Antihermitian -> Hermitian
388    """
389    if expr.is_number:
390        raise MDNotImplementedError
391    nccount = 0
392    result = True
393    for arg in expr.args:
394        if ask(Q.antihermitian(arg), assumptions):
395            result = result ^ True
396        elif not ask(Q.hermitian(arg), assumptions):
397            break
398        if ask(~Q.commutative(arg), assumptions):
399            nccount += 1
400            if nccount > 1:
401                break
402    else:
403        return result
404
405@HermitianPredicate.register(Pow)
406def _(expr, assumptions):
407    """
408    * Hermitian**Integer -> Hermitian
409    """
410    if expr.is_number:
411        raise MDNotImplementedError
412    if expr.base == E:
413        if ask(Q.hermitian(expr.exp), assumptions):
414            return True
415        raise MDNotImplementedError
416    if ask(Q.hermitian(expr.base), assumptions):
417        if ask(Q.integer(expr.exp), assumptions):
418            return True
419    raise MDNotImplementedError
420
421@HermitianPredicate.register_many(cos, sin)
422def _(expr, assumptions):
423    if ask(Q.hermitian(expr.args[0]), assumptions):
424        return True
425    raise MDNotImplementedError
426
427@HermitianPredicate.register(exp)
428def _(expr, assumptions):
429    if ask(Q.hermitian(expr.exp), assumptions):
430        return True
431    raise MDNotImplementedError
432
433@HermitianPredicate.register(MatrixBase)
434def _(mat, assumptions):
435    rows, cols = mat.shape
436    ret_val = True
437    for i in range(rows):
438        for j in range(i, cols):
439            cond = fuzzy_bool(Eq(mat[i, j], conjugate(mat[j, i])))
440            if cond is None:
441                ret_val = None
442            if cond == False:
443                return False
444    if ret_val is None:
445        raise MDNotImplementedError
446    return ret_val
447
448
449# ComplexPredicate
450
451@ComplexPredicate.register_many(Abs, cos, exp, im, ImaginaryUnit, log, Number,
452    NumberSymbol, re, sin)
453def _(expr, assumptions):
454    return True
455
456@ComplexPredicate.register_many(Infinity, NegativeInfinity)
457def _(expr, assumptions):
458    return False
459
460@ComplexPredicate.register(Expr)
461def _(expr, assumptions):
462    ret = expr.is_complex
463    if ret is None:
464        raise MDNotImplementedError
465    return ret
466
467@ComplexPredicate.register_many(Add, Mul)
468def _(expr, assumptions):
469    return test_closed_group(expr, assumptions, Q.complex)
470
471@ComplexPredicate.register(Pow)
472def _(expr, assumptions):
473    if expr.base == E:
474        return True
475    return test_closed_group(expr, assumptions, Q.complex)
476
477@ComplexPredicate.register_many(Determinant, MatrixElement, Trace)
478def _(expr, assumptions):
479    return ask(Q.complex_elements(expr.args[0]), assumptions)
480
481@ComplexPredicate.register(NaN)
482def _(expr, assumptions):
483    return None
484
485
486# ImaginaryPredicate
487
488def _Imaginary_number(expr, assumptions):
489    # let as_real_imag() work first since the expression may
490    # be simpler to evaluate
491    r = expr.as_real_imag()[0].evalf(2)
492    if r._prec != 1:
493        return not r
494    # allow None to be returned if we couldn't show for sure
495    # that r was 0
496
497@ImaginaryPredicate.register(ImaginaryUnit)
498def _(expr, assumptions):
499    return True
500
501@ImaginaryPredicate.register(Expr)
502def _(expr, assumptions):
503    ret = expr.is_imaginary
504    if ret is None:
505        raise MDNotImplementedError
506    return ret
507
508@ImaginaryPredicate.register(Add)
509def _(expr, assumptions):
510    """
511    * Imaginary + Imaginary -> Imaginary
512    * Imaginary + Complex   -> ?
513    * Imaginary + Real      -> !Imaginary
514    """
515    if expr.is_number:
516        return _Imaginary_number(expr, assumptions)
517
518    reals = 0
519    for arg in expr.args:
520        if ask(Q.imaginary(arg), assumptions):
521            pass
522        elif ask(Q.real(arg), assumptions):
523            reals += 1
524        else:
525            break
526    else:
527        if reals == 0:
528            return True
529        if reals == 1 or (len(expr.args) == reals):
530            # two reals could sum 0 thus giving an imaginary
531            return False
532
533@ImaginaryPredicate.register(Mul)
534def _(expr, assumptions):
535    """
536    * Real*Imaginary      -> Imaginary
537    * Imaginary*Imaginary -> Real
538    """
539    if expr.is_number:
540        return _Imaginary_number(expr, assumptions)
541    result = False
542    reals = 0
543    for arg in expr.args:
544        if ask(Q.imaginary(arg), assumptions):
545            result = result ^ True
546        elif not ask(Q.real(arg), assumptions):
547            break
548    else:
549        if reals == len(expr.args):
550            return False
551        return result
552
553@ImaginaryPredicate.register(Pow)
554def _(expr, assumptions):
555    """
556    * Imaginary**Odd        -> Imaginary
557    * Imaginary**Even       -> Real
558    * b**Imaginary          -> !Imaginary if exponent is an integer
559                               multiple of I*pi/log(b)
560    * Imaginary**Real       -> ?
561    * Positive**Real        -> Real
562    * Negative**Integer     -> Real
563    * Negative**(Integer/2) -> Imaginary
564    * Negative**Real        -> not Imaginary if exponent is not Rational
565    """
566    if expr.is_number:
567        return _Imaginary_number(expr, assumptions)
568
569    if expr.base == E:
570        a = expr.exp/I/pi
571        return ask(Q.integer(2*a) & ~Q.integer(a), assumptions)
572
573    if expr.base.func == exp or (expr.base.is_Pow and expr.base.base == E):
574        if ask(Q.imaginary(expr.base.exp), assumptions):
575            if ask(Q.imaginary(expr.exp), assumptions):
576                return False
577            i = expr.base.exp/I/pi
578            if ask(Q.integer(2*i), assumptions):
579                return ask(Q.imaginary(((-1)**i)**expr.exp), assumptions)
580
581    if ask(Q.imaginary(expr.base), assumptions):
582        if ask(Q.integer(expr.exp), assumptions):
583            odd = ask(Q.odd(expr.exp), assumptions)
584            if odd is not None:
585                return odd
586            return
587
588    if ask(Q.imaginary(expr.exp), assumptions):
589        imlog = ask(Q.imaginary(log(expr.base)), assumptions)
590        if imlog is not None:
591            # I**i -> real; (2*I)**i -> complex ==> not imaginary
592            return False
593
594    if ask(Q.real(expr.base) & Q.real(expr.exp), assumptions):
595        if ask(Q.positive(expr.base), assumptions):
596            return False
597        else:
598            rat = ask(Q.rational(expr.exp), assumptions)
599            if not rat:
600                return rat
601            if ask(Q.integer(expr.exp), assumptions):
602                return False
603            else:
604                half = ask(Q.integer(2*expr.exp), assumptions)
605                if half:
606                    return ask(Q.negative(expr.base), assumptions)
607                return half
608
609@ImaginaryPredicate.register(log)
610def _(expr, assumptions):
611    if ask(Q.real(expr.args[0]), assumptions):
612        if ask(Q.positive(expr.args[0]), assumptions):
613            return False
614        return
615    # XXX it should be enough to do
616    # return ask(Q.nonpositive(expr.args[0]), assumptions)
617    # but ask(Q.nonpositive(exp(x)), Q.imaginary(x)) -> None;
618    # it should return True since exp(x) will be either 0 or complex
619    if expr.args[0].func == exp or (expr.args[0].is_Pow and expr.args[0].base == E):
620        if expr.args[0].exp in [I, -I]:
621            return True
622    im = ask(Q.imaginary(expr.args[0]), assumptions)
623    if im is False:
624        return False
625
626@ImaginaryPredicate.register(exp)
627def _(expr, assumptions):
628    a = expr.exp/I/pi
629    return ask(Q.integer(2*a) & ~Q.integer(a), assumptions)
630
631@ImaginaryPredicate.register_many(Number, NumberSymbol)
632def _(expr, assumptions):
633    return not (expr.as_real_imag()[1] == 0)
634
635@ImaginaryPredicate.register(NaN)
636def _(expr, assumptions):
637    return None
638
639
640# AntihermitianPredicate
641
642@AntihermitianPredicate.register(object)
643def _(expr, assumptions):
644    if isinstance(expr, MatrixBase):
645        return None
646    if ask(Q.zero(expr), assumptions):
647        return True
648    return ask(Q.imaginary(expr), assumptions)
649
650@AntihermitianPredicate.register(Add)
651def _(expr, assumptions):
652    """
653    * Antihermitian + Antihermitian  -> Antihermitian
654    * Antihermitian + !Antihermitian -> !Antihermitian
655    """
656    if expr.is_number:
657        raise MDNotImplementedError
658    return test_closed_group(expr, assumptions, Q.antihermitian)
659
660@AntihermitianPredicate.register(Mul)
661def _(expr, assumptions):
662    """
663    As long as there is at most only one noncommutative term:
664
665    * Hermitian*Hermitian         -> !Antihermitian
666    * Hermitian*Antihermitian     -> Antihermitian
667    * Antihermitian*Antihermitian -> !Antihermitian
668    """
669    if expr.is_number:
670        raise MDNotImplementedError
671    nccount = 0
672    result = False
673    for arg in expr.args:
674        if ask(Q.antihermitian(arg), assumptions):
675            result = result ^ True
676        elif not ask(Q.hermitian(arg), assumptions):
677            break
678        if ask(~Q.commutative(arg), assumptions):
679            nccount += 1
680            if nccount > 1:
681                break
682    else:
683        return result
684
685@AntihermitianPredicate.register(Pow)
686def _(expr, assumptions):
687    """
688    * Hermitian**Integer  -> !Antihermitian
689    * Antihermitian**Even -> !Antihermitian
690    * Antihermitian**Odd  -> Antihermitian
691    """
692    if expr.is_number:
693        raise MDNotImplementedError
694    if ask(Q.hermitian(expr.base), assumptions):
695        if ask(Q.integer(expr.exp), assumptions):
696            return False
697    elif ask(Q.antihermitian(expr.base), assumptions):
698        if ask(Q.even(expr.exp), assumptions):
699            return False
700        elif ask(Q.odd(expr.exp), assumptions):
701            return True
702    raise MDNotImplementedError
703
704@AntihermitianPredicate.register(MatrixBase)
705def _(mat, assumptions):
706    rows, cols = mat.shape
707    ret_val = True
708    for i in range(rows):
709        for j in range(i, cols):
710            cond = fuzzy_bool(Eq(mat[i, j], -conjugate(mat[j, i])))
711            if cond is None:
712                ret_val = None
713            if cond == False:
714                return False
715    if ret_val is None:
716        raise MDNotImplementedError
717    return ret_val
718
719
720# AlgebraicPredicate
721
722@AlgebraicPredicate.register_many(AlgebraicNumber, Float, GoldenRatio,
723    ImaginaryUnit, TribonacciConstant)
724def _(expr, assumptions):
725    return True
726
727@AlgebraicPredicate.register_many(ComplexInfinity, Exp1, Infinity,
728    NegativeInfinity, Pi)
729def _(expr, assumptions):
730    return False
731
732@AlgebraicPredicate.register_many(Add, Mul)
733def _(expr, assumptions):
734    return test_closed_group(expr, assumptions, Q.algebraic)
735
736@AlgebraicPredicate.register(Pow)
737def _(expr, assumptions):
738    if expr.base == E:
739        if ask(Q.algebraic(expr.exp), assumptions):
740            return ask(~Q.nonzero(expr.exp), assumptions)
741        return
742    return expr.exp.is_Rational and ask(Q.algebraic(expr.base), assumptions)
743
744@AlgebraicPredicate.register(Rational)
745def _(expr, assumptions):
746    return expr.q != 0
747
748@AlgebraicPredicate.register_many(asin, atan, cos, sin, tan)
749def _(expr, assumptions):
750    x = expr.args[0]
751    if ask(Q.algebraic(x), assumptions):
752        return ask(~Q.nonzero(x), assumptions)
753
754@AlgebraicPredicate.register(exp)
755def _(expr, assumptions):
756    x = expr.exp
757    if ask(Q.algebraic(x), assumptions):
758        return ask(~Q.nonzero(x), assumptions)
759
760@AlgebraicPredicate.register_many(acot, cot)
761def _(expr, assumptions):
762    x = expr.args[0]
763    if ask(Q.algebraic(x), assumptions):
764        return False
765
766@AlgebraicPredicate.register_many(acos, log)
767def _(expr, assumptions):
768    x = expr.args[0]
769    if ask(Q.algebraic(x), assumptions):
770        return ask(~Q.nonzero(x - 1), assumptions)
771