def shape(self): """Returns a list with dimensions of each index. Dimensions is a property of the array, not of the indices. Still, if the ``IndexedBase`` does not define a shape attribute, it is assumed that the ranges of the indices correspond to the shape of the array. >>> from sympy import IndexedBase, Idx, symbols >>> n, m = symbols('n m', integer=True) >>> i = Idx('i', m) >>> j = Idx('j', m) >>> A = IndexedBase('A', shape=(n, n)) >>> B = IndexedBase('B') >>> A[i, j].shape (n, n) >>> B[i, j].shape (m, m) """ from sympy.utilities.misc import filldedent if self.base.shape: return self.base.shape try: return Tuple(*[i.upper - i.lower + 1 for i in self.indices]) except AttributeError: raise IndexException(filldedent(""" Range is not defined for all indices in: %s""" % self)) except TypeError: raise IndexException(filldedent(""" Shape cannot be inferred from Idx with undefined range: %s""" % self))
def __getitem__(self, key): if not isinstance(key, tuple) and isinstance(key, slice): from sympy.matrices.expressions.slice import MatrixSlice return MatrixSlice(self, key, (0, None, 1)) if isinstance(key, tuple) and len(key) == 2: i, j = key if isinstance(i, slice) or isinstance(j, slice): from sympy.matrices.expressions.slice import MatrixSlice return MatrixSlice(self, i, j) i, j = sympify(i), sympify(j) if self.valid_index(i, j) != False: return self._entry(i, j) else: raise IndexError("Invalid indices (%s, %s)" % (i, j)) elif isinstance(key, (SYMPY_INTS, Integer)): # row-wise decomposition of matrix rows, cols = self.shape # allow single indexing if number of columns is known if not isinstance(cols, Integer): raise IndexError(filldedent(''' Single indexing is only supported when the number of columns is known.''')) key = sympify(key) i = key // cols j = key % cols if self.valid_index(i, j) != False: return self._entry(i, j) else: raise IndexError("Invalid index %s" % key) elif isinstance(key, (Symbol, Expr)): raise IndexError(filldedent(''' Only integers may be used when addressing the matrix with a single index.''')) raise IndexError("Invalid index, wanted %s[i,j]" % self)
def __new__(cls, label, range=None, **kw_args): from sympy.utilities.misc import filldedent if isinstance(label, basestring): label = Symbol(label, integer=True) label, range = map(sympify, (label, range)) if not label.is_integer: raise TypeError("Idx object requires an integer label.") elif is_sequence(range): if len(range) != 2: raise ValueError(filldedent(""" Idx range tuple must have length 2, but got %s""" % len(range))) for bound in range: if not (bound.is_integer or abs(bound) is S.Infinity): raise TypeError("Idx object requires integer bounds.") args = label, Tuple(*range) elif isinstance(range, Expr): if not (range.is_integer or range is S.Infinity): raise TypeError("Idx object requires an integer dimension.") args = label, Tuple(0, range - 1) elif range: raise TypeError(filldedent(""" The range must be an ordered iterable or integer SymPy expression.""")) else: args = label, obj = Expr.__new__(cls, *args, **kw_args) return obj
def _contains(self, other): from sympy.matrices import Matrix from sympy.solvers.solveset import solveset, linsolve from sympy.utilities.iterables import iterable, cartes L = self.lamda if self._is_multivariate(): if not iterable(L.expr): if iterable(other): return S.false return other.as_numer_denom() in self.func( Lambda(L.variables, L.expr.as_numer_denom()), self.base_set) if len(L.expr) != len(self.lamda.variables): raise NotImplementedError(filldedent(''' Dimensions of input and output of Lambda are different.''')) eqs = [expr - val for val, expr in zip(other, L.expr)] variables = L.variables free = set(variables) if all(i.is_number for i in list(Matrix(eqs).jacobian(variables))): solns = list(linsolve([e - val for e, val in zip(L.expr, other)], variables)) else: syms = [e.free_symbols & free for e in eqs] solns = {} for i, (e, s, v) in enumerate(zip(eqs, syms, other)): if not s: if e != v: return S.false solns[vars[i]] = [v] continue elif len(s) == 1: sy = s.pop() sol = solveset(e, sy) if sol is S.EmptySet: return S.false elif isinstance(sol, FiniteSet): solns[sy] = list(sol) else: raise NotImplementedError else: raise NotImplementedError solns = cartes(*[solns[s] for s in variables]) else: # assume scalar -> scalar mapping solnsSet = solveset(L.expr - other, L.variables[0]) if solnsSet.is_FiniteSet: solns = list(solnsSet) else: raise NotImplementedError(filldedent(''' Determining whether an ImageSet contains %s has not been implemented.''' % func_name(other))) for soln in solns: try: if soln in self.base_set: return S.true except TypeError: return self.base_set.contains(soln.evalf()) return S.false
def _solve_reduced_system(system, gens, entry=False): """Recursively solves reduced polynomial systems. """ if len(system) == len(gens) == 1: zeros = list(roots(system[0], gens[-1]).keys()) return [(zero,) for zero in zeros] basis = groebner(system, gens, polys=True) if len(basis) == 1 and basis[0].is_ground: if not entry: return [] else: return None univariate = list(filter(_is_univariate, basis)) if len(univariate) == 1: f = univariate.pop() else: raise NotImplementedError(filldedent(''' only zero-dimensional systems supported (finite number of solutions) ''')) gens = f.gens gen = gens[-1] zeros = list(roots(f.ltrim(gen)).keys()) if not zeros: return [] if len(basis) == 1: return [(zero,) for zero in zeros] solutions = [] for zero in zeros: new_system = [] new_gens = gens[:-1] for b in basis[:-1]: eq = _subs_root(b, gen, zero) if eq is not S.Zero: new_system.append(eq) for solution in _solve_reduced_system(new_system, new_gens): solutions.append(solution + (zero,)) if solutions and len(solutions[0]) != len(gens): raise NotImplementedError(filldedent(''' only zero-dimensional systems supported (finite number of solutions) ''')) return solutions
def conjugate_gauss_beams(wavelen, waist_in, waist_out, **kwargs): """ Find the optical setup conjugating the object/image waists. Parameters ========== wavelen : the wavelength of the beam waist_in and waist_out : the waists to be conjugated f : the focal distance of the element used in the conjugation Returns ======= a tuple containing (s_in, s_out, f) s_in : the distance before the optical element s_out : the distance after the optical element f : the focal distance of the optical element Examples ======== >>> from sympy.physics.optics import conjugate_gauss_beams >>> from sympy import symbols, factor >>> l, w_i, w_o, f = symbols('l w_i w_o f') >>> conjugate_gauss_beams(l, w_i, w_o, f=f)[0] f*(1 - sqrt(w_i**2/w_o**2 - pi**2*w_i**4/(f**2*l**2))) >>> factor(conjugate_gauss_beams(l, w_i, w_o, f=f)[1]) f*w_o**2*(w_i**2/w_o**2 - sqrt(w_i**2/w_o**2 - pi**2*w_i**4/(f**2*l**2)))/w_i**2 >>> conjugate_gauss_beams(l, w_i, w_o, f=f)[2] f """ #TODO add the other possible arguments wavelen, waist_in, waist_out = map(sympify, (wavelen, waist_in, waist_out)) m = waist_out / waist_in z = waist2rayleigh(waist_in, wavelen) if len(kwargs) != 1: raise ValueError("The function expects only one named argument") elif 'dist' in kwargs: raise NotImplementedError(filldedent(''' Currently only focal length is supported as a parameter''')) elif 'f' in kwargs: f = sympify(kwargs['f']) s_in = f * (1 - sqrt(1/m**2 - z**2/f**2)) s_out = gaussian_conj(s_in, z, f)[0] elif 's_in' in kwargs: raise NotImplementedError(filldedent(''' Currently only focal length is supported as a parameter''')) else: raise ValueError(filldedent(''' The functions expects the focal length as a named argument''')) return (s_in, s_out, f)
def __new__(cls, *args, **kwargs): from sympy.matrices.immutable import ImmutableDenseMatrix from sympy.matrices import zeros from sympy.matrices.matrices import MatrixBase from sympy.utilities.iterables import is_sequence isMat = lambda i: getattr(i, 'is_Matrix', False) if len(args) != 1 or \ not is_sequence(args[0]) or \ len(set([isMat(r) for r in args[0]])) != 1: raise ValueError(filldedent(''' expecting a sequence of 1 or more rows containing Matrices.''')) rows = args[0] if args else [] if not isMat(rows): if rows and isMat(rows[0]): rows = [rows] # rows is not list of lists or [] # regularity check # same number of matrices in each row blocky = ok = len(set([len(r) for r in rows])) == 1 if ok: # same number of rows for each matrix in a row for r in rows: ok = len(set([i.rows for i in r])) == 1 if not ok: break blocky = ok # same number of cols for each matrix in each col for c in range(len(rows[0])): ok = len(set([rows[i][c].cols for i in range(len(rows))])) == 1 if not ok: break if not ok: # same total cols in each row ok = len(set([ sum([i.cols for i in r]) for r in rows])) == 1 if blocky and ok: raise ValueError(filldedent(''' Although this matrix is comprised of blocks, the blocks do not fill the matrix in a size-symmetric fashion. To create a full matrix from these arguments, pass them directly to Matrix.''')) raise ValueError(filldedent(''' When there are not the same number of rows in each row's matrices or there are not the same number of total columns in each row, the matrix is not a block matrix. If this matrix is known to consist of blocks fully filling a 2-D space then see Matrix.irregular.''')) mat = ImmutableDenseMatrix(rows, evaluate=False) obj = Basic.__new__(cls, mat) return obj
def __new__(cls, *args): from sympy.functions.elementary.integers import ceiling if len(args) == 1: if isinstance(args[0], range if PY3 else xrange): args = args[0].__reduce__()[1] # use pickle method # 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: start, stop, step = [ w if w in [S.NegativeInfinity, S.Infinity] else sympify(as_int(w)) for w in (start, stop, step)] 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].''')) if not step.is_Integer: raise ValueError(filldedent(''' Ranges must have a literal integer step.''')) if all(i.is_infinite for i in (start, stop)): if start == stop: # canonical null handled below start = stop = S.One else: raise ValueError(filldedent(''' Either the start or end value of the Range must be finite.''')) if start.is_infinite: end = stop else: ref = start if start.is_finite else stop n = ceiling((stop - ref)/step) if n <= 0: # null Range start = end = 0 step = 1 else: end = ref + n*step return Basic.__new__(cls, start, end, step)
def __new__( cls, center=None, hradius=None, vradius=None, eccentricity=None, **kwargs): hradius = sympify(hradius) vradius = sympify(vradius) eccentricity = sympify(eccentricity) if center is None: center = Point(0, 0) else: center = Point(center, dim=2) if len(center) != 2: raise ValueError('The center of "{0}" must be a two dimensional point'.format(cls)) if len(list(filter(lambda x: x is not None, (hradius, vradius, eccentricity)))) != 2: raise ValueError(filldedent(''' Exactly two arguments of "hradius", "vradius", and "eccentricity" must not be None.''')) if eccentricity is not None: if hradius is None: hradius = vradius / sqrt(1 - eccentricity**2) elif vradius is None: vradius = hradius * sqrt(1 - eccentricity**2) if hradius == vradius: return Circle(center, hradius, **kwargs) if hradius == 0 or vradius == 0: return Segment(Point(center[0] - hradius, center[1] - vradius), Point(center[0] + hradius, center[1] + vradius)) return GeometryEntity.__new__(cls, center, hradius, vradius, **kwargs)
def mechanics_printing(**kwargs): # mechanics_printing has slightly different functionality in 0.7.5 but # shouldn't fundamentally need a deprecation warning so we do this # little wrapper that gives the warning that things have changed. # TODO : Remove this warning in the release after SymPy 0.7.5 # The message is only printed if this function is called with no args, # as was the previous only way to call it. def dict_is_empty(D): for k in D: return False return True if dict_is_empty(kwargs): msg = ( "See the doc string for slight changes to this function: " "keyword args may be needed for the desired effect. " "Otherwise use sympy.physics.vector.init_vprinting directly." ) SymPyDeprecationWarning(filldedent(msg)).warn() init_vprinting(**kwargs)
def __new__(cls, lhs, rhs, rop=None, **assumptions): # If called by a subclass, do nothing special and pass on to Expr. if cls is not Relational: return Expr.__new__(cls, lhs, rhs, **assumptions) # If called directly with an operator, look up the subclass # corresponding to that operator and delegate to it try: cls = cls.ValidRelationOperator[rop] rv = cls(lhs, rhs, **assumptions) # /// drop when Py2 is no longer supported # validate that Booleans are not being used in a relational # other than Eq/Ne; if isinstance(rv, (Eq, Ne)): pass elif isinstance(rv, Relational): # could it be otherwise? from sympy.core.symbol import Symbol from sympy.logic.boolalg import Boolean for a in rv.args: if isinstance(a, Symbol): continue if isinstance(a, Boolean): from sympy.utilities.misc import filldedent raise TypeError(filldedent(''' A Boolean argument can only be used in Eq and Ne; all other relationals expect real expressions. ''')) # \\\ return rv except KeyError: raise ValueError( "Invalid relational operator symbol: %r" % rop)
def _solve_relational(r): if sym not in r.free_symbols: nonsymfail(r) rv = _solve_inequality(r, sym) if isinstance(rv, Relational): free = rv.args[1].free_symbols if rv.args[0] != sym or sym in free: raise NotImplementedError(filldedent(''' Unable to solve relational %s for %s.''' % (r, sym))) if rv.rel_op == '==': # this equality has been affirmed to have the form # Eq(sym, rhs) where rhs is sym-free; it represents # a zero-width interval which will be ignored # whether it is an isolated condition or contained # within an And or an Or rv = S.false elif rv.rel_op == '!=': try: rv = Or(sym < rv.rhs, sym > rv.rhs) except TypeError: # e.g. x != I ==> all real x satisfy rv = S.true elif rv == (S.NegativeInfinity < sym) & (sym < S.Infinity): rv = S.true return rv
def reduce_inequalities(inequalities, symbols=[]): """Reduce a system of inequalities with rational coefficients. Examples ======== >>> from sympy import sympify as S, Symbol >>> from sympy.abc import x, y >>> from sympy.solvers.inequalities import reduce_inequalities >>> reduce_inequalities(0 <= x + 3, []) And(-3 <= x, x < oo) >>> reduce_inequalities(0 <= x + y*2 - 1, [x]) x >= -2*y + 1 """ if not iterable(inequalities): inequalities = [inequalities] inequalities = [sympify(i) for i in inequalities] gens = set().union(*[i.free_symbols for i in inequalities]) if not iterable(symbols): symbols = [symbols] symbols = (set(symbols) or gens) & gens if any(i.is_real is False for i in symbols): raise TypeError( filldedent( """ inequalities cannot contain symbols that are not real.""" ) ) # make vanilla symbol real recast = dict([(i, Dummy(i.name, real=True)) for i in gens if i.is_real is None]) inequalities = [i.xreplace(recast) for i in inequalities] symbols = set([i.xreplace(recast) for i in symbols]) # prefilter keep = [] for i in inequalities: if isinstance(i, Relational): i = i.func(i.lhs.as_expr() - i.rhs.as_expr(), 0) elif i not in (True, False): i = Eq(i, 0) if i == True: continue elif i == False: return S.false if i.lhs.is_number: raise NotImplementedError("could not determine truth value of %s" % i) keep.append(i) inequalities = keep del keep # solve system rv = _reduce_inequalities(inequalities, symbols) # restore original symbols and return return rv.xreplace(dict([(v, k) for k, v in recast.items()]))
def __new__(cls, dist): if not isinstance(dist, (ContinuousDistribution, DiscreteDistribution)): raise ValueError(filldedent('''CompoundDistribution can only be initialized from ContinuousDistribution or DiscreteDistribution ''')) _args = dist.args if not any([isinstance(i, RandomSymbol) for i in _args]): return dist return Basic.__new__(cls, dist)
def __new__(cls,dist, rvs): if not all([isinstance(rv, (Indexed, RandomSymbol))] for rv in rvs): raise ValueError(filldedent('''Marginal distribution can be intitialised only in terms of random variables or indexed random variables''')) rvs = Tuple.fromiter(rv for rv in rvs) if not isinstance(dist, JointDistribution) and len(random_symbols(dist)) == 0: return dist return Basic.__new__(cls, dist, rvs)
def _eval_rewrite_as_ITE(self, *args): byfree = {} args = list(args) default = any(c == True for b, c in args) for i, (b, c) in enumerate(args): if not isinstance(b, Boolean) and b != True: raise TypeError(filldedent(''' Expecting Boolean or bool but got `%s` ''' % func_name(b))) if c == True: break # loop over independent conditions for this b for c in c.args if isinstance(c, Or) else [c]: free = c.free_symbols x = free.pop() try: byfree[x] = byfree.setdefault( x, S.EmptySet).union(c.as_set()) except NotImplementedError: if not default: raise NotImplementedError(filldedent(''' A method to determine whether a multivariate conditional is consistent with a complete coverage of all variables has not been implemented so the rewrite is being stopped after encountering `%s`. This error would not occur if a default expression like `(foo, True)` were given. ''' % c)) if byfree[x] in (S.UniversalSet, S.Reals): # collapse the ith condition to True and break args[i] = list(args[i]) c = args[i][1] = True break if c == True: break if c != True: raise ValueError(filldedent(''' Conditions must cover all reals or a final default condition `(foo, True)` must be given. ''')) last, _ = args[i] # ignore all past ith arg for a, c in reversed(args[:i]): last = ITE(c, a, last) return _canonical(last)
def __new__(cls, *args): if len(args) == 1 and isinstance(args[0], Matrix) \ and args[0].shape == (2, 1): temp = args[0] elif len(args) == 2: temp = ((args[0],), (args[1],)) else: raise ValueError(filldedent(''' Expecting 2x1 Matrix or the 2 elements of the Matrix but got %s''' % str(args))) return Matrix.__new__(cls, temp)
def __new__(cls, base, *args, **kw_args): from sympy.utilities.misc import filldedent if not args: raise IndexException("Indexed needs at least one index.") if isinstance(base, (basestring, Symbol)): base = IndexedBase(base) elif not isinstance(base, IndexedBase): raise TypeError(filldedent(""" Indexed expects string, Symbol or IndexedBase as base.""")) return Expr.__new__(cls, base, *args, **kw_args)
def reflect(self, line): """Override GeometryEntity.reflect since the radius is not a GeometryEntity. Examples ======== >>> from sympy import Circle, Line >>> Circle((0, 1), 1).reflect(Line((0, 0), (1, 1))) Circle(Point(1, 0), -1) >>> from sympy import Ellipse, Line, Point >>> Ellipse(Point(3, 4), 1, 3).reflect(Line(Point(0, -4), Point(5, 0))) Traceback (most recent call last): ... NotImplementedError: General Ellipse is not supported but the equation of the reflected Ellipse is given by the zeros of: f(x, y) = (9*x/41 + 40*y/41 + 37/41)**2 + (40*x/123 - 3*y/41 - 364/123)**2 - 1 Notes ===== Until the general ellipse (with no axis parallel to the x-axis) is supported a NotImplemented error is raised and the equation whose zeros define the rotated ellipse is given. """ def _uniquely_named_symbol(xname, *exprs): """Return a symbol which, when printed, will have a name unique from any other already in the expressions given. The name is made unique by prepending underscores. """ prefix = '%s' x = prefix % xname syms = set.union(*[e.free_symbols for e in exprs]) while any(x == str(s) for s in syms): prefix = '_' + prefix x = prefix % xname return _symbol(x) if line.slope in (0, oo): c = self.center c = c.reflect(line) return self.func(c, -self.hradius, self.vradius) else: x, y = [_uniquely_named_symbol(name, self, line) for name in 'xy'] expr = self.equation(x, y) p = Point(x, y).reflect(line) result = expr.subs(zip((x, y), p.args ), simultaneous=True) raise NotImplementedError(filldedent( 'General Ellipse is not supported but the equation ' 'of the reflected Ellipse is given by the zeros of: ' + "f(%s, %s) = %s" % (str(x), str(y), str(result))))
def _calc_limit(a, b): """ replace d with a, using subs if possible, otherwise limit where sign of b is considered """ avals = list(set([_calc_limit_1(Fi, a, b) for Fi in F])) if len(avals) > 1: raise ValueError(filldedent(''' The mapping between F(x) and f(u) did not give a unique limit.''')) return avals[0]
def exprcondpair_new(cls, expr, cond): expr = as_Basic(expr) if cond == True: return Tuple.__new__(cls, expr, true) elif cond == False: return Tuple.__new__(cls, expr, false) if not isinstance(cond, Boolean): raise TypeError(filldedent(''' Second argument must be a Boolean, not `%s`''' % func_name(cond))) return Tuple.__new__(cls, expr, cond)
def __init__(self, *args): if len(args) == 4: temp = ((args[0], args[1]), (args[2], args[3])) elif len(args) == 1 \ and isinstance(args[0], Matrix) \ and args[0].shape == (2, 2): temp = args[0] else: raise ValueError(filldedent(''' Expecting 2x2 Matrix or the 4 elements of the Matrix but got %s''' % str(args))) Matrix.__init__(self, temp)
def __new__(cls, base, *args, **kw_args): from sympy.utilities.misc import filldedent if not args: raise IndexException("Indexed needs at least one index.") if isinstance(base, (string_types, Symbol)): base = IndexedBase(base) elif not hasattr(base, '__getitem__') and not isinstance(base, IndexedBase): raise TypeError(filldedent(""" Indexed expects string, Symbol, or IndexedBase as base.""")) args = list(map(sympify, args)) return Expr.__new__(cls, base, *args, **kw_args)
def __new__(cls, label, range=None, **kw_args): from sympy.utilities.misc import filldedent if isinstance(label, string_types): label = Symbol(label, integer=True) label, range = list(map(sympify, (label, range))) if label.is_Number: if not label.is_integer: raise TypeError("Index is not an integer number.") return label if not label.is_integer: raise TypeError("Idx object requires an integer label.") elif is_sequence(range): if len(range) != 2: raise ValueError(filldedent(""" Idx range tuple must have length 2, but got %s""" % len(range))) for bound in range: if (bound.is_integer is False and bound is not S.Infinity and bound is not S.NegativeInfinity): raise TypeError("Idx object requires integer bounds.") args = label, Tuple(*range) elif isinstance(range, Expr): if not (range.is_integer or range is S.Infinity): raise TypeError("Idx object requires an integer dimension.") args = label, Tuple(0, range - 1) elif range: raise TypeError(filldedent(""" The range must be an ordered iterable or integer SymPy expression.""")) else: args = label, obj = Expr.__new__(cls, *args, **kw_args) obj._assumptions["finite"] = True obj._assumptions["real"] = True return obj
def _reduce_inequalities(inequalities, symbols): # helper for reduce_inequalities poly_part, abs_part = {}, {} other = [] for inequality in inequalities: expr, rel = inequality.lhs, inequality.rel_op # rhs is 0 # check for gens using atoms which is more strict than free_symbols to # guard against EX domain which won't be handled by # reduce_rational_inequalities gens = expr.atoms(Symbol) if len(gens) == 1: gen = gens.pop() else: common = expr.free_symbols & symbols if len(common) == 1: gen = common.pop() other.append(_solve_inequality(Relational(expr, 0, rel), gen)) continue else: raise NotImplementedError( filldedent( """ inequality has more than one symbol of interest""" ) ) if expr.is_polynomial(gen): poly_part.setdefault(gen, []).append((expr, rel)) else: components = expr.find(lambda u: u.has(gen) and (u.is_Function or u.is_Pow and not u.exp.is_Integer)) if components and all(isinstance(i, Abs) for i in components): abs_part.setdefault(gen, []).append((expr, rel)) else: other.append(_solve_inequality(Relational(expr, 0, rel), gen)) poly_reduced = [] abs_reduced = [] for gen, exprs in poly_part.items(): poly_reduced.append(reduce_rational_inequalities([exprs], gen)) for gen, exprs in abs_part.items(): abs_reduced.append(reduce_abs_inequalities(exprs, gen)) return And(*(poly_reduced + abs_reduced + other))
def __new__(cls, p1, pt=None, angle=None, **kwargs): p1 = Point(p1) if pt is not None and angle is None: try: p2 = Point(pt) except NotImplementedError: from sympy.utilities.misc import filldedent raise ValueError( filldedent( """ The 2nd argument was not a valid Point; if it was meant to be an angle it should be given with keyword "angle".""" ) ) if p1 == p2: raise ValueError("A Ray requires two distinct points.") elif angle is not None and pt is None: # we need to know if the angle is an odd multiple of pi/2 c = pi_coeff(sympify(angle)) p2 = None if c is not None: if c.is_Rational: if c.q == 2: if c.p == 1: p2 = p1 + Point(0, 1) elif c.p == 3: p2 = p1 + Point(0, -1) elif c.q == 1: if c.p == 0: p2 = p1 + Point(1, 0) elif c.p == 1: p2 = p1 + Point(-1, 0) if p2 is None: c *= S.Pi else: c = angle % (2 * S.Pi) if not p2: m = 2 * c / S.Pi left = And(1 < m, m < 3) # is it in quadrant 2 or 3? x = Piecewise((-1, left), (Piecewise((0, Eq(m % 1, 0)), (1, True)), True)) y = Piecewise((-C.tan(c), left), (Piecewise((1, Eq(m, 1)), (-1, Eq(m, 3)), (C.tan(c), True)), True)) p2 = p1 + Point(x, y) else: raise ValueError('A 2nd point or keyword "angle" must be used.') return LinearEntity.__new__(cls, p1, p2, **kwargs)
def copyin_matrix(self, key, value): """Copy in values from a matrix into the given bounds. Parameters ========== key : slice The section of this matrix to replace. value : Matrix The matrix to copy values from. Examples ======== >>> from sympy.matrices import Matrix, eye >>> M = Matrix([[0, 1], [2, 3], [4, 5]]) >>> I = eye(3) >>> I[:3, :2] = M >>> I Matrix([ [0, 1, 0], [2, 3, 0], [4, 5, 1]]) >>> I[0, 1] = M >>> I Matrix([ [0, 0, 1], [2, 2, 3], [4, 4, 5]]) See Also ======== copyin_list """ rlo, rhi, clo, chi = self.key2bounds(key) shape = value.shape dr, dc = rhi - rlo, chi - clo if shape != (dr, dc): raise ShapeError( filldedent( "The Matrix `value` doesn't have the " "same dimensions " "as the in sub-Matrix given by `key`." ) ) for i in range(value.rows): for j in range(value.cols): self[i + rlo, j + clo] = value[i, j]
def __new__(cls, base, *args, **kw_args): from sympy.utilities.misc import filldedent from sympy.tensor.array.ndim_array import NDimArray from sympy.matrices.matrices import MatrixBase if not args: raise IndexException("Indexed needs at least one index.") if isinstance(base, (string_types, Symbol)): base = IndexedBase(base) elif not hasattr(base, '__getitem__') and not isinstance(base, IndexedBase): raise TypeError(filldedent(""" Indexed expects string, Symbol, or IndexedBase as base.""")) args = list(map(sympify, args)) if isinstance(base, (NDimArray, collections.Iterable, Tuple, MatrixBase)) and all([i.is_number for i in args]): return base[args] return Expr.__new__(cls, base, *args, **kw_args)
def __new__(cls, expr, cond): expr = as_Basic(expr) if cond == True: return Tuple.__new__(cls, expr, true) elif cond == False: return Tuple.__new__(cls, expr, false) elif isinstance(cond, Basic) and cond.has(Piecewise): cond = piecewise_fold(cond) if isinstance(cond, Piecewise): cond = cond.rewrite(ITE) if not isinstance(cond, Boolean): raise TypeError(filldedent(''' Second argument must be a Boolean, not `%s`''' % func_name(cond))) return Tuple.__new__(cls, expr, cond)
def _solve_relational(r): rv = _solve_inequality(r, sym) if isinstance(rv, Relational) and \ sym in rv.free_symbols: if rv.args[0] != sym: raise NotImplementedError(filldedent(''' Unable to solve relational %s for %s.''' % (r, sym))) if rv.rel_op == '!=': try: rv = Or(sym < rv.rhs, sym > rv.rhs) except TypeError: # e.g. x != I ==> all real x satisfy rv = S.true if rv == (S.NegativeInfinity < sym) & (sym < S.Infinity): rv = S.true return rv
def linodesolve(A, t, b=None, B=None, type="auto", doit=False): r""" System of n equations linear first-order differential equations Explanation =========== This solver solves the system of ODEs of the follwing form: .. math:: X'(t) = A(t) X(t) + b(t) Here, $A(t)$ is the coefficient matrix, $X(t)$ is the vector of n independent variables, $b(t)$ is the non-homogeneous term and $X'(t)$ is the derivative of $X(t)$ Depending on the properties of $A(t)$ and $b(t)$, this solver evaluates the solution differently. When $A(t)$ is constant coefficient matrix and $b(t)$ is zero vector i.e. system is homogeneous, the solution is: .. math:: X(t) = \exp(A t) C Here, $C$ is a vector of constants and $A$ is the constant coefficient matrix. When $A(t)$ is constant coefficient matrix and $b(t)$ is non-zero i.e. system is non-homogeneous, the solution is: .. math:: X(t) = e^{A t} ( \int e^{- A t} b \,dt + C) When $A(t)$ is coefficient matrix such that its commutative with its antiderivative $B(t)$ and $b(t)$ is a zero vector i.e. system is homogeneous, the solution is: .. math:: X(t) = \exp(B(t)) C When $A(t)$ is commutative with its antiderivative $B(t)$ and $b(t)$ is non-zero i.e. system is non-homogeneous, the solution is: .. math:: X(t) = e^{B(t)} ( \int e^{-B(t)} b(t) \,dt + C) The final solution is the general solution for all the four equations since a constant coefficient matrix is always commutative with its antidervative. Parameters ========== A : Matrix Coefficient matrix of the system of linear first order ODEs. t : Symbol Independent variable in the system of ODEs. b : Matrix or None Non-homogeneous term in the system of ODEs. If None is passed, a homogeneous system of ODEs is assumed. B : Matrix or None Antiderivative of the coefficient matrix. If the antiderivative is not passed and the solution requires the term, then the solver would compute it internally. type : String Type of the system of ODEs passed. Depending on the type, the solution is evaluated. The type values allowed and the corresponding system it solves are: "type1" for constant coefficient homogeneous "type2" for constant coefficient non-homogeneous, "type3" for non-constant coefficient homogeneous and "type4" for non-constant coefficient non-homogeneous. The default value is "auto" which will let the solver decide the correct type of the system passed. doit : Boolean Evaluate the solution if True, default value is False Examples ======== To solve the system of ODEs using this function directly, several things must be done in the right order. Wrong inputs to the function will lead to incorrect results. >>> from sympy import symbols, Function, Eq >>> from sympy.solvers.ode.systems import canonical_odes, linear_ode_to_matrix, linodesolve, linodesolve_type >>> from sympy.solvers.ode.subscheck import checksysodesol >>> f, g = symbols("f, g", cls=Function) >>> x, a = symbols("x, a") >>> funcs = [f(x), g(x)] >>> eqs = [Eq(f(x).diff(x) - f(x), a*g(x) + 1), Eq(g(x).diff(x) + g(x), a*f(x))] Here, it is important to note that before we derive the coefficient matrix, it is important to get the system of ODEs into the desired form. For that we will use :obj:`sympy.solvers.ode.systems.canonical_odes()`. >>> eqs = canonical_odes(eqs, funcs, x) >>> eqs [Eq(Derivative(f(x), x), a*g(x) + f(x) + 1), Eq(Derivative(g(x), x), a*f(x) - g(x))] Now, we will use :obj:`sympy.solvers.ode.systems.linear_ode_to_matrix()` to get the coefficient matrix and the non-homogeneous term if it is there. >>> (A1, A0), b = linear_ode_to_matrix(eqs, funcs, x, 1) >>> A = -A0 We have the coefficient matrices and the non-homogeneous term ready. Now, we can use :obj:`sympy.solvers.ode.systems.linodesolve_type()` to get the information for the system of ODEs to finally pass it to the solver. >>> system_info = linodesolve_type(A, x, b=b) >>> sol_vector = linodesolve(A, x, b=b, B=system_info['antiderivative'], type=system_info['type']) Now, we can prove if the solution is correct or not by using :obj:`sympy.solvers.ode.subscheck.checksysodesol()` >>> sol = [Eq(f, s) for f, s in zip(funcs, sol_vector)] >>> checksysodesol(eqs, sol) (True, [0, 0]) We can also use the doit method to evaluate the solutions passed by the function. >>> sol_vector_evaluated = linodesolve(A, x, b=b, type="type2", doit=True) Now, we will look at a system of ODEs which is non-constant. >>> eqs = [Eq(f(x).diff(x), f(x) + x*g(x)), Eq(g(x).diff(x), -x*f(x) + g(x))] The system defined above is already in the desired form, so we don't have to convert it. >>> (A1, A0), b = linear_ode_to_matrix(eqs, funcs, x, 1) >>> A = -A0 A user can also pass the commutative antidervative required for type3 and type4 system of ODEs. Passing an incorrect one will lead to incorrect results. If the coefficient matrix is not commutative with its antiderivative, then :obj:`sympy.solvers.ode.systems.linodesolve_type()` raises a NotImplementedError. If it does have a commutative antiderivative, then the function just returns the information about the system. >>> system_info = linodesolve_type(A, x, b=b) Now, we can pass the antiderivative as an argument to get the solution. If the system information is not passed, then the solver will compute the required arguments internally. >>> sol_vector = linodesolve(A, x, b=b) Once again, we can verify the solution obtained. >>> sol = [Eq(f, s) for f, s in zip(funcs, sol_vector)] >>> checksysodesol(eqs, sol) (True, [0, 0]) Returns ======= List Raises ====== ValueError This error is raised when the coefficient matrix, non-homogeneous term or the antiderivative, if passed, aren't a matrix or don't have correct dimensions NonSquareMatrixError When the coefficient matrix or its antiderivative, if passed isn't a square matrix NotImplementedError If the coefficient matrix doesn't have a commutative antiderivative See Also ======== linear_ode_to_matrix: Coefficient matrix computation function canonical_odes: System of ODEs representation change linodesolve_type: Getting information about systems of ODEs to pass in this solver """ if not isinstance(A, MatrixBase): raise ValueError( filldedent('''\ The coefficients of the system of ODEs should be of type Matrix ''')) if not A.is_square: raise NonSquareMatrixError( filldedent('''\ The coefficient matrix must be a square ''')) if b is not None: if not isinstance(b, MatrixBase): raise ValueError( filldedent('''\ The non-homogeneous terms of the system of ODEs should be of type Matrix ''')) if A.rows != b.rows: raise ValueError( filldedent('''\ The system of ODEs should have the same number of non-homogeneous terms and the number of equations ''')) if B is not None: if not isinstance(B, MatrixBase): raise ValueError( filldedent('''\ The antiderivative of coefficients of the system of ODEs should be of type Matrix ''')) if not B.is_square: raise NonSquareMatrixError( filldedent('''\ The antiderivative of the coefficient matrix must be a square ''')) if A.rows != B.rows: raise ValueError( filldedent('''\ The coefficient matrix and its antiderivative should have same dimensions ''')) if not any(type == "type{}".format(i) for i in range(1, 5)) and not type == "auto": raise ValueError( filldedent('''\ The input type should be a valid one ''')) n = A.rows constants = numbered_symbols(prefix='C', cls=Symbol, start=1) Cvect = Matrix(list(next(constants) for _ in range(n))) if (type == "type2" or type == "type4") and b is None: b = zeros(n, 1) if type == "auto": system_info = linodesolve_type(A, t, b=b) type = system_info["type"] B = system_info["antiderivative"] if type == "type1" or type == "type2": P, J = matrix_exp_jordan_form(A, t) P = simplify(P) if type == "type1": sol_vector = P * (J * Cvect) else: sol_vector = P * J * ( (J.inv() * P.inv() * b).applyfunc(lambda x: Integral(x, t)) + Cvect) else: if B is None: B, _ = _is_commutative_anti_derivative(A, t) if type == "type3": sol_vector = B.exp() * Cvect else: sol_vector = B.exp() * (( (-B).exp() * b).applyfunc(lambda x: Integral(x, t)) + Cvect) gens = sol_vector.atoms(exp) if type != "type1": sol_vector = [expand_mul(s) for s in sol_vector] sol_vector = [collect(s, ordered(gens), exact=True) for s in sol_vector] if doit: sol_vector = [s.doit() for s in sol_vector] return sol_vector
def sample(expr, condition=None, size=(), library='scipy', numsamples=1, **kwargs): """ A realization of the random expression Parameters ========== expr : Expression of random variables Expression from which sample is extracted condition : Expr containing RandomSymbols A conditional expression size : int, tuple Represents size of each sample in numsamples library : str - 'scipy' : Sample using scipy - 'numpy' : Sample using numpy - 'pymc3' : Sample using PyMC3 Choose any of the available options to sample from as string, by default is 'scipy' numsamples : int Number of samples, each with size as ``size`` Examples ======== >>> from sympy.stats import Die, sample, Normal, Geometric >>> X, Y, Z = Die('X', 6), Die('Y', 6), Die('Z', 6) # Finite Random Variable >>> die_roll = sample(X + Y + Z) # doctest: +SKIP >>> next(die_roll) # doctest: +SKIP 6 >>> N = Normal('N', 3, 4) # Continuous Random Variable >>> samp = next(sample(N)) # doctest: +SKIP >>> samp in N.pspace.domain.set # doctest: +SKIP True >>> samp = next(sample(N, N>0)) # doctest: +SKIP >>> samp > 0 # doctest: +SKIP True >>> samp_list = next(sample(N, size=4)) # doctest: +SKIP >>> [sam in N.pspace.domain.set for sam in samp_list] # doctest: +SKIP [True, True, True, True] >>> G = Geometric('G', 0.5) # Discrete Random Variable >>> samp_list = next(sample(G, size=3)) # doctest: +SKIP >>> samp_list # doctest: +SKIP array([10, 4, 1]) >>> [sam in G.pspace.domain.set for sam in samp_list] # doctest: +SKIP [True, True, True] >>> MN = Normal("MN", [3, 4], [[2, 1], [1, 2]]) # Joint Random Variable >>> samp_list = next(sample(MN, size=4)) # doctest: +SKIP >>> samp_list # doctest: +SKIP array([[4.22564264, 3.23364418], [3.41002011, 4.60090908], [3.76151866, 4.77617143], [4.71440865, 2.65714157]]) >>> [tuple(sam) in MN.pspace.domain.set for sam in samp_list] # doctest: +SKIP [True, True, True, True] Returns ======= sample: iterator object iterator object containing the sample/samples of given expr """ ### TODO: Remove the user warnings in the future releases message = ("The return type of sample has been changed to return an " "iterator object since version 1.7. For more information see " "https://github.com/sympy/sympy/issues/19061") warnings.warn(filldedent(message)) return sample_iter(expr, condition, size=size, library=library, numsamples=numsamples)
def solve_univariate_inequality(expr, gen, relational=True, domain=S.Reals): """Solves a real univariate inequality. Parameters ========== expr : Relational The target inequality gen : Symbol The variable for which the inequality is solved relational : bool A Relational type output is expected or not domain : Set The domain over which the equation is solved Raises ====== NotImplementedError The solution of the inequality cannot be determined due to limitation in `solvify`. Notes ===== Currently, we cannot solve all the inequalities due to limitations in `solvify`. Also, the solution returned for trigonometric inequalities are restricted in its periodic interval. See Also ======== solvify: solver returning solveset solutions with solve's output API Examples ======== >>> from sympy.solvers.inequalities import solve_univariate_inequality >>> from sympy import Symbol, sin, Interval, S >>> x = Symbol('x') >>> solve_univariate_inequality(x**2 >= 4, x) Or(And(-oo < x, x <= -2), And(2 <= x, x < oo)) >>> solve_univariate_inequality(x**2 >= 4, x, relational=False) (-oo, -2] U [2, oo) >>> domain = Interval(0, S.Infinity) >>> solve_univariate_inequality(x**2 >= 4, x, False, domain) [2, oo) >>> solve_univariate_inequality(sin(x) > 0, x, relational=False) (0, pi) """ from sympy.calculus.util import (continuous_domain, periodicity, function_range) from sympy.solvers.solvers import denoms from sympy.solvers.solveset import solveset_real, solvify # This keeps the function independent of the assumptions about `gen`. # `solveset` makes sure this function is called only when the domain is # real. d = Dummy(real=True) expr = expr.subs(gen, d) _gen = gen gen = d rv = None if expr is S.true: rv = domain elif expr is S.false: rv = S.EmptySet else: e = expr.lhs - expr.rhs period = periodicity(e, gen) if period is not None: frange = function_range(e, gen, domain) rel = expr.rel_op if rel == '<' or rel == '<=': if expr.func(frange.sup, 0): rv = domain elif not expr.func(frange.inf, 0): rv = S.EmptySet elif rel == '>' or rel == '>=': if expr.func(frange.inf, 0): rv = domain elif not expr.func(frange.sup, 0): rv = S.EmptySet inf, sup = domain.inf, domain.sup if sup - inf is S.Infinity: domain = Interval(0, period, False, True) if rv is None: singularities = [] for d in denoms(e): singularities.extend(solvify(d, gen, domain)) domain = continuous_domain(e, gen, domain) solns = solvify(e, gen, domain) if solns is None: raise NotImplementedError(filldedent('''The inequality cannot be solved using solve_univariate_inequality.''')) include_x = expr.func(0, 0) def valid(x): v = e.subs(gen, x) try: r = expr.func(v, 0) except TypeError: r = S.false if r in (S.true, S.false): return r if v.is_real is False: return S.false else: v = v.n(2) if v.is_comparable: return expr.func(v, 0) return S.false start = domain.inf sol_sets = [S.EmptySet] try: discontinuities = domain.boundary - FiniteSet(domain.inf, domain.sup) critical_points = set(solns + singularities + list(discontinuities)) reals = _nsort(critical_points, separated=True)[0] except NotImplementedError: raise NotImplementedError('sorting of these roots is not supported') if valid(start) and start.is_finite: sol_sets.append(FiniteSet(start)) for x in reals: end = x if end in [S.NegativeInfinity, S.Infinity]: if valid(S(0)): sol_sets.append(Interval(start, S.Infinity, True, True)) break pt = ((start + end)/2 if start is not S.NegativeInfinity else (end/2 if end.is_positive else (2*end if end.is_negative else end - 1))) if valid(pt): sol_sets.append(Interval(start, end, True, True)) if x in singularities: singularities.remove(x) elif include_x: sol_sets.append(FiniteSet(x)) start = end end = domain.sup # in case start == -oo then there were no solutions so we just # check a point between -oo and oo (e.g. 0) else pick a point # past the last solution (which is start after the end of the # for-loop above pt = (0 if start is S.NegativeInfinity else (start/2 if start.is_negative else (2*start if start.is_positive else start + 1))) if pt >= end: pt = (start + end)/2 if valid(pt): sol_sets.append(Interval(start, end, True, True)) rv = Union(*sol_sets).subs(gen, _gen) return rv if not relational else rv.as_relational(_gen)
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 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)
def transform(self, x, u): r""" Performs a change of variables from `x` to `u` using the relationship given by `x` and `u` which will define the transformations `f` and `F` (which are inverses of each other) as follows: 1) If `x` is a Symbol (which is a variable of integration) then `u` will be interpreted as some function, f(u), with inverse F(u). This, in effect, just makes the substitution of x with f(x). 2) If `u` is a Symbol then `x` will be interpreted as some function, F(x), with inverse f(u). This is commonly referred to as u-substitution. Once f and F have been identified, the transformation is made as follows: .. math:: \int_a^b x \mathrm{d}x \rightarrow \int_{F(a)}^{F(b)} f(x) \frac{\mathrm{d}}{\mathrm{d}x} where `F(x)` is the inverse of `f(x)` and the limits and integrand have been corrected so as to retain the same value after integration. Notes ===== The mappings, F(x) or f(u), must lead to a unique integral. Linear or rational linear expression, `2*x`, `1/x` and `sqrt(x)`, will always work; quadratic expressions like `x**2 - 1` are acceptable as long as the resulting integrand does not depend on the sign of the solutions (see examples). The integral will be returned unchanged if `x` is not a variable of integration. `x` must be (or contain) only one of of the integration variables. If `u` has more than one free symbol then it should be sent as a tuple (`u`, `uvar`) where `uvar` identifies which variable is replacing the integration variable. XXX can it contain another integration variable? Examples ======== >>> from sympy.abc import a, b, c, d, x, u, y >>> from sympy import Integral, S, cos, sqrt >>> i = Integral(x*cos(x**2 - 1), (x, 0, 1)) transform can change the variable of integration >>> i.transform(x, u) Integral(u*cos(u**2 - 1), (u, 0, 1)) transform can perform u-substitution as long as a unique integrand is obtained: >>> i.transform(x**2 - 1, u) Integral(cos(u)/2, (u, -1, 0)) This attempt fails because x = +/-sqrt(u + 1) and the sign does not cancel out of the integrand: >>> Integral(cos(x**2 - 1), (x, 0, 1)).transform(x**2 - 1, u) Traceback (most recent call last): ... ValueError: The mapping between F(x) and f(u) did not give a unique integrand. transform can do a substitution. Here, the previous result is transformed back into the original expression using "u-substitution": >>> ui = _ >>> _.transform(sqrt(u + 1), x) == i True We can accomplish the same with a regular substitution: >>> ui.transform(u, x**2 - 1) == i True If the `x` does not contain a symbol of integration then the integral will be returned unchanged. Integral `i` does not have an integration variable `a` so no change is made: >>> i.transform(a, x) == i True When `u` has more than one free symbol the symbol that is replacing `x` must be identified by passing `u` as a tuple: >>> Integral(x, (x, 0, 1)).transform(x, (u + a, u)) Integral(a + u, (u, -a, -a + 1)) >>> Integral(x, (x, 0, 1)).transform(x, (u + a, a)) Integral(a + u, (a, -u, -u + 1)) See Also ======== variables : Lists the integration variables as_dummy : Replace integration variables with dummy ones """ d = Dummy('d') xfree = x.free_symbols.intersection(self.variables) if len(xfree) > 1: raise ValueError('F(x) can only contain one of: %s' % self.variables) xvar = xfree.pop() if xfree else d if xvar not in self.variables: return self u = sympify(u) if isinstance(u, Expr): ufree = u.free_symbols if len(ufree) != 1: raise ValueError( filldedent(''' When f(u) has more than one free symbol, the one replacing x must be identified: pass f(u) as (f(u), u)''')) uvar = ufree.pop() else: u, uvar = u if uvar not in u.free_symbols: raise ValueError( filldedent(''' Expecting a tuple (expr, symbol) where symbol identified a free symbol in expr, but symbol is not in expr's free symbols.''')) if not isinstance(uvar, Symbol): raise ValueError( filldedent(''' Expecting a tuple (expr, symbol) but didn't get a symbol; got %s''' % uvar)) if x.is_Symbol and u.is_Symbol: return self.xreplace({x: u}) if not x.is_Symbol and not u.is_Symbol: raise ValueError('either x or u must be a symbol') if uvar == xvar: return self.transform(x, u.subs(uvar, d)).xreplace({d: uvar}) if uvar in self.limits: raise ValueError( filldedent(''' u must contain the same variable as in x or a variable that is not already an integration variable''')) if not x.is_Symbol: F = [x.subs(xvar, d)] soln = solve(u - x, xvar, check=False) if not soln: raise ValueError('no solution for solve(F(x) - f(u), x)') f = [fi.subs(uvar, d) for fi in soln] else: f = [u.subs(uvar, d)] pdiff, reps = posify(u - x) puvar = uvar.subs([(v, k) for k, v in reps.items()]) soln = [s.subs(reps) for s in solve(pdiff, puvar)] if not soln: raise ValueError('no solution for solve(F(x) - f(u), u)') F = [fi.subs(xvar, d) for fi in soln] newfuncs = set([ (self.function.subs(xvar, fi) * fi.diff(d)).subs(d, uvar) for fi in f ]) if len(newfuncs) > 1: raise ValueError( filldedent(''' The mapping between F(x) and f(u) did not give a unique integrand.''')) newfunc = newfuncs.pop() def _calc_limit_1(F, a, b): """ replace d with a, using subs if possible, otherwise limit where sign of b is considered """ wok = F.subs(d, a) if wok is S.NaN or wok.is_finite is False and a.is_finite: return limit(sign(b) * F, d, a) return wok def _calc_limit(a, b): """ replace d with a, using subs if possible, otherwise limit where sign of b is considered """ avals = list(set([_calc_limit_1(Fi, a, b) for Fi in F])) if len(avals) > 1: raise ValueError( filldedent(''' The mapping between F(x) and f(u) did not give a unique limit.''')) return avals[0] newlimits = [] for xab in self.limits: sym = xab[0] if sym == xvar: if len(xab) == 3: a, b = xab[1:] a, b = _calc_limit(a, b), _calc_limit(b, a) if a > b: a, b = b, a newfunc = -newfunc newlimits.append((uvar, a, b)) elif len(xab) == 2: a = _calc_limit(xab[1], 1) newlimits.append((uvar, a)) else: newlimits.append(uvar) else: newlimits.append(xab) return self.func(newfunc, *newlimits)
def solve_univariate_inequality(expr, gen, relational=True, domain=S.Reals, continuous=False): """Solves a real univariate inequality. Parameters ========== expr : Relational The target inequality gen : Symbol The variable for which the inequality is solved relational : bool A Relational type output is expected or not domain : Set The domain over which the equation is solved continuous: bool True if expr is known to be continuous over the given domain (and so continuous_domain() doesn't need to be called on it) Raises ====== NotImplementedError The solution of the inequality cannot be determined due to limitation in :func:`sympy.solvers.solveset.solvify`. Notes ===== Currently, we cannot solve all the inequalities due to limitations in :func:`sympy.solvers.solveset.solvify`. Also, the solution returned for trigonometric inequalities are restricted in its periodic interval. See Also ======== sympy.solvers.solveset.solvify: solver returning solveset solutions with solve's output API Examples ======== >>> from sympy.solvers.inequalities import solve_univariate_inequality >>> from sympy import Symbol, sin, Interval, S >>> x = Symbol('x') >>> solve_univariate_inequality(x**2 >= 4, x) ((2 <= x) & (x < oo)) | ((x <= -2) & (-oo < x)) >>> solve_univariate_inequality(x**2 >= 4, x, relational=False) Union(Interval(-oo, -2), Interval(2, oo)) >>> domain = Interval(0, S.Infinity) >>> solve_univariate_inequality(x**2 >= 4, x, False, domain) Interval(2, oo) >>> solve_univariate_inequality(sin(x) > 0, x, relational=False) Interval.open(0, pi) """ from sympy import im from sympy.calculus.util import continuous_domain, periodicity, function_range from sympy.solvers.solvers import denoms from sympy.solvers.solveset import solvify, solveset # This keeps the function independent of the assumptions about `gen`. # `solveset` makes sure this function is called only when the domain is # real. _gen = gen _domain = domain if gen.is_extended_real is False: rv = S.EmptySet return rv if not relational else rv.as_relational(_gen) elif gen.is_extended_real is None: gen = Dummy("gen", extended_real=True) try: expr = expr.xreplace({_gen: gen}) except TypeError: raise TypeError( filldedent(""" When gen is real, the relational has a complex part which leads to an invalid comparison like I < 0. """)) rv = None if expr is S.true: rv = domain elif expr is S.false: rv = S.EmptySet else: e = expr.lhs - expr.rhs period = periodicity(e, gen) if period == S.Zero: e = expand_mul(e) const = expr.func(e, 0) if const is S.true: rv = domain elif const is S.false: rv = S.EmptySet elif period is not None: frange = function_range(e, gen, domain) rel = expr.rel_op if rel == "<" or rel == "<=": if expr.func(frange.sup, 0): rv = domain elif not expr.func(frange.inf, 0): rv = S.EmptySet elif rel == ">" or rel == ">=": if expr.func(frange.inf, 0): rv = domain elif not expr.func(frange.sup, 0): rv = S.EmptySet inf, sup = domain.inf, domain.sup if sup - inf is S.Infinity: domain = Interval(0, period, False, True) if rv is None: n, d = e.as_numer_denom() try: if gen not in n.free_symbols and len(e.free_symbols) > 1: raise ValueError # this might raise ValueError on its own # or it might give None... solns = solvify(e, gen, domain) if solns is None: # in which case we raise ValueError raise ValueError except (ValueError, NotImplementedError): # replace gen with generic x since it's # univariate anyway raise NotImplementedError( filldedent(""" The inequality, %s, cannot be solved using solve_univariate_inequality. """ % expr.subs(gen, Symbol("x")))) expanded_e = expand_mul(e) def valid(x): # this is used to see if gen=x satisfies the # relational by substituting it into the # expanded form and testing against 0, e.g. # if expr = x*(x + 1) < 2 then e = x*(x + 1) - 2 # and expanded_e = x**2 + x - 2; the test is # whether a given value of x satisfies # x**2 + x - 2 < 0 # # expanded_e, expr and gen used from enclosing scope v = expanded_e.subs(gen, expand_mul(x)) try: r = expr.func(v, 0) except TypeError: r = S.false if r in (S.true, S.false): return r if v.is_extended_real is False: return S.false else: v = v.n(2) if v.is_comparable: return expr.func(v, 0) # not comparable or couldn't be evaluated raise NotImplementedError( "relationship did not evaluate: %s" % r) singularities = [] for d in denoms(expr, gen): singularities.extend(solvify(d, gen, domain)) if not continuous: domain = continuous_domain(expanded_e, gen, domain) include_x = "=" in expr.rel_op and expr.rel_op != "!=" try: discontinuities = set(domain.boundary - FiniteSet(domain.inf, domain.sup)) # remove points that are not between inf and sup of domain critical_points = FiniteSet( *(solns + singularities + list(discontinuities))).intersection( Interval( domain.inf, domain.sup, domain.inf not in domain, domain.sup not in domain, )) if all(r.is_number for r in critical_points): reals = _nsort(critical_points, separated=True)[0] else: sifted = sift(critical_points, lambda x: x.is_extended_real) if sifted[None]: # there were some roots that weren't known # to be real raise NotImplementedError try: reals = sifted[True] if len(reals) > 1: reals = list(sorted(reals)) except TypeError: raise NotImplementedError except NotImplementedError: raise NotImplementedError( "sorting of these roots is not supported") # If expr contains imaginary coefficients, only take real # values of x for which the imaginary part is 0 make_real = S.Reals if im(expanded_e) != S.Zero: check = True im_sol = FiniteSet() try: a = solveset(im(expanded_e), gen, domain) if not isinstance(a, Interval): for z in a: if (z not in singularities and valid(z) and z.is_extended_real): im_sol += FiniteSet(z) else: start, end = a.inf, a.sup for z in _nsort(critical_points + FiniteSet(end)): valid_start = valid(start) if start != end: valid_z = valid(z) pt = _pt(start, z) if (pt not in singularities and pt.is_extended_real and valid(pt)): if valid_start and valid_z: im_sol += Interval(start, z) elif valid_start: im_sol += Interval.Ropen(start, z) elif valid_z: im_sol += Interval.Lopen(start, z) else: im_sol += Interval.open(start, z) start = z for s in singularities: im_sol -= FiniteSet(s) except (TypeError): im_sol = S.Reals check = False if isinstance(im_sol, EmptySet): raise ValueError( filldedent(""" %s contains imaginary parts which cannot be made 0 for any value of %s satisfying the inequality, leading to relations like I < 0. """ % (expr.subs(gen, _gen), _gen))) make_real = make_real.intersect(im_sol) sol_sets = [S.EmptySet] start = domain.inf if valid(start) and start.is_finite: sol_sets.append(FiniteSet(start)) for x in reals: end = x if valid(_pt(start, end)): sol_sets.append(Interval(start, end, True, True)) if x in singularities: singularities.remove(x) else: if x in discontinuities: discontinuities.remove(x) _valid = valid(x) else: # it's a solution _valid = include_x if _valid: sol_sets.append(FiniteSet(x)) start = end end = domain.sup if valid(end) and end.is_finite: sol_sets.append(FiniteSet(end)) if valid(_pt(start, end)): sol_sets.append(Interval.open(start, end)) if im(expanded_e) != S.Zero and check: rv = (make_real).intersect(_domain) else: rv = Intersection((Union(*sol_sets)), make_real, _domain).subs(gen, _gen) return rv if not relational else rv.as_relational(_gen)
def reduce_inequalities(inequalities, symbols=[]): """Reduce a system of inequalities with rational coefficients. Examples ======== >>> from sympy import sympify as S, Symbol >>> from sympy.abc import x, y >>> from sympy.solvers.inequalities import reduce_inequalities >>> reduce_inequalities(0 <= x + 3, []) (-3 <= x) & (x < oo) >>> reduce_inequalities(0 <= x + y*2 - 1, [x]) (x < oo) & (x >= 1 - 2*y) """ if not iterable(inequalities): inequalities = [inequalities] inequalities = [sympify(i) for i in inequalities] gens = set().union(*[i.free_symbols for i in inequalities]) if not iterable(symbols): symbols = [symbols] symbols = (set(symbols) or gens) & gens if any(i.is_extended_real is False for i in symbols): raise TypeError( filldedent(""" inequalities cannot contain symbols that are not real. """)) # make vanilla symbol real recast = { i: Dummy(i.name, extended_real=True) for i in gens if i.is_extended_real is None } inequalities = [i.xreplace(recast) for i in inequalities] symbols = {i.xreplace(recast) for i in symbols} # prefilter keep = [] for i in inequalities: if isinstance(i, Relational): i = i.func(i.lhs.as_expr() - i.rhs.as_expr(), 0) elif i not in (True, False): i = Eq(i, 0) if i == True: continue elif i == False: return S.false if i.lhs.is_number: raise NotImplementedError("could not determine truth value of %s" % i) keep.append(i) inequalities = keep del keep # solve system rv = _reduce_inequalities(inequalities, symbols) # restore original symbols and return return rv.xreplace({v: k for k, v in recast.items()})
def __str__(self): return '\n%s\n' % filldedent(self.fullMessage)
def banded(*args, **kwargs): """Returns a SparseMatrix from the given dictionary describing the diagonals of the matrix. The keys are positive for upper diagonals and negative for those below the main diagonal. The values may be: * expressions or single-argument functions, * lists or tuples of values, * matrices Unless dimensions are given, the size of the returned matrix will be large enough to contain the largest non-zero value provided. kwargs ====== rows : rows of the resulting matrix; computed if not given. cols : columns of the resulting matrix; computed if not given. Examples ======== >>> from sympy import banded, ones, Matrix >>> from sympy.abc import x If explicit values are given in tuples, the matrix will autosize to contain all values, otherwise a single value is filled onto the entire diagonal: >>> banded({1: (1, 2, 3), -1: (4, 5, 6), 0: x}) Matrix([ [x, 1, 0, 0], [4, x, 2, 0], [0, 5, x, 3], [0, 0, 6, x]]) A function accepting a single argument can be used to fill the diagonal as a function of diagonal index (which starts at 0). The size (or shape) of the matrix must be given to obtain more than a 1x1 matrix: >>> s = lambda d: (1 + d)**2 >>> banded(5, {0: s, 2: s, -2: 2}) Matrix([ [1, 0, 1, 0, 0], [0, 4, 0, 4, 0], [2, 0, 9, 0, 9], [0, 2, 0, 16, 0], [0, 0, 2, 0, 25]]) The diagonal of matrices placed on a diagonal will coincide with the indicated diagonal: >>> vert = Matrix([1, 2, 3]) >>> banded({0: vert}, cols=3) Matrix([ [1, 0, 0], [2, 1, 0], [3, 2, 1], [0, 3, 2], [0, 0, 3]]) >>> banded(4, {0: ones(2)}) Matrix([ [1, 1, 0, 0], [1, 1, 0, 0], [0, 0, 1, 1], [0, 0, 1, 1]]) Errors are raised if the designated size will not hold all values an integral number of times. Here, the rows are designated as odd (but an even number is required to hold the off-diagonal 2x2 ones): >>> banded({0: 2, 1: ones(2)}, rows=5) Traceback (most recent call last): ... ValueError: sequence does not fit an integral number of times in the matrix And here, an even number of rows is given...but the square matrix has an even number of columns, too. As we saw in the previous example, an odd number is required: >>> banded(4, {0: 2, 1: ones(2)}) # trying to make 4x4 and cols must be odd Traceback (most recent call last): ... ValueError: sequence does not fit an integral number of times in the matrix A way around having to count rows is to enclosing matrix elements in a tuple and indicate the desired number of them to the right: >>> banded({0: 2, 2: (ones(2),)*3}) Matrix([ [2, 0, 1, 1, 0, 0, 0, 0], [0, 2, 1, 1, 0, 0, 0, 0], [0, 0, 2, 0, 1, 1, 0, 0], [0, 0, 0, 2, 1, 1, 0, 0], [0, 0, 0, 0, 2, 0, 1, 1], [0, 0, 0, 0, 0, 2, 1, 1]]) An error will be raised if more than one value is written to a given entry. Here, the ones overlap with the main diagonal if they are placed on the first diagonal: >>> banded({0: (2,)*5, 1: (ones(2),)*3}) Traceback (most recent call last): ... ValueError: collision at (1, 1) By placing a 0 at the bottom left of the 2x2 matrix of ones, the collision is avoided: >>> u2 = Matrix([ ... [1, 1], ... [0, 1]]) >>> banded({0: [2]*5, 1: [u2]*3}) Matrix([ [2, 1, 1, 0, 0, 0, 0], [0, 2, 1, 0, 0, 0, 0], [0, 0, 2, 1, 1, 0, 0], [0, 0, 0, 2, 1, 0, 0], [0, 0, 0, 0, 2, 1, 1], [0, 0, 0, 0, 0, 0, 1]]) """ from sympy import Dict, Dummy, SparseMatrix try: if len(args) not in (1, 2, 3): raise TypeError if not isinstance(args[-1], (dict, Dict)): raise TypeError if len(args) == 1: rows = kwargs.get("rows", None) cols = kwargs.get("cols", None) if rows is not None: rows = as_int(rows) if cols is not None: cols = as_int(cols) elif len(args) == 2: rows = cols = as_int(args[0]) else: rows, cols = map(as_int, args[:2]) # fails with ValueError if any keys are not ints _ = all(as_int(k) for k in args[-1]) except (ValueError, TypeError): raise TypeError( filldedent("""unrecognized input to banded: expecting [[row,] col,] {int: value}""")) def rc(d): # return row,col coord of diagonal start r = -d if d < 0 else 0 c = 0 if r else d return r, c smat = {} undone = [] tba = Dummy() # first handle objects with size for d, v in args[-1].items(): r, c = rc(d) # note: only list and tuple are recognized since this # will allow other Basic objects like Tuple # into the matrix if so desired if isinstance(v, (list, tuple)): extra = 0 for i, vi in enumerate(v): i += extra if is_sequence(vi): vi = SparseMatrix(vi) smat[r + i, c + i] = vi extra += min(vi.shape) - 1 else: smat[r + i, c + i] = vi elif is_sequence(v): v = SparseMatrix(v) rv, cv = v.shape if rows and cols: nr, xr = divmod(rows - r, rv) nc, xc = divmod(cols - c, cv) x = xr or xc do = min(nr, nc) elif rows: do, x = divmod(rows - r, rv) elif cols: do, x = divmod(cols - c, cv) else: do = 1 x = 0 if x: raise ValueError( filldedent(""" sequence does not fit an integral number of times in the matrix""")) j = min(v.shape) for i in range(do): smat[r, c] = v r += j c += j elif v: smat[r, c] = tba undone.append((d, v)) s = SparseMatrix(None, smat) # to expand matrices smat = s._smat # check for dim errors here if rows is not None and rows < s.rows: raise ValueError("Designated rows %s < needed %s" % (rows, s.rows)) if cols is not None and cols < s.cols: raise ValueError("Designated cols %s < needed %s" % (cols, s.cols)) if rows is cols is None: rows = s.rows cols = s.cols elif rows is not None and cols is None: cols = max(rows, s.cols) elif cols is not None and rows is None: rows = max(cols, s.rows) def update(i, j, v): # update smat and make sure there are # no collisions if v: if (i, j) in smat and smat[i, j] not in (tba, v): raise ValueError("collision at %s" % ((i, j), )) smat[i, j] = v if undone: for d, vi in undone: r, c = rc(d) v = vi if callable(vi) else lambda _: vi i = 0 while r + i < rows and c + i < cols: update(r + i, c + i, v(i)) i += 1 return SparseMatrix(rows, cols, smat)
def checkpdesol(pde, sol, func=None, solve_for_func=True): """ Checks if the given solution satisfies the partial differential equation. pde is the partial differential equation which can be given in the form of an equation or an expression. sol is the solution for which the pde is to be checked. This can also be given in an equation or an expression form. If the function is not provided, the helper function _preprocess from deutils is used to identify the function. If a sequence of solutions is passed, the same sort of container will be used to return the result for each solution. The following methods are currently being implemented to check if the solution satisfies the PDE: 1. Directly substitute the solution in the PDE and check. If the solution hasn't been solved for f, then it will solve for f provided solve_for_func hasn't been set to False. If the solution satisfies the PDE, then a tuple (True, 0) is returned. Otherwise a tuple (False, expr) where expr is the value obtained after substituting the solution in the PDE. However if a known solution returns False, it may be due to the inability of doit() to simplify it to zero. Examples ======== >>> from sympy import Function, symbols >>> from sympy.solvers.pde import checkpdesol, pdsolve >>> x, y = symbols('x y') >>> f = Function('f') >>> eq = 2*f(x,y) + 3*f(x,y).diff(x) + 4*f(x,y).diff(y) >>> sol = pdsolve(eq) >>> assert checkpdesol(eq, sol)[0] >>> eq = x*f(x,y) + f(x,y).diff(x) >>> checkpdesol(eq, sol) (False, (x*F(4*x - 3*y) - 6*F(4*x - 3*y)/25 + 4*Subs(Derivative(F(_xi_1), _xi_1), _xi_1, 4*x - 3*y))*exp(-6*x/25 - 8*y/25)) """ # Converting the pde into an equation if not isinstance(pde, Equality): pde = Eq(pde, 0) # If no function is given, try finding the function present. if func is None: try: _, func = _preprocess(pde.lhs) except ValueError: funcs = [ s.atoms(AppliedUndef) for s in (sol if is_sequence(sol, set) else [sol]) ] funcs = set().union(funcs) if len(funcs) != 1: raise ValueError( 'must pass func arg to checkpdesol for this case.') func = funcs.pop() # If the given solution is in the form of a list or a set # then return a list or set of tuples. if is_sequence(sol, set): return type(sol)([ checkpdesol(pde, i, func=func, solve_for_func=solve_for_func) for i in sol ]) # Convert solution into an equation if not isinstance(sol, Equality): sol = Eq(func, sol) elif sol.rhs == func: sol = sol.reversed # Try solving for the function solved = sol.lhs == func and not sol.rhs.has(func) if solve_for_func and not solved: solved = solve(sol, func) if solved: if len(solved) == 1: return checkpdesol(pde, Eq(func, solved[0]), func=func, solve_for_func=False) else: return checkpdesol(pde, [Eq(func, t) for t in solved], func=func, solve_for_func=False) # try direct substitution of the solution into the PDE and simplify if sol.lhs == func: pde = pde.lhs - pde.rhs s = simplify(pde.subs(func, sol.rhs).doit()) return s is S.Zero, s raise NotImplementedError( filldedent(''' Unable to test if %s is a solution to %s.''' % (sol, pde)))
def reduce_abs_inequality(expr, rel, gen): """Reduce an inequality with nested absolute values. Examples ======== >>> from sympy import Abs, Symbol >>> from sympy.solvers.inequalities import reduce_abs_inequality >>> x = Symbol('x', real=True) >>> reduce_abs_inequality(Abs(x - 5) - 3, '<', x) (2 < x) & (x < 8) >>> reduce_abs_inequality(Abs(x + 2)*3 - 13, '<', x) (-19/3 < x) & (x < 7/3) See Also ======== reduce_abs_inequalities """ if gen.is_extended_real is False: raise TypeError( filldedent(""" can't solve inequalities with absolute values containing non-real variables. """)) def _bottom_up_scan(expr): exprs = [] if expr.is_Add or expr.is_Mul: op = expr.func for arg in expr.args: _exprs = _bottom_up_scan(arg) if not exprs: exprs = _exprs else: args = [] for expr, conds in exprs: for _expr, _conds in _exprs: args.append((op(expr, _expr), conds + _conds)) exprs = args elif expr.is_Pow: n = expr.exp if not n.is_Integer: raise ValueError("Only Integer Powers are allowed on Abs.") _exprs = _bottom_up_scan(expr.base) for expr, conds in _exprs: exprs.append((expr**n, conds)) elif isinstance(expr, Abs): _exprs = _bottom_up_scan(expr.args[0]) for expr, conds in _exprs: exprs.append((expr, conds + [Ge(expr, 0)])) exprs.append((-expr, conds + [Lt(expr, 0)])) else: exprs = [(expr, [])] return exprs exprs = _bottom_up_scan(expr) mapping = {"<": ">", "<=": ">="} inequalities = [] for expr, conds in exprs: if rel not in mapping.keys(): expr = Relational(expr, 0, rel) else: expr = Relational(-expr, 0, mapping[rel]) inequalities.append([expr] + conds) return reduce_rational_inequalities(inequalities, gen)
def eval(cls, *_args): """Either return a modified version of the args or, if no modifications were made, return None. Modifications that are made here: 1) relationals are made canonical 2) any False conditions are dropped 3) any repeat of a previous condition is ignored 3) any args past one with a true condition are dropped If there are no args left, an empty Piecewise will be returned. If there is a single arg with a True condition, its corresponding expression will be returned. """ if not _args: return if len(_args) == 1 and _args[0][-1] == True: return _args[0][0] newargs = [] # the unevaluated conditions current_cond = set() # the conditions up to a given e, c pair # make conditions canonical args = [] for e, c in _args: if not c.is_Atom and not isinstance(c, Relational): free = c.free_symbols if len(free) == 1: funcs = [i for i in c.atoms(Function) if not isinstance(i, Boolean)] if len(funcs) == 1 and len( c.xreplace({list(funcs)[0]: Dummy()} ).free_symbols) == 1: # we can treat function like a symbol free = funcs _c = c x = free.pop() try: c = c.as_set().as_relational(x) except NotImplementedError: pass else: reps = {} for i in c.atoms(Relational): ic = i.canonical if ic.rhs in (S.Infinity, S.NegativeInfinity): if not _c.has(ic.rhs): # don't accept introduction of # new Relationals with +/-oo reps[i] = S.true elif ('=' not in ic.rel_op and c.xreplace({x: i.rhs}) != _c.xreplace({x: i.rhs})): reps[i] = Relational( i.lhs, i.rhs, i.rel_op + '=') c = c.xreplace(reps) args.append((e, _canonical(c))) for expr, cond in args: # Check here if expr is a Piecewise and collapse if one of # the conds in expr matches cond. This allows the collapsing # of Piecewise((Piecewise((x,x<0)),x<0)) to Piecewise((x,x<0)). # This is important when using piecewise_fold to simplify # multiple Piecewise instances having the same conds. # Eventually, this code should be able to collapse Piecewise's # having different intervals, but this will probably require # using the new assumptions. if isinstance(expr, Piecewise): unmatching = [] for i, (e, c) in enumerate(expr.args): if c in current_cond: # this would already have triggered continue if c == cond: if c != True: # nothing past this condition will ever # trigger and only those args before this # that didn't match a previous condition # could possibly trigger if unmatching: expr = Piecewise(*( unmatching + [(e, c)])) else: expr = e break else: unmatching.append((e, c)) # check for condition repeats got = False # -- if an And contains a condition that was # already encountered, then the And will be # False: if the previous condition was False # then the And will be False and if the previous # condition is True then then we wouldn't get to # this point. In either case, we can skip this condition. for i in ([cond] + (list(cond.args) if isinstance(cond, And) else [])): if i in current_cond: got = True break if got: continue # -- if not(c) is already in current_cond then c is # a redundant condition in an And. This does not # apply to Or, however: (e1, c), (e2, Or(~c, d)) # is not (e1, c), (e2, d) because if c and d are # both False this would give no results when the # true answer should be (e2, True) if isinstance(cond, And): nonredundant = [] for c in cond.args: if (isinstance(c, Relational) and (~c).canonical in current_cond): continue nonredundant.append(c) cond = cond.func(*nonredundant) elif isinstance(cond, Relational): if (~cond).canonical in current_cond: cond = S.true current_cond.add(cond) # collect successive e,c pairs when exprs or cond match if newargs: if newargs[-1].expr == expr: orcond = Or(cond, newargs[-1].cond) if isinstance(orcond, (And, Or)): orcond = distribute_and_over_or(orcond) newargs[-1] = ExprCondPair(expr, orcond) continue elif newargs[-1].cond == cond: orexpr = Or(expr, newargs[-1].expr) if isinstance(orexpr, (And, Or)): orexpr = distribute_and_over_or(orexpr) newargs[-1] == ExprCondPair(orexpr, cond) continue newargs.append(ExprCondPair(expr, cond)) # some conditions may have been redundant missing = len(newargs) != len(_args) # some conditions may have changed same = all(a == b for a, b in zip(newargs, _args)) # if either change happened we return the expr with the # updated args if not newargs: raise ValueError(filldedent(''' There are no conditions (or none that are not trivially false) to define an expression.''')) if missing or not same: return cls(*newargs)
def reduce_rational_inequalities(exprs, gen, relational=True): """Reduce a system of rational inequalities with rational coefficients. Examples ======== >>> from sympy import Poly, Symbol >>> from sympy.solvers.inequalities import reduce_rational_inequalities >>> x = Symbol('x', real=True) >>> reduce_rational_inequalities([[x**2 <= 0]], x) Eq(x, 0) >>> reduce_rational_inequalities([[x + 2 > 0]], x) -2 < x >>> reduce_rational_inequalities([[(x + 2, ">")]], x) -2 < x >>> reduce_rational_inequalities([[x + 2]], x) Eq(x, -2) This function find the non-infinite solution set so if the unknown symbol is declared as extended real rather than real then the result may include finiteness conditions: >>> y = Symbol('y', extended_real=True) >>> reduce_rational_inequalities([[y + 2 > 0]], y) (-2 < y) & (y < oo) """ exact = True eqs = [] solution = S.Reals if exprs else S.EmptySet for _exprs in exprs: _eqs = [] for expr in _exprs: if isinstance(expr, tuple): expr, rel = expr else: if expr.is_Relational: expr, rel = expr.lhs - expr.rhs, expr.rel_op else: expr, rel = expr, "==" if expr is S.true: numer, denom, rel = S.Zero, S.One, "==" elif expr is S.false: numer, denom, rel = S.One, S.One, "==" else: numer, denom = expr.together().as_numer_denom() try: (numer, denom), opt = parallel_poly_from_expr((numer, denom), gen) except PolynomialError: raise PolynomialError( filldedent(""" only polynomials and rational functions are supported in this context. """)) if not opt.domain.is_Exact: numer, denom, exact = numer.to_exact(), denom.to_exact(), False domain = opt.domain.get_exact() if not (domain.is_ZZ or domain.is_QQ): expr = numer / denom expr = Relational(expr, 0, rel) solution &= solve_univariate_inequality(expr, gen, relational=False) else: _eqs.append(((numer, denom), rel)) if _eqs: eqs.append(_eqs) if eqs: solution &= solve_rational_inequalities(eqs) exclude = solve_rational_inequalities([[((d, d.one), "==") for i in eqs for ((n, d), _) in i if d.has(gen)]]) solution -= exclude if not exact and solution: solution = solution.evalf() if relational: solution = solution.as_relational(gen) return solution
move(git_people, 79, 76, 'Kevin Goodsell') git_people.insert(84, "*Chu-Ching Huang <*****@*****.**>") move(git_people, 93, 92, 'James Pearson') # this will fail if the .mailmap is not right assert 'Sergey B Kirpichev' == author_name(git_people.pop(226) ), 'Sergey B Kirpichev was not found at line 226.' except AssertionError as msg: print(red(msg)) sys.exit(1) # define new lines for the file header = filldedent(""" All people who contributed to SymPy by sending at least a patch or more (in the order of the date of their first contribution), except those who explicitly didn't want to be mentioned. People with a * next to their names are not found in the metadata of the git history. This file is generated automatically by running `./bin/authors_update.py`. """).lstrip() fmt = """There are a total of {authors_count} authors.""" header_extra = fmt.format(authors_count=len(git_people)) lines = header.splitlines() lines.append('') lines.append(header_extra) lines.append('') lines.extend(git_people) # compare to old lines and stop if no changes were made old_lines = codecs.open(os.path.realpath(os.path.join( __file__, os.path.pardir, os.path.pardir, "AUTHORS")),
def __new__(cls, *args, **kwargs): self = object.__new__(cls) if len(args) == 1 and isinstance(args[0], SparseMatrix): self.rows = args[0].rows self.cols = args[0].cols self._smat = dict(args[0]._smat) return self self._smat = {} # autosizing if len(args) == 2 and args[0] is None: args = (None, ) + args if len(args) == 3: r, c = args[:2] if r is c is None: self.rows = self.cols = None elif None in (r, c): raise ValueError('Pass rows=None and no cols for autosizing.') else: self.rows, self.cols = map(as_int, args[:2]) if isinstance(args[2], Callable): op = args[2] for i in range(self.rows): for j in range(self.cols): value = self._sympify( op(self._sympify(i), self._sympify(j))) if value: self._smat[i, j] = value elif isinstance(args[2], (dict, Dict)): def update(i, j, v): # update self._smat and make sure there are # no collisions if v: if (i, j) in self._smat and v != self._smat[i, j]: raise ValueError('collision at %s' % ((i, j), )) self._smat[i, j] = v # manual copy, copy.deepcopy() doesn't work for key, v in args[2].items(): r, c = key if isinstance(v, SparseMatrix): for (i, j), vij in v._smat.items(): update(r + i, c + j, vij) else: if isinstance(v, (Matrix, list, tuple)): v = SparseMatrix(v) for i, j in v._smat: update(r + i, c + j, v[i, j]) else: v = self._sympify(v) update(r, c, self._sympify(v)) elif is_sequence(args[2]): flat = not any(is_sequence(i) for i in args[2]) if not flat: s = SparseMatrix(args[2]) self._smat = s._smat else: if len(args[2]) != self.rows * self.cols: raise ValueError( 'Flat list length (%s) != rows*columns (%s)' % (len(args[2]), self.rows * self.cols)) flat_list = args[2] for i in range(self.rows): for j in range(self.cols): value = self._sympify(flat_list[i * self.cols + j]) if value: self._smat[i, j] = value if self.rows is None: # autosizing k = self._smat.keys() self.rows = max([i[0] for i in k]) + 1 if k else 0 self.cols = max([i[1] for i in k]) + 1 if k else 0 else: for i, j in self._smat.keys(): if i and i >= self.rows or j and j >= self.cols: r, c = self.shape raise ValueError( filldedent(''' The location %s is out of designated range: %s''' % ((i, j), (r - 1, c - 1)))) else: if (len(args) == 1 and isinstance(args[0], (list, tuple))): # list of values or lists v = args[0] c = 0 for i, row in enumerate(v): if not isinstance(row, (list, tuple)): row = [row] for j, vij in enumerate(row): if vij: self._smat[i, j] = self._sympify(vij) c = max(c, len(row)) self.rows = len(v) if c else 0 self.cols = c else: # handle full matrix forms with _handle_creation_inputs r, c, _list = Matrix._handle_creation_inputs(*args) self.rows = r self.cols = c for i in range(self.rows): for j in range(self.cols): value = _list[self.cols * i + j] if value: self._smat[i, j] = value return self
def __new__(cls, *args, **kwargs): evaluate = kwargs.get('evaluate', global_evaluate[0]) on_morph = kwargs.get('on_morph', 'ignore') # unpack into coords coords = args[0] if len(args) == 1 else args # check args and handle quickly handle Point instances if isinstance(coords, Point): # even if we're mutating the dimension of a point, we # don't reevaluate its coordinates evaluate = False if len(coords) == kwargs.get('dim', len(coords)): return coords if not is_sequence(coords): raise TypeError( filldedent(''' Expecting sequence of coordinates, not `{}`'''.format( func_name(coords)))) # A point where only `dim` is specified is initialized # to zeros. if len(coords) == 0 and kwargs.get('dim', None): coords = (S.Zero, ) * kwargs.get('dim') coords = Tuple(*coords) dim = kwargs.get('dim', len(coords)) if len(coords) < 2: raise ValueError( filldedent(''' Point requires 2 or more coordinates or keyword `dim` > 1.''')) if len(coords) != dim: message = ("Dimension of {} needs to be changed " "from {} to {}.").format(coords, len(coords), dim) if on_morph == 'ignore': pass elif on_morph == "error": raise ValueError(message) elif on_morph == 'warn': warnings.warn(message) else: raise ValueError( filldedent(''' on_morph value should be 'error', 'warn' or 'ignore'.''')) if any(coords[dim:]): raise ValueError('Nonzero coordinates cannot be removed.') if any(a.is_number and im(a) for a in coords): raise ValueError('Imaginary coordinates are not permitted.') if not all(isinstance(a, Expr) for a in coords): raise TypeError('Coordinates must be valid SymPy expressions.') # pad with zeros appropriately coords = coords[:dim] + (S.Zero, ) * (dim - len(coords)) # Turn any Floats into rationals and simplify # any expressions before we instantiate if evaluate: coords = coords.xreplace( dict([(f, simplify(nsimplify(f, rational=True))) for f in coords.atoms(Float)])) # return 2D or 3D instances if len(coords) == 2: kwargs['_nocheck'] = True return Point2D(*coords, **kwargs) elif len(coords) == 3: kwargs['_nocheck'] = True return Point3D(*coords, **kwargs) # the general Point return GeometryEntity.__new__(cls, *coords)
def linodesolve_type(A, t, b=None): r""" Helper function that determines the type of the system of ODEs for solving with :obj:`sympy.solvers.ode.systems.linodesolve()` Explanation =========== This function takes in the coefficient matrix and/or the non-homogeneous term and returns the type of the equation that can be solved by :obj:`sympy.solvers.ode.systems.linodesolve()`. If the system is constant coefficient homogeneous, then "type1" is returned If the system is constant coefficient non-homogeneous, then "type2" is returned If the system is non-constant coefficient homogeneous, then "type3" is returned If the system is non-constnt coefficient non-homogeneous, then "type4" is returned Note that, if the system of ODEs is of "type3" or "type4", then along with the type, the commutative antiderivative of the coefficient matrix is also returned. If the system cannot be solved by :obj:`sympy.solvers.ode.systems.linodesolve()`, then NotImplementedError is raised. Parameters ========== A : Matrix Coefficient matrix of the system of ODEs b : Matrix or None Non-homogeneous term of the system. The default value is None. If this argument is None, then the system is assumed to be homogeneous. Examples ======== >>> from sympy import symbols, Matrix >>> from sympy.solvers.ode.systems import linodesolve_type >>> t = symbols("t") >>> A = Matrix([[1, 1], [2, 3]]) >>> b = Matrix([t, 1]) >>> linodesolve_type(A, t) {'antiderivative': None, 'type': 'type1'} >>> linodesolve_type(A, t, b=b) {'antiderivative': None, 'type': 'type2'} >>> A_t = Matrix([[1, t], [-t, 1]]) >>> linodesolve_type(A_t, t) {'antiderivative': Matrix([ [ t, t**2/2], [-t**2/2, t]]), 'type': 'type3'} >>> linodesolve_type(A_t, t, b=b) {'antiderivative': Matrix([ [ t, t**2/2], [-t**2/2, t]]), 'type': 'type4'} >>> A_non_commutative = Matrix([[1, t], [t, -1]]) >>> linodesolve_type(A_non_commutative, t) Traceback (most recent call last): ... NotImplementedError: The system doesn't have a commutative antiderivative, it can't be solved by linodesolve. Returns ======= Dict Raises ====== NotImplementedError When the coefficient matrix doesn't have a commutative antiderivative See Also ======== linodesolve: Function for which linodesolve_type gets the information """ is_non_constant = not _matrix_is_constant(A, t) is_non_homogeneous = not (b is None or b.is_zero_matrix) type = "type{}".format( int("{}{}".format(int(is_non_constant), int(is_non_homogeneous)), 2) + 1) B = None if is_non_constant: B, is_commuting = _is_commutative_anti_derivative(A, t) if not is_commuting: raise NotImplementedError( filldedent(''' The system doesn't have a commutative antiderivative, it can't be solved by linodesolve. ''')) return {"type": type, "antiderivative": B}
def parse_expr(s, local_dict=None, transformations=standard_transformations, global_dict=None, evaluate=True): """Converts the string ``s`` to a SymPy expression, in ``local_dict`` Parameters ========== s : str The string to parse. local_dict : dict, optional A dictionary of local variables to use when parsing. global_dict : dict, optional A dictionary of global variables. By default, this is initialized with ``from sympy import *``; provide this parameter to override this behavior (for instance, to parse ``"Q & S"``). transformations : tuple, optional A tuple of transformation functions used to modify the tokens of the parsed expression before evaluation. The default transformations convert numeric literals into their SymPy equivalents, convert undefined variables into SymPy symbols, and allow the use of standard mathematical factorial notation (e.g. ``x!``). evaluate : bool, optional When False, the order of the arguments will remain as they were in the string and automatic simplification that would normally occur is suppressed. (see examples) Examples ======== >>> from sympy.parsing.sympy_parser import parse_expr >>> parse_expr("1/2") 1/2 >>> type(_) <class 'sympy.core.numbers.Half'> >>> from sympy.parsing.sympy_parser import standard_transformations,\\ ... implicit_multiplication_application >>> transformations = (standard_transformations + ... (implicit_multiplication_application,)) >>> parse_expr("2x", transformations=transformations) 2*x When evaluate=False, some automatic simplifications will not occur: >>> parse_expr("2**3"), parse_expr("2**3", evaluate=False) (8, 2**3) In addition the order of the arguments will not be made canonical. This feature allows one to tell exactly how the expression was entered: >>> a = parse_expr('1 + x', evaluate=False) >>> b = parse_expr('x + 1', evaluate=0) >>> a == b False >>> a.args (1, x) >>> b.args (x, 1) See Also ======== stringify_expr, eval_expr, standard_transformations, implicit_multiplication_application """ if local_dict is None: local_dict = {} elif not isinstance(local_dict, dict): raise TypeError('expecting local_dict to be a dict') if global_dict is None: global_dict = {} exec('from sympy import *', global_dict) elif not isinstance(global_dict, dict): raise TypeError('expecting global_dict to be a dict') transformations = transformations or () if transformations: if not iterable(transformations): raise TypeError('`transformations` should be a list of functions.') for _ in transformations: if not callable(_): raise TypeError( filldedent(''' expected a function in `transformations`, not %s''' % func_name(_))) if arity(_) != 3: raise TypeError( filldedent(''' a transformation should be function that takes 3 arguments''')) code = stringify_expr(s, local_dict, global_dict, transformations) if not evaluate: code = compile(evaluateFalse(code), '<string>', 'eval') try: rv = eval_expr(code, local_dict, global_dict) # restore neutral definitions for names for i in local_dict.pop(None, ()): local_dict[i] = None return rv except Exception as e: # restore neutral definitions for names for i in local_dict.pop(None, ()): local_dict[i] = None raise e from ValueError( f"Error from parse_expr with transformed code: {code!r}")
def __init__(self, other, want): msg = filldedent("Expected an instance of %s, but received object " "'%s' of %s." % (type(want), other, type(other))) super(VectorTypeError, self).__init__(msg)
def __new__(cls, *args): from sympy.functions.elementary.integers import ceiling if len(args) == 1: if isinstance(args[0], range if PY3 else xrange): 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: 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)): if start == stop: null = True else: end = stop 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: end = start + 1 step = S.One # make it a canonical single step else: end = start + n * step if null: start = end = S.Zero step = S.One return Basic.__new__(cls, start, end, step)
def nonsymfail(cond): raise NotImplementedError(filldedent(''' A condition not involving %s appeared: %s''' % (sym, cond)))
def parse_expr(s: str, local_dict: Optional[DICT] = None, transformations: tUnion[tTuple[TRANS, ...], str] \ = standard_transformations, global_dict: Optional[DICT] = None, evaluate=True): """Converts the string ``s`` to a SymPy expression, in ``local_dict`` Parameters ========== s : str The string to parse. local_dict : dict, optional A dictionary of local variables to use when parsing. global_dict : dict, optional A dictionary of global variables. By default, this is initialized with ``from sympy import *``; provide this parameter to override this behavior (for instance, to parse ``"Q & S"``). transformations : tuple or str A tuple of transformation functions used to modify the tokens of the parsed expression before evaluation. The default transformations convert numeric literals into their SymPy equivalents, convert undefined variables into SymPy symbols, and allow the use of standard mathematical factorial notation (e.g. ``x!``). Selection via string is available (see below). evaluate : bool, optional When False, the order of the arguments will remain as they were in the string and automatic simplification that would normally occur is suppressed. (see examples) Examples ======== >>> from sympy.parsing.sympy_parser import parse_expr >>> parse_expr("1/2") 1/2 >>> type(_) <class 'sympy.core.numbers.Half'> >>> from sympy.parsing.sympy_parser import standard_transformations,\\ ... implicit_multiplication_application >>> transformations = (standard_transformations + ... (implicit_multiplication_application,)) >>> parse_expr("2x", transformations=transformations) 2*x When evaluate=False, some automatic simplifications will not occur: >>> parse_expr("2**3"), parse_expr("2**3", evaluate=False) (8, 2**3) In addition the order of the arguments will not be made canonical. This feature allows one to tell exactly how the expression was entered: >>> a = parse_expr('1 + x', evaluate=False) >>> b = parse_expr('x + 1', evaluate=0) >>> a == b False >>> a.args (1, x) >>> b.args (x, 1) Note, however, that when these expressions are printed they will appear the same: >>> assert str(a) == str(b) As a convenience, transformations can be seen by printing ``transformations``: >>> from sympy.parsing.sympy_parser import transformations >>> print(transformations) 0: lambda_notation 1: auto_symbol 2: repeated_decimals 3: auto_number 4: factorial_notation 5: implicit_multiplication_application 6: convert_xor 7: implicit_application 8: implicit_multiplication 9: convert_equals_signs 10: function_exponentiation 11: rationalize The ``T`` object provides a way to select these transformations: >>> from sympy.parsing.sympy_parser import T If you print it, you will see the same list as shown above. >>> str(T) == str(transformations) True Standard slicing will return a tuple of transformations: >>> T[:5] == standard_transformations True So ``T`` can be used to specify the parsing transformations: >>> parse_expr("2x", transformations=T[:5]) Traceback (most recent call last): ... SyntaxError: invalid syntax >>> parse_expr("2x", transformations=T[:6]) 2*x >>> parse_expr('.3', transformations=T[3, 11]) 3/10 >>> parse_expr('.3x', transformations=T[:]) 3*x/10 As a further convenience, strings 'implicit' and 'all' can be used to select 0-5 and all the transformations, respectively. >>> parse_expr('.3x', transformations='all') 3*x/10 See Also ======== stringify_expr, eval_expr, standard_transformations, implicit_multiplication_application """ if local_dict is None: local_dict = {} elif not isinstance(local_dict, dict): raise TypeError('expecting local_dict to be a dict') elif null in local_dict: raise ValueError('cannot use "" in local_dict') if global_dict is None: global_dict = {} exec('from sympy import *', global_dict) builtins_dict = vars(builtins) for name, obj in builtins_dict.items(): if isinstance(obj, types.BuiltinFunctionType): global_dict[name] = obj global_dict['max'] = Max global_dict['min'] = Min elif not isinstance(global_dict, dict): raise TypeError('expecting global_dict to be a dict') transformations = transformations or () if isinstance(transformations, str): if transformations == 'all': _transformations = T[:] elif transformations == 'implicit': _transformations = T[:6] else: raise ValueError('unknown transformation group name') else: _transformations = transformations if _transformations: if not iterable(_transformations): raise TypeError('`transformations` should be a list of functions.') for _ in _transformations: if not callable(_): raise TypeError( filldedent(''' expected a function in `transformations`, not %s''' % func_name(_))) if arity(_) != 3: raise TypeError( filldedent(''' a transformation should be function that takes 3 arguments''')) code = stringify_expr(s, local_dict, global_dict, _transformations) if not evaluate: code = compile(evaluateFalse(code), '<string>', 'eval') try: rv = eval_expr(code, local_dict, global_dict) # restore neutral definitions for names for i in local_dict.pop(null, ()): local_dict[i] = null return rv except Exception as e: # restore neutral definitions for names for i in local_dict.pop(null, ()): local_dict[i] = null raise e from ValueError( f"Error from parse_expr with transformed code: {code!r}")
def reduce_rational_inequalities(exprs, gen, relational=True): """Reduce a system of rational inequalities with rational coefficients. Examples ======== >>> from sympy import Poly, Symbol >>> from sympy.solvers.inequalities import reduce_rational_inequalities >>> x = Symbol('x', real=True) >>> reduce_rational_inequalities([[x**2 <= 0]], x) Eq(x, 0) >>> reduce_rational_inequalities([[x + 2 > 0]], x) (-2 < x) & (x < oo) >>> reduce_rational_inequalities([[(x + 2, ">")]], x) (-2 < x) & (x < oo) >>> reduce_rational_inequalities([[x + 2]], x) Eq(x, -2) """ exact = True eqs = [] solution = S.Reals if exprs else S.EmptySet for _exprs in exprs: _eqs = [] for expr in _exprs: if isinstance(expr, tuple): expr, rel = expr else: if expr.is_Relational: expr, rel = expr.lhs - expr.rhs, expr.rel_op else: expr, rel = expr, '==' if expr is S.true: numer, denom, rel = S.Zero, S.One, '==' elif expr is S.false: numer, denom, rel = S.One, S.One, '==' else: numer, denom = expr.together().as_numer_denom() try: (numer, denom), opt = parallel_poly_from_expr( (numer, denom), gen) except PolynomialError: raise PolynomialError(filldedent(''' only polynomials and rational functions are supported in this context''')) if not opt.domain.is_Exact: numer, denom, exact = numer.to_exact(), denom.to_exact(), False domain = opt.domain.get_exact() if not (domain.is_ZZ or domain.is_QQ): expr = numer/denom expr = Relational(expr, 0, rel) solution &= solve_univariate_inequality(expr, gen, relational=False) else: _eqs.append(((numer, denom), rel)) if _eqs: eqs.append(_eqs) if eqs: solution &= solve_rational_inequalities(eqs) if not exact: solution = solution.evalf() if relational: solution = solution.as_relational(gen) return solution
def _contains(self, other): from sympy.matrices import Matrix from sympy.solvers.solveset import solveset, linsolve from sympy.utilities.iterables import is_sequence, iterable, cartes L = self.lamda if is_sequence(other): if not is_sequence(L.expr): return S.false if len(L.expr) != len(other): raise ValueError( filldedent(''' Dimensions of other and output of Lambda are different.''')) elif iterable(other): raise ValueError( filldedent(''' `other` should be an ordered object like a Tuple.''')) solns = None if self._is_multivariate(): if not is_sequence(L.expr): # exprs -> (numer, denom) and check again # XXX this is a bad idea -- make the user # remap self to desired form return other.as_numer_denom() in self.func( Lambda(L.variables, L.expr.as_numer_denom()), self.base_set) eqs = [expr - val for val, expr in zip(other, L.expr)] variables = L.variables free = set(variables) if all(i.is_number for i in list(Matrix(eqs).jacobian(variables))): solns = list( linsolve([e - val for e, val in zip(L.expr, other)], variables)) else: syms = [e.free_symbols & free for e in eqs] solns = {} for i, (e, s, v) in enumerate(zip(eqs, syms, other)): if not s: if e != v: return S.false solns[vars[i]] = [v] continue elif len(s) == 1: sy = s.pop() sol = solveset(e, sy) if sol is S.EmptySet: return S.false elif isinstance(sol, FiniteSet): solns[sy] = list(sol) else: raise NotImplementedError else: raise NotImplementedError solns = cartes(*[solns[s] for s in variables]) else: x = L.variables[0] if isinstance(L.expr, Expr): # scalar -> scalar mapping solnsSet = solveset(L.expr - other, x) if solnsSet.is_FiniteSet: solns = list(solnsSet) else: msgset = solnsSet else: # scalar -> vector for e, o in zip(L.expr, other): solns = solveset(e - o, x) if solns is S.EmptySet: return S.false for soln in solns: try: if soln in self.base_set: break # check next pair except TypeError: if self.base_set.contains(soln.evalf()): break else: return S.false # never broke so there was no True return S.true if solns is None: raise NotImplementedError( filldedent(''' Determining whether %s contains %s has not been implemented.''' % (msgset, other))) for soln in solns: try: if soln in self.base_set: return S.true except TypeError: return self.base_set.contains(soln.evalf()) return S.false
def sample(expr, condition=None, size=(), library='scipy', numsamples=1, **kwargs): """ A realization of the random expression Parameters ========== expr : Expression of random variables Expression from which sample is extracted condition : Expr containing RandomSymbols A conditional expression size : int, tuple Represents size of each sample in numsamples library : str - 'scipy' : Sample using scipy - 'numpy' : Sample using numpy - 'pymc3' : Sample using PyMC3 Choose any of the available options to sample from as string, by default is 'scipy' numsamples : int Number of samples, each with size as ``size`` Examples ======== >>> from sympy.stats import Die, sample, Normal >>> X, Y, Z = Die('X', 6), Die('Y', 6), Die('Z', 6) >>> die_roll = sample(X + Y + Z) # doctest: +SKIP >>> N = Normal('N', 3, 4) >>> samp = next(sample(N)) # doctest: +SKIP >>> samp in N.pspace.domain.set # doctest: +SKIP True >>> samp = next(sample(N, N>0)) # doctest: +SKIP >>> samp > 0 # doctest: +SKIP True >>> samp_list = next(sample(N, size=4)) # doctest: +SKIP >>> [sam in N.pspace.domain.set for sam in samp_list] # doctest: +SKIP [True, True, True, True] Returns ======= sample: iterator object iterator object containing the sample/samples of given expr """ message = ("The return type of sample has been changed to return an " "iterator object since version 1.7. For more information see " "https://github.com/sympy/sympy/issues/19061") warnings.warn(filldedent(message)) return sample_iter(expr, condition, size=size, library=library, numsamples=numsamples)