1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507 |
- from functools import reduce
- from itertools import product
- from sympy.core.basic import Basic
- from sympy.core.containers import Tuple
- from sympy.core.expr import Expr
- from sympy.core.function import Lambda
- from sympy.core.logic import fuzzy_not, fuzzy_or, fuzzy_and
- from sympy.core.mod import Mod
- from sympy.core.numbers import oo, igcd, Rational
- from sympy.core.relational import Eq, is_eq
- from sympy.core.singleton import Singleton, S
- from sympy.core.symbol import Dummy, symbols, Symbol
- from sympy.core.sympify import _sympify, sympify, _sympy_converter
- from sympy.logic.boolalg import And, Or
- from .sets import Set, Interval, Union, FiniteSet, ProductSet
- from sympy.utilities.misc import filldedent
- class Rationals(Set, metaclass=Singleton):
- """
- Represents the rational numbers. This set is also available as
- the singleton ``S.Rationals``.
- Examples
- ========
- >>> from sympy import S
- >>> S.Half in S.Rationals
- True
- >>> iterable = iter(S.Rationals)
- >>> [next(iterable) for i in range(12)]
- [0, 1, -1, 1/2, 2, -1/2, -2, 1/3, 3, -1/3, -3, 2/3]
- """
- is_iterable = True
- _inf = S.NegativeInfinity
- _sup = S.Infinity
- is_empty = False
- is_finite_set = False
- def _contains(self, other):
- if not isinstance(other, Expr):
- return False
- return other.is_rational
- def __iter__(self):
- yield S.Zero
- yield S.One
- yield S.NegativeOne
- d = 2
- while True:
- for n in range(d):
- if igcd(n, d) == 1:
- yield Rational(n, d)
- yield Rational(d, n)
- yield Rational(-n, d)
- yield Rational(-d, n)
- d += 1
- @property
- def _boundary(self):
- return S.Reals
- class Naturals(Set, metaclass=Singleton):
- """
- Represents the natural numbers (or counting numbers) which are all
- positive integers starting from 1. This set is also available as
- the singleton ``S.Naturals``.
- Examples
- ========
- >>> from sympy import S, Interval, pprint
- >>> 5 in S.Naturals
- True
- >>> iterable = iter(S.Naturals)
- >>> next(iterable)
- 1
- >>> next(iterable)
- 2
- >>> next(iterable)
- 3
- >>> pprint(S.Naturals.intersect(Interval(0, 10)))
- {1, 2, ..., 10}
- See Also
- ========
- Naturals0 : non-negative integers (i.e. includes 0, too)
- Integers : also includes negative integers
- """
- is_iterable = True
- _inf = S.One
- _sup = S.Infinity
- is_empty = False
- is_finite_set = False
- def _contains(self, other):
- if not isinstance(other, Expr):
- return False
- elif other.is_positive and other.is_integer:
- return True
- elif other.is_integer is False or other.is_positive is False:
- return False
- def _eval_is_subset(self, other):
- return Range(1, oo).is_subset(other)
- def _eval_is_superset(self, other):
- return Range(1, oo).is_superset(other)
- def __iter__(self):
- i = self._inf
- while True:
- yield i
- i = i + 1
- @property
- def _boundary(self):
- return self
- def as_relational(self, x):
- from sympy.functions.elementary.integers import floor
- return And(Eq(floor(x), x), x >= self.inf, x < oo)
- class Naturals0(Naturals):
- """Represents the whole numbers which are all the non-negative integers,
- inclusive of zero.
- See Also
- ========
- Naturals : positive integers; does not include 0
- Integers : also includes the negative integers
- """
- _inf = S.Zero
- def _contains(self, other):
- if not isinstance(other, Expr):
- return S.false
- elif other.is_integer and other.is_nonnegative:
- return S.true
- elif other.is_integer is False or other.is_nonnegative is False:
- return S.false
- def _eval_is_subset(self, other):
- return Range(oo).is_subset(other)
- def _eval_is_superset(self, other):
- return Range(oo).is_superset(other)
- class Integers(Set, metaclass=Singleton):
- """
- Represents all integers: positive, negative and zero. This set is also
- available as the singleton ``S.Integers``.
- Examples
- ========
- >>> from sympy import S, Interval, pprint
- >>> 5 in S.Naturals
- True
- >>> iterable = iter(S.Integers)
- >>> next(iterable)
- 0
- >>> next(iterable)
- 1
- >>> next(iterable)
- -1
- >>> next(iterable)
- 2
- >>> pprint(S.Integers.intersect(Interval(-4, 4)))
- {-4, -3, ..., 4}
- See Also
- ========
- Naturals0 : non-negative integers
- Integers : positive and negative integers and zero
- """
- is_iterable = True
- is_empty = False
- is_finite_set = False
- def _contains(self, other):
- if not isinstance(other, Expr):
- return S.false
- return other.is_integer
- def __iter__(self):
- yield S.Zero
- i = S.One
- while True:
- yield i
- yield -i
- i = i + 1
- @property
- def _inf(self):
- return S.NegativeInfinity
- @property
- def _sup(self):
- return S.Infinity
- @property
- def _boundary(self):
- return self
- def as_relational(self, x):
- from sympy.functions.elementary.integers import floor
- return And(Eq(floor(x), x), -oo < x, x < oo)
- def _eval_is_subset(self, other):
- return Range(-oo, oo).is_subset(other)
- def _eval_is_superset(self, other):
- return Range(-oo, oo).is_superset(other)
- class Reals(Interval, metaclass=Singleton):
- """
- Represents all real numbers
- from negative infinity to positive infinity,
- including all integer, rational and irrational numbers.
- This set is also available as the singleton ``S.Reals``.
- Examples
- ========
- >>> from sympy import S, Rational, pi, I
- >>> 5 in S.Reals
- True
- >>> Rational(-1, 2) in S.Reals
- True
- >>> pi in S.Reals
- True
- >>> 3*I in S.Reals
- False
- >>> S.Reals.contains(pi)
- True
- See Also
- ========
- ComplexRegion
- """
- @property
- def start(self):
- return S.NegativeInfinity
- @property
- def end(self):
- return S.Infinity
- @property
- def left_open(self):
- return True
- @property
- def right_open(self):
- return True
- def __eq__(self, other):
- return other == Interval(S.NegativeInfinity, S.Infinity)
- def __hash__(self):
- return hash(Interval(S.NegativeInfinity, S.Infinity))
- class ImageSet(Set):
- """
- Image of a set under a mathematical function. The transformation
- must be given as a Lambda function which has as many arguments
- as the elements of the set upon which it operates, e.g. 1 argument
- when acting on the set of integers or 2 arguments when acting on
- a complex region.
- This function is not normally called directly, but is called
- from ``imageset``.
- Examples
- ========
- >>> from sympy import Symbol, S, pi, Dummy, Lambda
- >>> from sympy import FiniteSet, ImageSet, Interval
- >>> x = Symbol('x')
- >>> N = S.Naturals
- >>> squares = ImageSet(Lambda(x, x**2), N) # {x**2 for x in N}
- >>> 4 in squares
- True
- >>> 5 in squares
- False
- >>> FiniteSet(0, 1, 2, 3, 4, 5, 6, 7, 9, 10).intersect(squares)
- {1, 4, 9}
- >>> square_iterable = iter(squares)
- >>> for i in range(4):
- ... next(square_iterable)
- 1
- 4
- 9
- 16
- If you want to get value for `x` = 2, 1/2 etc. (Please check whether the
- `x` value is in ``base_set`` or not before passing it as args)
- >>> squares.lamda(2)
- 4
- >>> squares.lamda(S(1)/2)
- 1/4
- >>> n = Dummy('n')
- >>> solutions = ImageSet(Lambda(n, n*pi), S.Integers) # solutions of sin(x) = 0
- >>> dom = Interval(-1, 1)
- >>> dom.intersect(solutions)
- {0}
- See Also
- ========
- sympy.sets.sets.imageset
- """
- def __new__(cls, flambda, *sets):
- if not isinstance(flambda, Lambda):
- raise ValueError('First argument must be a Lambda')
- signature = flambda.signature
- if len(signature) != len(sets):
- raise ValueError('Incompatible signature')
- sets = [_sympify(s) for s in sets]
- if not all(isinstance(s, Set) for s in sets):
- raise TypeError("Set arguments to ImageSet should of type Set")
- if not all(cls._check_sig(sg, st) for sg, st in zip(signature, sets)):
- raise ValueError("Signature %s does not match sets %s" % (signature, sets))
- if flambda is S.IdentityFunction and len(sets) == 1:
- return sets[0]
- if not set(flambda.variables) & flambda.expr.free_symbols:
- is_empty = fuzzy_or(s.is_empty for s in sets)
- if is_empty == True:
- return S.EmptySet
- elif is_empty == False:
- return FiniteSet(flambda.expr)
- return Basic.__new__(cls, flambda, *sets)
- lamda = property(lambda self: self.args[0])
- base_sets = property(lambda self: self.args[1:])
- @property
- def base_set(self):
- # XXX: Maybe deprecate this? It is poorly defined in handling
- # the multivariate case...
- sets = self.base_sets
- if len(sets) == 1:
- return sets[0]
- else:
- return ProductSet(*sets).flatten()
- @property
- def base_pset(self):
- return ProductSet(*self.base_sets)
- @classmethod
- def _check_sig(cls, sig_i, set_i):
- if sig_i.is_symbol:
- return True
- elif isinstance(set_i, ProductSet):
- sets = set_i.sets
- if len(sig_i) != len(sets):
- return False
- # Recurse through the signature for nested tuples:
- return all(cls._check_sig(ts, ps) for ts, ps in zip(sig_i, sets))
- else:
- # XXX: Need a better way of checking whether a set is a set of
- # Tuples or not. For example a FiniteSet can contain Tuples
- # but so can an ImageSet or a ConditionSet. Others like
- # Integers, Reals etc can not contain Tuples. We could just
- # list the possibilities here... Current code for e.g.
- # _contains probably only works for ProductSet.
- return True # Give the benefit of the doubt
- def __iter__(self):
- already_seen = set()
- for i in self.base_pset:
- val = self.lamda(*i)
- if val in already_seen:
- continue
- else:
- already_seen.add(val)
- yield val
- def _is_multivariate(self):
- return len(self.lamda.variables) > 1
- def _contains(self, other):
- from sympy.solvers.solveset import _solveset_multi
- def get_symsetmap(signature, base_sets):
- '''Attempt to get a map of symbols to base_sets'''
- queue = list(zip(signature, base_sets))
- symsetmap = {}
- for sig, base_set in queue:
- if sig.is_symbol:
- symsetmap[sig] = base_set
- elif base_set.is_ProductSet:
- sets = base_set.sets
- if len(sig) != len(sets):
- raise ValueError("Incompatible signature")
- # Recurse
- queue.extend(zip(sig, sets))
- else:
- # If we get here then we have something like sig = (x, y) and
- # base_set = {(1, 2), (3, 4)}. For now we give up.
- return None
- return symsetmap
- def get_equations(expr, candidate):
- '''Find the equations relating symbols in expr and candidate.'''
- queue = [(expr, candidate)]
- for e, c in queue:
- if not isinstance(e, Tuple):
- yield Eq(e, c)
- elif not isinstance(c, Tuple) or len(e) != len(c):
- yield False
- return
- else:
- queue.extend(zip(e, c))
- # Get the basic objects together:
- other = _sympify(other)
- expr = self.lamda.expr
- sig = self.lamda.signature
- variables = self.lamda.variables
- base_sets = self.base_sets
- # Use dummy symbols for ImageSet parameters so they don't match
- # anything in other
- rep = {v: Dummy(v.name) for v in variables}
- variables = [v.subs(rep) for v in variables]
- sig = sig.subs(rep)
- expr = expr.subs(rep)
- # Map the parts of other to those in the Lambda expr
- equations = []
- for eq in get_equations(expr, other):
- # Unsatisfiable equation?
- if eq is False:
- return False
- equations.append(eq)
- # Map the symbols in the signature to the corresponding domains
- symsetmap = get_symsetmap(sig, base_sets)
- if symsetmap is None:
- # Can't factor the base sets to a ProductSet
- return None
- # Which of the variables in the Lambda signature need to be solved for?
- symss = (eq.free_symbols for eq in equations)
- variables = set(variables) & reduce(set.union, symss, set())
- # Use internal multivariate solveset
- variables = tuple(variables)
- base_sets = [symsetmap[v] for v in variables]
- solnset = _solveset_multi(equations, variables, base_sets)
- if solnset is None:
- return None
- return fuzzy_not(solnset.is_empty)
- @property
- def is_iterable(self):
- return all(s.is_iterable for s in self.base_sets)
- def doit(self, **kwargs):
- from sympy.sets.setexpr import SetExpr
- f = self.lamda
- sig = f.signature
- if len(sig) == 1 and sig[0].is_symbol and isinstance(f.expr, Expr):
- base_set = self.base_sets[0]
- return SetExpr(base_set)._eval_func(f).set
- if all(s.is_FiniteSet for s in self.base_sets):
- return FiniteSet(*(f(*a) for a in product(*self.base_sets)))
- return self
- class Range(Set):
- """
- Represents a range of integers. Can be called as ``Range(stop)``,
- ``Range(start, stop)``, or ``Range(start, stop, step)``; when ``step`` is
- not given it defaults to 1.
- ``Range(stop)`` is the same as ``Range(0, stop, 1)`` and the stop value
- (just as for Python ranges) is not included in the Range values.
- >>> from sympy import Range
- >>> list(Range(3))
- [0, 1, 2]
- The step can also be negative:
- >>> list(Range(10, 0, -2))
- [10, 8, 6, 4, 2]
- The stop value is made canonical so equivalent ranges always
- have the same args:
- >>> Range(0, 10, 3)
- Range(0, 12, 3)
- Infinite ranges are allowed. ``oo`` and ``-oo`` are never included in the
- set (``Range`` is always a subset of ``Integers``). If the starting point
- is infinite, then the final value is ``stop - step``. To iterate such a
- range, it needs to be reversed:
- >>> from sympy import oo
- >>> r = Range(-oo, 1)
- >>> r[-1]
- 0
- >>> next(iter(r))
- Traceback (most recent call last):
- ...
- TypeError: Cannot iterate over Range with infinite start
- >>> next(iter(r.reversed))
- 0
- Although ``Range`` is a :class:`Set` (and supports the normal set
- operations) it maintains the order of the elements and can
- be used in contexts where ``range`` would be used.
- >>> from sympy import Interval
- >>> Range(0, 10, 2).intersect(Interval(3, 7))
- Range(4, 8, 2)
- >>> list(_)
- [4, 6]
- Although slicing of a Range will always return a Range -- possibly
- empty -- an empty set will be returned from any intersection that
- is empty:
- >>> Range(3)[:0]
- Range(0, 0, 1)
- >>> Range(3).intersect(Interval(4, oo))
- EmptySet
- >>> Range(3).intersect(Range(4, oo))
- EmptySet
- Range will accept symbolic arguments but has very limited support
- for doing anything other than displaying the Range:
- >>> from sympy import Symbol, pprint
- >>> from sympy.abc import i, j, k
- >>> Range(i, j, k).start
- i
- >>> Range(i, j, k).inf
- Traceback (most recent call last):
- ...
- ValueError: invalid method for symbolic range
- Better success will be had when using integer symbols:
- >>> n = Symbol('n', integer=True)
- >>> r = Range(n, n + 20, 3)
- >>> r.inf
- n
- >>> pprint(r)
- {n, n + 3, ..., n + 18}
- """
- def __new__(cls, *args):
- from sympy.functions.elementary.integers import ceiling
- if len(args) == 1:
- if isinstance(args[0], range):
- raise TypeError(
- 'use sympify(%s) to convert range to Range' % args[0])
- # expand range
- slc = slice(*args)
- if slc.step == 0:
- raise ValueError("step cannot be 0")
- start, stop, step = slc.start or 0, slc.stop, slc.step or 1
- try:
- ok = []
- for w in (start, stop, step):
- w = sympify(w)
- if w in [S.NegativeInfinity, S.Infinity] or (
- w.has(Symbol) and w.is_integer != False):
- ok.append(w)
- elif not w.is_Integer:
- if w.is_infinite:
- raise ValueError('infinite symbols not allowed')
- raise ValueError
- else:
- ok.append(w)
- except ValueError:
- raise ValueError(filldedent('''
- Finite arguments to Range must be integers; `imageset` can define
- other cases, e.g. use `imageset(i, i/10, Range(3))` to give
- [0, 1/10, 1/5].'''))
- start, stop, step = ok
- null = False
- if any(i.has(Symbol) for i in (start, stop, step)):
- dif = stop - start
- n = dif/step
- if n.is_Rational:
- from sympy.functions.elementary.integers import floor
- if dif == 0:
- null = True
- else: # (x, x + 5, 2) or (x, 3*x, x)
- n = floor(n)
- end = start + n*step
- if dif.is_Rational: # (x, x + 5, 2)
- if (end - stop).is_negative:
- end += step
- else: # (x, 3*x, x)
- if (end/stop - 1).is_negative:
- end += step
- elif n.is_extended_negative:
- null = True
- else:
- end = stop # other methods like sup and reversed must fail
- elif start.is_infinite:
- span = step*(stop - start)
- if span is S.NaN or span <= 0:
- null = True
- elif step.is_Integer and stop.is_infinite and abs(step) != 1:
- raise ValueError(filldedent('''
- Step size must be %s in this case.''' % (1 if step > 0 else -1)))
- else:
- end = stop
- else:
- oostep = step.is_infinite
- if oostep:
- step = S.One if step > 0 else S.NegativeOne
- n = ceiling((stop - start)/step)
- if n <= 0:
- null = True
- elif oostep:
- step = S.One # make it canonical
- end = start + step
- else:
- end = start + n*step
- if null:
- start = end = S.Zero
- step = S.One
- return Basic.__new__(cls, start, end, step)
- start = property(lambda self: self.args[0])
- stop = property(lambda self: self.args[1])
- step = property(lambda self: self.args[2])
- @property
- def reversed(self):
- """Return an equivalent Range in the opposite order.
- Examples
- ========
- >>> from sympy import Range
- >>> Range(10).reversed
- Range(9, -1, -1)
- """
- if self.has(Symbol):
- n = (self.stop - self.start)/self.step
- if not n.is_extended_positive or not all(
- i.is_integer or i.is_infinite for i in self.args):
- raise ValueError('invalid method for symbolic range')
- if self.start == self.stop:
- return self
- return self.func(
- self.stop - self.step, self.start - self.step, -self.step)
- def _contains(self, other):
- if self.start == self.stop:
- return S.false
- if other.is_infinite:
- return S.false
- if not other.is_integer:
- return other.is_integer
- if self.has(Symbol):
- n = (self.stop - self.start)/self.step
- if not n.is_extended_positive or not all(
- i.is_integer or i.is_infinite for i in self.args):
- return
- else:
- n = self.size
- if self.start.is_finite:
- ref = self.start
- elif self.stop.is_finite:
- ref = self.stop
- else: # both infinite; step is +/- 1 (enforced by __new__)
- return S.true
- if n == 1:
- return Eq(other, self[0])
- res = (ref - other) % self.step
- if res == S.Zero:
- if self.has(Symbol):
- d = Dummy('i')
- return self.as_relational(d).subs(d, other)
- return And(other >= self.inf, other <= self.sup)
- elif res.is_Integer: # off sequence
- return S.false
- else: # symbolic/unsimplified residue modulo step
- return None
- def __iter__(self):
- n = self.size # validate
- if not (n.has(S.Infinity) or n.has(S.NegativeInfinity) or n.is_Integer):
- raise TypeError("Cannot iterate over symbolic Range")
- if self.start in [S.NegativeInfinity, S.Infinity]:
- raise TypeError("Cannot iterate over Range with infinite start")
- elif self.start != self.stop:
- i = self.start
- if n.is_infinite:
- while True:
- yield i
- i += self.step
- else:
- for _ in range(n):
- yield i
- i += self.step
- @property
- def is_iterable(self):
- # Check that size can be determined, used by __iter__
- dif = self.stop - self.start
- n = dif/self.step
- if not (n.has(S.Infinity) or n.has(S.NegativeInfinity) or n.is_Integer):
- return False
- if self.start in [S.NegativeInfinity, S.Infinity]:
- return False
- if not (n.is_extended_nonnegative and all(i.is_integer for i in self.args)):
- return False
- return True
- def __len__(self):
- rv = self.size
- if rv is S.Infinity:
- raise ValueError('Use .size to get the length of an infinite Range')
- return int(rv)
- @property
- def size(self):
- if self.start == self.stop:
- return S.Zero
- dif = self.stop - self.start
- n = dif/self.step
- if n.is_infinite:
- return S.Infinity
- if n.is_extended_nonnegative and all(i.is_integer for i in self.args):
- from sympy.functions.elementary.integers import floor
- return abs(floor(n))
- raise ValueError('Invalid method for symbolic Range')
- @property
- def is_finite_set(self):
- if self.start.is_integer and self.stop.is_integer:
- return True
- return self.size.is_finite
- @property
- def is_empty(self):
- try:
- return self.size.is_zero
- except ValueError:
- return None
- def __bool__(self):
- # this only distinguishes between definite null range
- # and non-null/unknown null; getting True doesn't mean
- # that it actually is not null
- b = is_eq(self.start, self.stop)
- if b is None:
- raise ValueError('cannot tell if Range is null or not')
- return not bool(b)
- def __getitem__(self, i):
- from sympy.functions.elementary.integers import ceiling
- ooslice = "cannot slice from the end with an infinite value"
- zerostep = "slice step cannot be zero"
- infinite = "slicing not possible on range with infinite start"
- # if we had to take every other element in the following
- # oo, ..., 6, 4, 2, 0
- # we might get oo, ..., 4, 0 or oo, ..., 6, 2
- ambiguous = "cannot unambiguously re-stride from the end " + \
- "with an infinite value"
- if isinstance(i, slice):
- if self.size.is_finite: # validates, too
- if self.start == self.stop:
- return Range(0)
- start, stop, step = i.indices(self.size)
- n = ceiling((stop - start)/step)
- if n <= 0:
- return Range(0)
- canonical_stop = start + n*step
- end = canonical_stop - step
- ss = step*self.step
- return Range(self[start], self[end] + ss, ss)
- else: # infinite Range
- start = i.start
- stop = i.stop
- if i.step == 0:
- raise ValueError(zerostep)
- step = i.step or 1
- ss = step*self.step
- #---------------------
- # handle infinite Range
- # i.e. Range(-oo, oo) or Range(oo, -oo, -1)
- # --------------------
- if self.start.is_infinite and self.stop.is_infinite:
- raise ValueError(infinite)
- #---------------------
- # handle infinite on right
- # e.g. Range(0, oo) or Range(0, -oo, -1)
- # --------------------
- if self.stop.is_infinite:
- # start and stop are not interdependent --
- # they only depend on step --so we use the
- # equivalent reversed values
- return self.reversed[
- stop if stop is None else -stop + 1:
- start if start is None else -start:
- step].reversed
- #---------------------
- # handle infinite on the left
- # e.g. Range(oo, 0, -1) or Range(-oo, 0)
- # --------------------
- # consider combinations of
- # start/stop {== None, < 0, == 0, > 0} and
- # step {< 0, > 0}
- if start is None:
- if stop is None:
- if step < 0:
- return Range(self[-1], self.start, ss)
- elif step > 1:
- raise ValueError(ambiguous)
- else: # == 1
- return self
- elif stop < 0:
- if step < 0:
- return Range(self[-1], self[stop], ss)
- else: # > 0
- return Range(self.start, self[stop], ss)
- elif stop == 0:
- if step > 0:
- return Range(0)
- else: # < 0
- raise ValueError(ooslice)
- elif stop == 1:
- if step > 0:
- raise ValueError(ooslice) # infinite singleton
- else: # < 0
- raise ValueError(ooslice)
- else: # > 1
- raise ValueError(ooslice)
- elif start < 0:
- if stop is None:
- if step < 0:
- return Range(self[start], self.start, ss)
- else: # > 0
- return Range(self[start], self.stop, ss)
- elif stop < 0:
- return Range(self[start], self[stop], ss)
- elif stop == 0:
- if step < 0:
- raise ValueError(ooslice)
- else: # > 0
- return Range(0)
- elif stop > 0:
- raise ValueError(ooslice)
- elif start == 0:
- if stop is None:
- if step < 0:
- raise ValueError(ooslice) # infinite singleton
- elif step > 1:
- raise ValueError(ambiguous)
- else: # == 1
- return self
- elif stop < 0:
- if step > 1:
- raise ValueError(ambiguous)
- elif step == 1:
- return Range(self.start, self[stop], ss)
- else: # < 0
- return Range(0)
- else: # >= 0
- raise ValueError(ooslice)
- elif start > 0:
- raise ValueError(ooslice)
- else:
- if self.start == self.stop:
- raise IndexError('Range index out of range')
- if not (all(i.is_integer or i.is_infinite
- for i in self.args) and ((self.stop - self.start)/
- self.step).is_extended_positive):
- raise ValueError('Invalid method for symbolic Range')
- if i == 0:
- if self.start.is_infinite:
- raise ValueError(ooslice)
- return self.start
- if i == -1:
- if self.stop.is_infinite:
- raise ValueError(ooslice)
- return self.stop - self.step
- n = self.size # must be known for any other index
- rv = (self.stop if i < 0 else self.start) + i*self.step
- if rv.is_infinite:
- raise ValueError(ooslice)
- val = (rv - self.start)/self.step
- rel = fuzzy_or([val.is_infinite,
- fuzzy_and([val.is_nonnegative, (n-val).is_nonnegative])])
- if rel:
- return rv
- if rel is None:
- raise ValueError('Invalid method for symbolic Range')
- raise IndexError("Range index out of range")
- @property
- def _inf(self):
- if not self:
- return S.EmptySet.inf
- if self.has(Symbol):
- if all(i.is_integer or i.is_infinite for i in self.args):
- dif = self.stop - self.start
- if self.step.is_positive and dif.is_positive:
- return self.start
- elif self.step.is_negative and dif.is_negative:
- return self.stop - self.step
- raise ValueError('invalid method for symbolic range')
- if self.step > 0:
- return self.start
- else:
- return self.stop - self.step
- @property
- def _sup(self):
- if not self:
- return S.EmptySet.sup
- if self.has(Symbol):
- if all(i.is_integer or i.is_infinite for i in self.args):
- dif = self.stop - self.start
- if self.step.is_positive and dif.is_positive:
- return self.stop - self.step
- elif self.step.is_negative and dif.is_negative:
- return self.start
- raise ValueError('invalid method for symbolic range')
- if self.step > 0:
- return self.stop - self.step
- else:
- return self.start
- @property
- def _boundary(self):
- return self
- def as_relational(self, x):
- """Rewrite a Range in terms of equalities and logic operators. """
- if self.start.is_infinite:
- assert not self.stop.is_infinite # by instantiation
- a = self.reversed.start
- else:
- a = self.start
- step = self.step
- in_seq = Eq(Mod(x - a, step), 0)
- ints = And(Eq(Mod(a, 1), 0), Eq(Mod(step, 1), 0))
- n = (self.stop - self.start)/self.step
- if n == 0:
- return S.EmptySet.as_relational(x)
- if n == 1:
- return And(Eq(x, a), ints)
- try:
- a, b = self.inf, self.sup
- except ValueError:
- a = None
- if a is not None:
- range_cond = And(
- x > a if a.is_infinite else x >= a,
- x < b if b.is_infinite else x <= b)
- else:
- a, b = self.start, self.stop - self.step
- range_cond = Or(
- And(self.step >= 1, x > a if a.is_infinite else x >= a,
- x < b if b.is_infinite else x <= b),
- And(self.step <= -1, x < a if a.is_infinite else x <= a,
- x > b if b.is_infinite else x >= b))
- return And(in_seq, ints, range_cond)
- _sympy_converter[range] = lambda r: Range(r.start, r.stop, r.step)
- def normalize_theta_set(theta):
- r"""
- Normalize a Real Set `theta` in the interval `[0, 2\pi)`. It returns
- a normalized value of theta in the Set. For Interval, a maximum of
- one cycle $[0, 2\pi]$, is returned i.e. for theta equal to $[0, 10\pi]$,
- returned normalized value would be $[0, 2\pi)$. As of now intervals
- with end points as non-multiples of ``pi`` is not supported.
- Raises
- ======
- NotImplementedError
- The algorithms for Normalizing theta Set are not yet
- implemented.
- ValueError
- The input is not valid, i.e. the input is not a real set.
- RuntimeError
- It is a bug, please report to the github issue tracker.
- Examples
- ========
- >>> from sympy.sets.fancysets import normalize_theta_set
- >>> from sympy import Interval, FiniteSet, pi
- >>> normalize_theta_set(Interval(9*pi/2, 5*pi))
- Interval(pi/2, pi)
- >>> normalize_theta_set(Interval(-3*pi/2, pi/2))
- Interval.Ropen(0, 2*pi)
- >>> normalize_theta_set(Interval(-pi/2, pi/2))
- Union(Interval(0, pi/2), Interval.Ropen(3*pi/2, 2*pi))
- >>> normalize_theta_set(Interval(-4*pi, 3*pi))
- Interval.Ropen(0, 2*pi)
- >>> normalize_theta_set(Interval(-3*pi/2, -pi/2))
- Interval(pi/2, 3*pi/2)
- >>> normalize_theta_set(FiniteSet(0, pi, 3*pi))
- {0, pi}
- """
- from sympy.functions.elementary.trigonometric import _pi_coeff as coeff
- if theta.is_Interval:
- interval_len = theta.measure
- # one complete circle
- if interval_len >= 2*S.Pi:
- if interval_len == 2*S.Pi and theta.left_open and theta.right_open:
- k = coeff(theta.start)
- return Union(Interval(0, k*S.Pi, False, True),
- Interval(k*S.Pi, 2*S.Pi, True, True))
- return Interval(0, 2*S.Pi, False, True)
- k_start, k_end = coeff(theta.start), coeff(theta.end)
- if k_start is None or k_end is None:
- raise NotImplementedError("Normalizing theta without pi as coefficient is "
- "not yet implemented")
- new_start = k_start*S.Pi
- new_end = k_end*S.Pi
- if new_start > new_end:
- return Union(Interval(S.Zero, new_end, False, theta.right_open),
- Interval(new_start, 2*S.Pi, theta.left_open, True))
- else:
- return Interval(new_start, new_end, theta.left_open, theta.right_open)
- elif theta.is_FiniteSet:
- new_theta = []
- for element in theta:
- k = coeff(element)
- if k is None:
- raise NotImplementedError('Normalizing theta without pi as '
- 'coefficient, is not Implemented.')
- else:
- new_theta.append(k*S.Pi)
- return FiniteSet(*new_theta)
- elif theta.is_Union:
- return Union(*[normalize_theta_set(interval) for interval in theta.args])
- elif theta.is_subset(S.Reals):
- raise NotImplementedError("Normalizing theta when, it is of type %s is not "
- "implemented" % type(theta))
- else:
- raise ValueError(" %s is not a real set" % (theta))
- class ComplexRegion(Set):
- r"""
- Represents the Set of all Complex Numbers. It can represent a
- region of Complex Plane in both the standard forms Polar and
- Rectangular coordinates.
- * Polar Form
- Input is in the form of the ProductSet or Union of ProductSets
- of the intervals of ``r`` and ``theta``, and use the flag ``polar=True``.
- .. math:: Z = \{z \in \mathbb{C} \mid z = r\times (\cos(\theta) + I\sin(\theta)), r \in [\texttt{r}], \theta \in [\texttt{theta}]\}
- * Rectangular Form
- Input is in the form of the ProductSet or Union of ProductSets
- of interval of x and y, the real and imaginary parts of the Complex numbers in a plane.
- Default input type is in rectangular form.
- .. math:: Z = \{z \in \mathbb{C} \mid z = x + Iy, x \in [\operatorname{re}(z)], y \in [\operatorname{im}(z)]\}
- Examples
- ========
- >>> from sympy import ComplexRegion, Interval, S, I, Union
- >>> a = Interval(2, 3)
- >>> b = Interval(4, 6)
- >>> c1 = ComplexRegion(a*b) # Rectangular Form
- >>> c1
- CartesianComplexRegion(ProductSet(Interval(2, 3), Interval(4, 6)))
- * c1 represents the rectangular region in complex plane
- surrounded by the coordinates (2, 4), (3, 4), (3, 6) and
- (2, 6), of the four vertices.
- >>> c = Interval(1, 8)
- >>> c2 = ComplexRegion(Union(a*b, b*c))
- >>> c2
- CartesianComplexRegion(Union(ProductSet(Interval(2, 3), Interval(4, 6)), ProductSet(Interval(4, 6), Interval(1, 8))))
- * c2 represents the Union of two rectangular regions in complex
- plane. One of them surrounded by the coordinates of c1 and
- other surrounded by the coordinates (4, 1), (6, 1), (6, 8) and
- (4, 8).
- >>> 2.5 + 4.5*I in c1
- True
- >>> 2.5 + 6.5*I in c1
- False
- >>> r = Interval(0, 1)
- >>> theta = Interval(0, 2*S.Pi)
- >>> c2 = ComplexRegion(r*theta, polar=True) # Polar Form
- >>> c2 # unit Disk
- PolarComplexRegion(ProductSet(Interval(0, 1), Interval.Ropen(0, 2*pi)))
- * c2 represents the region in complex plane inside the
- Unit Disk centered at the origin.
- >>> 0.5 + 0.5*I in c2
- True
- >>> 1 + 2*I in c2
- False
- >>> unit_disk = ComplexRegion(Interval(0, 1)*Interval(0, 2*S.Pi), polar=True)
- >>> upper_half_unit_disk = ComplexRegion(Interval(0, 1)*Interval(0, S.Pi), polar=True)
- >>> intersection = unit_disk.intersect(upper_half_unit_disk)
- >>> intersection
- PolarComplexRegion(ProductSet(Interval(0, 1), Interval(0, pi)))
- >>> intersection == upper_half_unit_disk
- True
- See Also
- ========
- CartesianComplexRegion
- PolarComplexRegion
- Complexes
- """
- is_ComplexRegion = True
- def __new__(cls, sets, polar=False):
- if polar is False:
- return CartesianComplexRegion(sets)
- elif polar is True:
- return PolarComplexRegion(sets)
- else:
- raise ValueError("polar should be either True or False")
- @property
- def sets(self):
- """
- Return raw input sets to the self.
- Examples
- ========
- >>> from sympy import Interval, ComplexRegion, Union
- >>> a = Interval(2, 3)
- >>> b = Interval(4, 5)
- >>> c = Interval(1, 7)
- >>> C1 = ComplexRegion(a*b)
- >>> C1.sets
- ProductSet(Interval(2, 3), Interval(4, 5))
- >>> C2 = ComplexRegion(Union(a*b, b*c))
- >>> C2.sets
- Union(ProductSet(Interval(2, 3), Interval(4, 5)), ProductSet(Interval(4, 5), Interval(1, 7)))
- """
- return self.args[0]
- @property
- def psets(self):
- """
- Return a tuple of sets (ProductSets) input of the self.
- Examples
- ========
- >>> from sympy import Interval, ComplexRegion, Union
- >>> a = Interval(2, 3)
- >>> b = Interval(4, 5)
- >>> c = Interval(1, 7)
- >>> C1 = ComplexRegion(a*b)
- >>> C1.psets
- (ProductSet(Interval(2, 3), Interval(4, 5)),)
- >>> C2 = ComplexRegion(Union(a*b, b*c))
- >>> C2.psets
- (ProductSet(Interval(2, 3), Interval(4, 5)), ProductSet(Interval(4, 5), Interval(1, 7)))
- """
- if self.sets.is_ProductSet:
- psets = ()
- psets = psets + (self.sets, )
- else:
- psets = self.sets.args
- return psets
- @property
- def a_interval(self):
- """
- Return the union of intervals of `x` when, self is in
- rectangular form, or the union of intervals of `r` when
- self is in polar form.
- Examples
- ========
- >>> from sympy import Interval, ComplexRegion, Union
- >>> a = Interval(2, 3)
- >>> b = Interval(4, 5)
- >>> c = Interval(1, 7)
- >>> C1 = ComplexRegion(a*b)
- >>> C1.a_interval
- Interval(2, 3)
- >>> C2 = ComplexRegion(Union(a*b, b*c))
- >>> C2.a_interval
- Union(Interval(2, 3), Interval(4, 5))
- """
- a_interval = []
- for element in self.psets:
- a_interval.append(element.args[0])
- a_interval = Union(*a_interval)
- return a_interval
- @property
- def b_interval(self):
- """
- Return the union of intervals of `y` when, self is in
- rectangular form, or the union of intervals of `theta`
- when self is in polar form.
- Examples
- ========
- >>> from sympy import Interval, ComplexRegion, Union
- >>> a = Interval(2, 3)
- >>> b = Interval(4, 5)
- >>> c = Interval(1, 7)
- >>> C1 = ComplexRegion(a*b)
- >>> C1.b_interval
- Interval(4, 5)
- >>> C2 = ComplexRegion(Union(a*b, b*c))
- >>> C2.b_interval
- Interval(1, 7)
- """
- b_interval = []
- for element in self.psets:
- b_interval.append(element.args[1])
- b_interval = Union(*b_interval)
- return b_interval
- @property
- def _measure(self):
- """
- The measure of self.sets.
- Examples
- ========
- >>> from sympy import Interval, ComplexRegion, S
- >>> a, b = Interval(2, 5), Interval(4, 8)
- >>> c = Interval(0, 2*S.Pi)
- >>> c1 = ComplexRegion(a*b)
- >>> c1.measure
- 12
- >>> c2 = ComplexRegion(a*c, polar=True)
- >>> c2.measure
- 6*pi
- """
- return self.sets._measure
- @classmethod
- def from_real(cls, sets):
- """
- Converts given subset of real numbers to a complex region.
- Examples
- ========
- >>> from sympy import Interval, ComplexRegion
- >>> unit = Interval(0,1)
- >>> ComplexRegion.from_real(unit)
- CartesianComplexRegion(ProductSet(Interval(0, 1), {0}))
- """
- if not sets.is_subset(S.Reals):
- raise ValueError("sets must be a subset of the real line")
- return CartesianComplexRegion(sets * FiniteSet(0))
- def _contains(self, other):
- from sympy.functions import arg, Abs
- other = sympify(other)
- isTuple = isinstance(other, Tuple)
- if isTuple and len(other) != 2:
- raise ValueError('expecting Tuple of length 2')
- # If the other is not an Expression, and neither a Tuple
- if not isinstance(other, (Expr, Tuple)):
- return S.false
- # self in rectangular form
- if not self.polar:
- re, im = other if isTuple else other.as_real_imag()
- return fuzzy_or(fuzzy_and([
- pset.args[0]._contains(re),
- pset.args[1]._contains(im)])
- for pset in self.psets)
- # self in polar form
- elif self.polar:
- if other.is_zero:
- # ignore undefined complex argument
- return fuzzy_or(pset.args[0]._contains(S.Zero)
- for pset in self.psets)
- if isTuple:
- r, theta = other
- else:
- r, theta = Abs(other), arg(other)
- if theta.is_real and theta.is_number:
- # angles in psets are normalized to [0, 2pi)
- theta %= 2*S.Pi
- return fuzzy_or(fuzzy_and([
- pset.args[0]._contains(r),
- pset.args[1]._contains(theta)])
- for pset in self.psets)
- class CartesianComplexRegion(ComplexRegion):
- r"""
- Set representing a square region of the complex plane.
- .. math:: Z = \{z \in \mathbb{C} \mid z = x + Iy, x \in [\operatorname{re}(z)], y \in [\operatorname{im}(z)]\}
- Examples
- ========
- >>> from sympy import ComplexRegion, I, Interval
- >>> region = ComplexRegion(Interval(1, 3) * Interval(4, 6))
- >>> 2 + 5*I in region
- True
- >>> 5*I in region
- False
- See also
- ========
- ComplexRegion
- PolarComplexRegion
- Complexes
- """
- polar = False
- variables = symbols('x, y', cls=Dummy)
- def __new__(cls, sets):
- if sets == S.Reals*S.Reals:
- return S.Complexes
- if all(_a.is_FiniteSet for _a in sets.args) and (len(sets.args) == 2):
- # ** ProductSet of FiniteSets in the Complex Plane. **
- # For Cases like ComplexRegion({2, 4}*{3}), It
- # would return {2 + 3*I, 4 + 3*I}
- # FIXME: This should probably be handled with something like:
- # return ImageSet(Lambda((x, y), x+I*y), sets).rewrite(FiniteSet)
- complex_num = []
- for x in sets.args[0]:
- for y in sets.args[1]:
- complex_num.append(x + S.ImaginaryUnit*y)
- return FiniteSet(*complex_num)
- else:
- return Set.__new__(cls, sets)
- @property
- def expr(self):
- x, y = self.variables
- return x + S.ImaginaryUnit*y
- class PolarComplexRegion(ComplexRegion):
- r"""
- Set representing a polar region of the complex plane.
- .. math:: Z = \{z \in \mathbb{C} \mid z = r\times (\cos(\theta) + I\sin(\theta)), r \in [\texttt{r}], \theta \in [\texttt{theta}]\}
- Examples
- ========
- >>> from sympy import ComplexRegion, Interval, oo, pi, I
- >>> rset = Interval(0, oo)
- >>> thetaset = Interval(0, pi)
- >>> upper_half_plane = ComplexRegion(rset * thetaset, polar=True)
- >>> 1 + I in upper_half_plane
- True
- >>> 1 - I in upper_half_plane
- False
- See also
- ========
- ComplexRegion
- CartesianComplexRegion
- Complexes
- """
- polar = True
- variables = symbols('r, theta', cls=Dummy)
- def __new__(cls, sets):
- new_sets = []
- # sets is Union of ProductSets
- if not sets.is_ProductSet:
- for k in sets.args:
- new_sets.append(k)
- # sets is ProductSets
- else:
- new_sets.append(sets)
- # Normalize input theta
- for k, v in enumerate(new_sets):
- new_sets[k] = ProductSet(v.args[0],
- normalize_theta_set(v.args[1]))
- sets = Union(*new_sets)
- return Set.__new__(cls, sets)
- @property
- def expr(self):
- from sympy.functions.elementary.trigonometric import sin, cos
- r, theta = self.variables
- return r*(cos(theta) + S.ImaginaryUnit*sin(theta))
- class Complexes(CartesianComplexRegion, metaclass=Singleton):
- """
- The :class:`Set` of all complex numbers
- Examples
- ========
- >>> from sympy import S, I
- >>> S.Complexes
- Complexes
- >>> 1 + I in S.Complexes
- True
- See also
- ========
- Reals
- ComplexRegion
- """
- is_empty = False
- is_finite_set = False
- # Override property from superclass since Complexes has no args
- @property
- def sets(self):
- return ProductSet(S.Reals, S.Reals)
- def __new__(cls):
- return Set.__new__(cls)
|