def __getitem__(self, key): """Return portion of self defined by key. If the key involves a slice then a list will be returned (if key is a single slice) or a matrix (if key was a tuple involving a slice). Examples ======== >>> from sympy import Matrix, I >>> m = Matrix([ ... [1, 2 + I], ... [3, 4 ]]) If the key is a tuple that doesn't involve a slice then that element is returned: >>> m[1, 0] 3 When a tuple key involves a slice, a matrix is returned. Here, the first column is selected (all rows, column 0): >>> m[:, 0] Matrix([ [1], [3]]) If the slice is not a tuple then it selects from the underlying list of elements that are arranged in row order and a list is returned if a slice is involved: >>> m[0] 1 >>> m[::2] [1, 3] """ if isinstance(key, tuple): i, j = key try: i, j = self.key2ij(key) return self._mat[i*self.cols + j] except (TypeError, IndexError): if isinstance(i, slice): i = range(self.rows)[i] elif is_sequence(i): pass else: i = [i] if isinstance(j, slice): j = range(self.cols)[j] elif is_sequence(j): pass else: j = [j] return self.extract(i, j) else: # row-wise decomposition of matrix if isinstance(key, slice): return self._mat[key] return self._mat[a2idx(key)]
def __new__(cls, *args, **kw_args): """ Constructor for the Permutation object. Examples ======== >>> from sympy.combinatorics.permutations import Permutation >>> p = Permutation([0,1,2]) >>> p Permutation([0, 1, 2]) >>> q = Permutation([[0,1],[2]]) >>> q Permutation([[0, 1], [2]]) """ if not args or not is_sequence(args[0]) or len(args) > 1 or \ len(set(is_sequence(a) for a in args[0])) > 1: raise ValueError('Permutation argument must be a list of ints or a list of lists.') # 0, 1, ..., n-1 should all be present temp = [int(i) for i in flatten(args[0])] if set(range(len(temp))) != set(temp): raise ValueError("Integers 0 through %s must be present." % len(temp)) cform = aform = None if args[0] and is_sequence(args[0][0]): cform = [list(a) for a in args[0]] else: aform = list(args[0]) ret_obj = Basic.__new__(cls, (cform or aform), **kw_args) ret_obj._cyclic_form, ret_obj._array_form = cform, aform return ret_obj
def __new__(cls, periodical, limits=None): x, start, stop = None, None, None if limits is None: x, start, stop = Dummy("k"), 0, S.Infinity if is_sequence(limits, Tuple): if len(limits) == 3: x, start, stop = limits elif len(limits) == 2: x = Dummy("k") start, stop = limits if not isinstance(x, Symbol) or start is None or stop is None: raise ValueError("Invalid limits given: %s" % str(limits)) if start is S.NegativeInfinity and stop is S.Infinity: raise ValueError("Both the start and end value" " cannot be unbounded") limits = sympify((x, start, stop)) if is_sequence(periodical, Tuple): periodical = sympify(tuple(flatten(periodical))) else: raise ValueError("invalid period %s should be something " "like e.g (1, 2) " % periodical) if Interval(limits[1], limits[2]) is S.EmptySet: return S.EmptySequence return Basic.__new__(cls, periodical, limits)
def __getitem__(self, key): if isinstance(key, tuple): i, j = key try: i, j = self.key2ij(key) return self._smat.get((i, j), S.Zero) except (TypeError, IndexError): if isinstance(i, slice): i = range(self.rows)[i] elif is_sequence(i): pass else: i = [i] if isinstance(j, slice): j = range(self.cols)[j] elif is_sequence(j): pass else: j = [j] return self.extract(i, j) # check for single arg, like M[:] or M[3] if isinstance(key, slice): lo, hi = key.indices(len(self))[:2] L = [] for i in range(lo, hi): m, n = divmod(i, self.cols) L.append(self._smat.get((m, n), S.Zero)) return L i, j = divmod(a2idx(key, len(self)), self.cols) return self._smat.get((i, j), S.Zero)
def __new__(cls, function, limits): fun = sympify(function) if not is_sequence(fun) or len(fun) != 2: raise ValueError("Function argument should be (x(t), y(t)) but got %s" % str(function)) if not is_sequence(limits) or len(limits) != 3: raise ValueError("Limit argument should be (t, tmin, tmax) but got %s" % str(limits)) return GeometryEntity.__new__(cls, tuple(sympify(fun)), tuple(sympify(limits)))
def _eval_subs(self, old, new): from sympy.geometry.point import Point if is_sequence(old) or is_sequence(new): old = Point(old) new = Point(new) return self._subs(old, new)
def test_iterable_is_sequence(): ordered = [list(), tuple(), Tuple(), Matrix([[]])] unordered = [set()] not_sympy_iterable = [{}, '', u('')] assert all(is_sequence(i) for i in ordered) assert all(not is_sequence(i) for i in unordered) assert all(iterable(i) for i in ordered + unordered) assert all(not iterable(i) for i in not_sympy_iterable) assert all(iterable(i, exclude=None) for i in not_sympy_iterable)
def _eval_subs(self, old, new): from sympy.geometry.point import Point, Point3D if is_sequence(old) or is_sequence(new): if isinstance(self, Point3D): old = Point3D(old) new = Point3D(new) else: old = Point(old) new = Point(new) return self._subs(old, new)
def copyin_list(self, key, value): """Copy in elements from a list. Parameters ========== key : slice The section of this matrix to replace. value : iterable The iterable to copy values from. Examples ======== >>> from sympy.matrices import eye >>> I = eye(3) >>> I[:2, 0] = [1, 2] # col >>> I [1, 0, 0] [2, 1, 0] [0, 0, 1] >>> I[1, :2] = [[3, 4]] >>> I [1, 0, 0] [3, 4, 0] [0, 0, 1] See Also ======== copyin_matrix """ if not is_sequence(value): raise TypeError("`value` must be an ordered iterable, not %s." % type(value)) return self.copyin_matrix(key, Matrix(value))
def hessian(f, varlist): """Compute Hessian matrix for a function f see: http://en.wikipedia.org/wiki/Hessian_matrix See Also ======== sympy.matrices.mutable.Matrix.jacobian wronskian """ # f is the expression representing a function f, return regular matrix if is_sequence(varlist): m = len(varlist) if not m: raise ShapeError("`len(varlist)` must not be zero.") elif isinstance(varlist, MatrixBase): m = varlist.cols if not m: raise ShapeError("`varlist.cols` must not be zero.") if varlist.rows != 1: raise ShapeError("`varlist` must be a row vector.") else: raise ValueError("Improper variable list in hessian function") if not getattr(f, 'diff'): # check differentiability raise ValueError("Function `f` (%s) is not differentiable" % f) out = zeros(m) for i in range(m): for j in range(i, m): out[i, j] = f.diff(varlist[i]).diff(varlist[j]) for i in range(m): for j in range(i): out[i, j] = out[j, i] return out
def mplot2d(f, var, show=True): """ Plot a 2d function using matplotlib/Tk. """ import warnings warnings.filterwarnings("ignore", "Could not match \S") p = import_module('pylab') if not p: sys.exit("Matplotlib is required to use mplot2d.") if not is_sequence(f): f = [f,] for f_i in f: x, y = sample2d(f_i, var) p.plot(x, y,'black') p.draw() p.ylabel("Transverse beam cordinate") p.xlabel("Propagation coordinate") p.grid(True) if show: p.show()
def distance(self, o): """ Finds the shortest distance between a line segment and a point. Raises ====== NotImplementedError is raised if o is not a Point Examples ======== >>> from sympy import Point, Segment >>> p1, p2 = Point(0, 1), Point(3, 4) >>> s = Segment(p1, p2) >>> s.distance(Point(10, 15)) sqrt(170) >>> s.distance((0, 12)) sqrt(73) """ if is_sequence(o): o = Point(o) if isinstance(o, Point): seg_vector = self.p2 - self.p1 pt_vector = o - self.p1 t = seg_vector.dot(pt_vector)/self.length**2 if t >= 1: distance = Point.distance(self.p2, o) elif t <= 0: distance = Point.distance(self.p1, o) else: distance = Point.distance( self.p1 + Point(t*seg_vector.x, t*seg_vector.y), o) return distance raise NotImplementedError()
def convert(self, element, base=None): """Convert ``element`` to ``self.dtype``. """ if base is not None: return self.convert_from(element, base) if self.of_type(element): return element if isinstance(element, SYMPY_INTS): return self.new(element) if isinstance(element, DomainElement): return self.convert_from(element, element.parent()) # TODO: implement this in from_ methods if self.is_Numerical and getattr(element, 'is_ground', False): return self.convert(element.LC()) if not is_sequence(element): try: element = sympify(element) if isinstance(element, Basic): return self.from_sympy(element) except (TypeError, ValueError): pass raise CoercionFailed("can't convert %s of type %s to %s" % (element, type(element), self))
def distance(self, o): """ Finds the shortest distance between a line and a point. Raises ====== NotImplementedError is raised if o is not a Point Examples ======== >>> from sympy import Point, Line >>> p1, p2 = Point(0, 0), Point(1, 1) >>> s = Line(p1, p2) >>> s.distance(Point(-1, 1)) sqrt(2) >>> s.distance((-1, 2)) 3*sqrt(2)/2 """ if not isinstance(o, Point): if is_sequence(o): o = Point(o) a, b, c = self.coefficients if 0 in (a, b): return self.perpendicular_segment(o).length m = self.slope x = o.x y = m*x - c/b return abs(factor_terms(o.y - y))/sqrt(1 + m**2)
def contains(self, o): """Return True if o is on this Line, or False otherwise.""" if is_sequence(o): o = Point3D(o) if isinstance(o, Point3D): if self.arbitrary_point == 0: return True else: o = o.func(*[simplify(i) for i in o.args]) eq = self.equation() a = [] for i in range(3): k = eq[i].subs(eq[i].free_symbols.pop(), o.args[i]) if k != nan: a.append(k) if len(set(a)) == 1: return True else: return False elif not isinstance(o, LinearEntity3D): return False elif isinstance(o, Line3D): return self.__eq__(o) else: return o.p1 in self and o.p2 in self and o.p3 in self
def distance(self, o): """ Finds the shortest distance between the ray and a point. Raises ====== NotImplementedError is raised if o is not a Point Examples ======== >>> from sympy import Point, Ray >>> p1, p2 = Point(0, 0), Point(1, 1) >>> s = Ray(p1, p2) >>> s.distance(Point(-1, -1)) sqrt(2) >>> s.distance((-1, 2)) 3*sqrt(2)/2 """ if not isinstance(o, Point): if is_sequence(o): o = Point(o) s = self.perpendicular_segment(o) if isinstance(s, Point): if self.contains(s): return S.Zero else: # since arg-order is arbitrary, find the non-o point non_o = s.p1 if s.p1 != o else s.p2 if self.contains(non_o): return Line(self).distance(o) # = s.length but simpler # the following applies when neither of the above apply return self.source.distance(o)
def distance(self, o): """ Finds the shortest distance between a line and a point. Raises ====== NotImplementedError is raised if o is not an instance of Point3D Examples ======== >>> from sympy import Point3D, Line3D >>> p1, p2 = Point3D(0, 0, 0), Point3D(1, 1, 1) >>> s = Line3D(p1, p2) >>> s.distance(Point3D(-1, 1, 1)) 2*sqrt(6)/3 >>> s.distance((-1, 1, 1)) 2*sqrt(6)/3 """ if not isinstance(o, Point3D): if is_sequence(o): o = Point3D(o) if o in self: return S.Zero a = self.perpendicular_segment(o).length return a
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 distance(self, o): """ Finds the shortest distance between the ray and a point. Raises ====== NotImplementedError is raised if o is not a Point Examples ======== >>> from sympy import Point3D, Ray3D >>> p1, p2 = Point3D(0, 0, 0), Point3D(1, 1, 2) >>> s = Ray3D(p1, p2) >>> s.distance(Point3D(-1, -1, 2)) sqrt(6) >>> s.distance((-1, -1, 2)) sqrt(6) """ if not isinstance(o, Point3D): if is_sequence(o): o = Point3D(o) if o in self: return S.Zero s = self.perpendicular_segment(o) if not isinstance(s, Point3D): non_o = s.p1 if s.p1 == o else s.p2 if self.contains(non_o): return Line3D(self).distance(o) # = s.length but simpler # the following applies when neither of the above apply return self.source.distance(o)
def extract_leading_order(self, symbols, point=None): """ Returns the leading term and it's order. Examples ======== >>> from sympy.abc import x >>> (x + 1 + 1/x**5).extract_leading_order(x) ((x**(-5), O(x**(-5))),) >>> (1 + x).extract_leading_order(x) ((1, O(1)),) >>> (x + x**2).extract_leading_order(x) ((x, O(x)),) """ lst = [] symbols = list(symbols if is_sequence(symbols) else [symbols]) if not point: point = [0]*len(symbols) seq = [(f, C.Order(f, *zip(symbols, point))) for f in self.args] for ef, of in seq: for e, o in lst: if o.contains(of) and o != of: of = None break if of is None: continue new_lst = [(ef, of)] for e, o in lst: if of.contains(o) and o != of: continue new_lst.append((e, o)) lst = new_lst return tuple(lst)
def contains(self, other): """ Is the other GeometryEntity contained within this Segment? Examples ======== >>> from sympy import Point3D, Segment3D >>> p1, p2 = Point3D(0, 1, 1), Point3D(3, 4, 5) >>> s = Segment3D(p1, p2) >>> s2 = Segment3D(p2, p1) >>> s.contains(s2) True """ if is_sequence(other): other = Point3D(other) if isinstance(other, Segment3D): return other.p1 in self and other.p2 in self elif isinstance(other, Point3D): if Point3D.are_collinear(self.p1, self.p2, other): if other.distance(self.p1) + other.distance(self.p2) == self.length: return True else: return False return False
def ones(r, c=None): """Returns a matrix of ones with ``r`` rows and ``c`` columns; if ``c`` is omitted a square matrix will be returned. See Also ======== zeros eye diag """ from dense import Matrix if is_sequence(r): SymPyDeprecationWarning( feature="The syntax ones([%i, %i])" % tuple(r), useinstead="ones(%i, %i)." % tuple(r), issue=3381, deprecated_since_version="0.7.2", ).warn() r, c = r else: c = r if c is None else c r = as_int(r) c = as_int(c) return Matrix(r, c, [S.One]*r*c)
def __new__(cls, label, shape=None, **kw_args): from sympy import MatrixBase, NDimArray if isinstance(label, string_types): label = Symbol(label) elif isinstance(label, Symbol): pass elif isinstance(label, (MatrixBase, NDimArray)): return label elif isinstance(label, Iterable): return _sympify(label) else: label = _sympify(label) if is_sequence(shape): shape = Tuple(*shape) elif shape is not None: shape = Tuple(shape) offset = kw_args.pop('offset', S.Zero) strides = kw_args.pop('strides', None) if shape is not None: obj = Expr.__new__(cls, label, shape) else: obj = Expr.__new__(cls, label) obj._shape = shape obj._offset = offset obj._strides = strides obj._name = str(label) return obj
def __new__(cls, term, *symbols, **assumptions): term = sympify(term) if term is S.NaN: return S.NaN if len(symbols) == 1: symbol = symbols[0] if isinstance(symbol, C.Equality): k = symbol.lhs a = symbol.rhs.start n = symbol.rhs.end elif is_sequence(symbol): k, a, n = symbol else: raise ValueError("Invalid arguments") k, a, n = map(sympify, (k, a, n)) else: raise NotImplementedError obj = Expr.__new__(cls, **assumptions) obj._args = (term, Tuple(k, a, n)) return obj
def flatten(iterable, levels=None, cls=None): """ Recursively denest iterable containers. >>> from sympy.utilities.iterables import flatten >>> flatten([1, 2, 3]) [1, 2, 3] >>> flatten([1, 2, [3]]) [1, 2, 3] >>> flatten([1, [2, 3], [4, 5]]) [1, 2, 3, 4, 5] >>> flatten([1.0, 2, (1, None)]) [1.0, 2, 1, None] If you want to denest only a specified number of levels of nested containers, then set ``levels`` flag to the desired number of levels:: >>> ls = [[(-2, -1), (1, 2)], [(0, 0)]] >>> flatten(ls, levels=1) [(-2, -1), (1, 2), (0, 0)] If cls argument is specified, it will only flatten instances of that class, for example: >>> from sympy.core import Basic >>> class MyOp(Basic): ... pass ... >>> flatten([MyOp(1, MyOp(2, 3))], cls=MyOp) [1, 2, 3] adapted from http://kogs-www.informatik.uni-hamburg.de/~meine/python_tricks """ if levels is not None: if not levels: return iterable elif levels > 0: levels -= 1 else: raise ValueError("expected non-negative number of levels, got %s" % levels) if cls is None: reducible = lambda x: is_sequence(x, set) else: reducible = lambda x: isinstance(x, cls) result = [] for el in iterable: if reducible(el): if hasattr(el, 'args'): el = el.args result.extend(flatten(el, levels=levels, cls=cls)) else: result.append(el) return result
def _interpret_args(args): interval_wrong_order = "PlotInterval %s was given before any function(s)." interpret_error = "Could not interpret %s as a function or interval." functions, intervals = [], [] if isinstance(args[0], GeometryEntity): for coords in list(args[0].arbitrary_point()): functions.append(coords) intervals.append(PlotInterval.try_parse(args[0].plot_interval())) else: for a in args: i = PlotInterval.try_parse(a) if i is not None: if len(functions) == 0: raise ValueError(interval_wrong_order % (str(i))) else: intervals.append(i) else: if is_sequence(a, include=str): raise ValueError(interpret_error % (str(a))) try: f = sympify(a) functions.append(f) except: raise ValueError(interpret_error % str(a)) return functions, intervals
def __new__(cls, label, range=None, **kw_args): 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): assert len(range) == 2, "Idx got range tuple with wrong length" 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(S.Zero, range-S.One) elif range: raise TypeError("range must be ordered iterable or integer sympy expression") else: args = label, obj = Expr.__new__(cls, *args, **kw_args) return obj
def idiff(eq, y, x, n=1): """Return ``dy/dx`` assuming that ``eq == 0``. Parameters ========== y : the dependent variable or a list of dependent variables (with y first) x : the variable that the derivative is being taken with respect to n : the order of the derivative (default is 1) Examples ======== >>> from sympy.abc import x, y, a >>> from sympy.geometry.util import idiff >>> circ = x**2 + y**2 - 4 >>> idiff(circ, y, x) -x/y >>> idiff(circ, y, x, 2).simplify() -(x**2 + y**2)/y**3 Here, ``a`` is assumed to be independent of ``x``: >>> idiff(x + a + y, y, x) -1 Now the x-dependence of ``a`` is made explicit by listing ``a`` after ``y`` in a list. >>> idiff(x + a + y, [y, a], x) -Derivative(a, x) - 1 See Also ======== sympy.core.function.Derivative: represents unevaluated derivatives sympy.core.function.diff: explicitly differentiates wrt symbols """ if is_sequence(y): dep = set(y) y = y[0] elif isinstance(y, Symbol): dep = set([y]) else: raise ValueError("expecting x-dependent symbol(s) but got: %s" % y) f = dict([(s, Function( s.name)(x)) for s in eq.atoms(Symbol) if s != x and s in dep]) dydx = Function(y.name)(x).diff(x) eq = eq.subs(f) derivs = {} for i in range(n): yp = solve(eq.diff(x), dydx)[0].subs(derivs) if i == n - 1: return yp.subs([(v, k) for k, v in f.items()]) derivs[dydx] = yp eq = dydx - yp dydx = dydx.diff(x)
def __setitem__(self, i, args): """ Parses and adds a PlotMode to the function list. """ if not (isinstance(i, (int, Integer)) and i >= 0): raise ValueError("Function index must " "be an integer >= 0.") if isinstance(args, PlotObject): f = args else: if (not is_sequence(args)) or isinstance(args, GeometryEntity): args = [args] if len(args) == 0: return # no arguments given kwargs = dict(bounds_callback=self.adjust_all_bounds) f = PlotMode(*args, **kwargs) if f: self._render_lock.acquire() self._functions[i] = f self._render_lock.release() else: raise ValueError("Failed to parse '%s'." % ', '.join(str(a) for a in args))
def sequence(seq, limits=None): """Returns appropriate sequence object. If seq is a sympy sequence, returns SeqPer object otherwise returns SeqFormula object Examples ======== >>> from sympy import sequence, SeqPer, SeqFormula >>> from sympy.abc import n >>> sequence(n**2, (n, 0, 5)) SeqFormula(n**2, (n, 0, 5)) >>> sequence((1, 2, 3), (n, 0, 5)) SeqPer((1, 2, 3), (n, 0, 5)) See Also ======== sympy.series.sequences.SeqPer sympy.series.sequences.SeqFormula """ seq = sympify(seq) if is_sequence(seq, Tuple): return SeqPer(seq, limits) else: return SeqFormula(seq, limits)
def diag(*values, **kwargs): """Create a sparse, diagonal matrix from a list of diagonal values. Notes ===== When arguments are matrices they are fitted in resultant matrix. The returned matrix is a mutable, dense matrix. To make it a different type, send the desired class for keyword ``cls``. Examples ======== >>> from sympy.matrices import diag, Matrix, ones >>> diag(1, 2, 3) [1, 0, 0] [0, 2, 0] [0, 0, 3] >>> diag(*[1, 2, 3]) [1, 0, 0] [0, 2, 0] [0, 0, 3] The diagonal elements can be matrices; diagonal filling will continue on the diagonal from the last element of the matrix: >>> from sympy.abc import x, y, z >>> a = Matrix([x, y, z]) >>> b = Matrix([[1, 2], [3, 4]]) >>> c = Matrix([[5, 6]]) >>> diag(a, 7, b, c) [x, 0, 0, 0, 0, 0] [y, 0, 0, 0, 0, 0] [z, 0, 0, 0, 0, 0] [0, 7, 0, 0, 0, 0] [0, 0, 1, 2, 0, 0] [0, 0, 3, 4, 0, 0] [0, 0, 0, 0, 5, 6] When diagonal elements are lists, they will be treated as arguments to Matrix: >>> diag([1, 2, 3], 4) [1, 0] [2, 0] [3, 0] [0, 4] >>> diag([[1, 2, 3]], 4) [1, 2, 3, 0] [0, 0, 0, 4] A given band off the diagonal can be made by padding with a vertical or horizontal "kerning" vector: >>> hpad = ones(0, 2) >>> vpad = ones(2, 0) >>> diag(vpad, 1, 2, 3, hpad) + diag(hpad, 4, 5, 6, vpad) [0, 0, 4, 0, 0] [0, 0, 0, 5, 0] [1, 0, 0, 0, 6] [0, 2, 0, 0, 0] [0, 0, 3, 0, 0] The type is mutable by default but can be made immutable by setting the ``mutable`` flag to False: >>> type(diag(1)) <class 'sympy.matrices.dense.MutableDenseMatrix'> >>> from sympy.matrices import ImmutableMatrix >>> type(diag(1, cls=ImmutableMatrix)) <class 'sympy.matrices.immutable.ImmutableMatrix'> See Also ======== eye """ from sparse import MutableSparseMatrix cls = kwargs.pop('cls', None) if cls is None: from dense import Matrix as cls if kwargs: raise ValueError( 'unrecognized keyword%s: %s' % ('s' if len(kwargs) > 1 else '', ', '.join(kwargs.keys()))) rows = 0 cols = 0 values = list(values) for i in range(len(values)): m = values[i] if isinstance(m, MatrixBase): rows += m.rows cols += m.cols elif is_sequence(m): m = values[i] = Matrix(m) rows += m.rows cols += m.cols else: rows += 1 cols += 1 res = MutableSparseMatrix.zeros(rows, cols) i_row = 0 i_col = 0 for m in values: if isinstance(m, MatrixBase): res[i_row:i_row + m.rows, i_col:i_col + m.cols] = m i_row += m.rows i_col += m.cols else: res[i_row, i_col] = m i_row += 1 i_col += 1 return cls._new(res)
def copyin_list(self, key, value): if not is_sequence(value): raise TypeError("`value` must be of type list or tuple.") self.copyin_matrix(key, Matrix(value))
def __getitem__(self, key): """Return portion of self defined by key. If the key involves a slice then a list will be returned (if key is a single slice) or a matrix (if key was a tuple involving a slice). Examples ======== >>> from sympy import Matrix, I >>> m = Matrix([ ... [1, 2 + I], ... [3, 4 ]]) If the key is a tuple that doesn't involve a slice then that element is returned: >>> m[1, 0] 3 When a tuple key involves a slice, a matrix is returned. Here, the first column is selected (all rows, column 0): >>> m[:, 0] Matrix([ [1], [3]]) If the slice is not a tuple then it selects from the underlying list of elements that are arranged in row order and a list is returned if a slice is involved: >>> m[0] 1 >>> m[::2] [1, 3] """ if isinstance(key, tuple): i, j = key try: i, j = self.key2ij(key) return self._mat[i * self.cols + j] except (TypeError, IndexError): if (isinstance(i, Expr) and not i.is_number) or (isinstance(j, Expr) and not j.is_number): if ((j < 0) is True) or ((j >= self.shape[1]) is True) or\ ((i < 0) is True) or ((i >= self.shape[0]) is True): raise ValueError("index out of boundary") from sympy.matrices.expressions.matexpr import MatrixElement return MatrixElement(self, i, j) if isinstance(i, slice): i = range(self.rows)[i] elif is_sequence(i): pass else: i = [i] if isinstance(j, slice): j = range(self.cols)[j] elif is_sequence(j): pass else: j = [j] return self.extract(i, j) else: # row-wise decomposition of matrix if isinstance(key, slice): return self._mat[key] return self._mat[a2idx(key)]
def hessian(f, varlist, constraints=[]): """Compute Hessian matrix for a function f wrt parameters in varlist which may be given as a sequence or a row/column vector. A list of constraints may optionally be given. Examples ======== >>> from sympy import Function, hessian, pprint >>> from sympy.abc import x, y >>> f = Function('f')(x, y) >>> g1 = Function('g')(x, y) >>> g2 = x**2 + 3*y >>> pprint(hessian(f, (x, y), [g1, g2])) [ d d ] [ 0 0 --(g(x, y)) --(g(x, y)) ] [ dx dy ] [ ] [ 0 0 2*x 3 ] [ ] [ 2 2 ] [d d d ] [--(g(x, y)) 2*x ---(f(x, y)) -----(f(x, y))] [dx 2 dy dx ] [ dx ] [ ] [ 2 2 ] [d d d ] [--(g(x, y)) 3 -----(f(x, y)) ---(f(x, y)) ] [dy dy dx 2 ] [ dy ] References ========== https://en.wikipedia.org/wiki/Hessian_matrix See Also ======== sympy.matrices.matrices.MatrixCalculus.jacobian wronskian """ # f is the expression representing a function f, return regular matrix if isinstance(varlist, MatrixBase): if 1 not in varlist.shape: raise ShapeError("`varlist` must be a column or row vector.") if varlist.cols == 1: varlist = varlist.T varlist = varlist.tolist()[0] if is_sequence(varlist): n = len(varlist) if not n: raise ShapeError("`len(varlist)` must not be zero.") else: raise ValueError("Improper variable list in hessian function") if not getattr(f, 'diff'): # check differentiability raise ValueError("Function `f` (%s) is not differentiable" % f) m = len(constraints) N = m + n out = zeros(N) for k, g in enumerate(constraints): if not getattr(g, 'diff'): # check differentiability raise ValueError("Function `f` (%s) is not differentiable" % f) for i in range(n): out[k, i + m] = g.diff(varlist[i]) for i in range(n): for j in range(i, n): out[i + m, j + m] = f.diff(varlist[i]).diff(varlist[j]) for i in range(N): for j in range(i + 1, N): out[j, i] = out[i, j] return out
def __init__(self, *args, **kwargs): # initialize style parameter style = kwargs.pop('style', '').lower() # allow alias kwargs to override style kwarg if kwargs.pop('none', None) is not None: style = 'none' if kwargs.pop('frame', None) is not None: style = 'frame' if kwargs.pop('box', None) is not None: style = 'box' if kwargs.pop('ordinate', None) is not None: style = 'ordinate' if style in ['', 'ordinate']: self._render_object = PlotAxesOrdinate(self) elif style in ['frame', 'box']: self._render_object = PlotAxesFrame(self) elif style in ['none']: self._render_object = None else: raise ValueError(("Unrecognized axes style %s.") % (style)) # initialize stride parameter stride = kwargs.pop('stride', 0.25) try: stride = eval(stride) except TypeError: pass if is_sequence(stride): if len(stride) != 3: raise ValueError("length should be equal to 3") self._stride = stride else: self._stride = [stride, stride, stride] self._tick_length = float(kwargs.pop('tick_length', 0.1)) # setup bounding box and ticks self._origin = [0, 0, 0] self.reset_bounding_box() def flexible_boolean(input, default): if input in [True, False]: return input if input in ['f', 'F', 'false', 'False']: return False if input in ['t', 'T', 'true', 'True']: return True return default # initialize remaining parameters self.visible = flexible_boolean(kwargs.pop('visible', ''), True) self._overlay = flexible_boolean(kwargs.pop('overlay', ''), True) self._colored = flexible_boolean(kwargs.pop('colored', ''), False) self._label_axes = flexible_boolean( kwargs.pop('label_axes', ''), False) self._label_ticks = flexible_boolean( kwargs.pop('label_ticks', ''), True) # setup label font self.font_face = kwargs.pop('font_face', 'Arial') self.font_size = kwargs.pop('font_size', 28) # this is also used to reinit the # font on window close/reopen self.reset_resources()
def __init__(self, *args, style='', none=None, frame=None, box=None, ordinate=None, stride=0.25, visible='', overlay='', colored='', label_axes='', label_ticks='', tick_length=0.1, font_face='Arial', font_size=28, **kwargs): # initialize style parameter style = style.lower() # allow alias kwargs to override style kwarg if none is not None: style = 'none' if frame is not None: style = 'frame' if box is not None: style = 'box' if ordinate is not None: style = 'ordinate' if style in ['', 'ordinate']: self._render_object = PlotAxesOrdinate(self) elif style in ['frame', 'box']: self._render_object = PlotAxesFrame(self) elif style in ['none']: self._render_object = None else: raise ValueError(("Unrecognized axes style %s.") % (style)) # initialize stride parameter try: stride = eval(stride) except TypeError: pass if is_sequence(stride): if len(stride) != 3: raise ValueError("length should be equal to 3") self._stride = stride else: self._stride = [stride, stride, stride] self._tick_length = float(tick_length) # setup bounding box and ticks self._origin = [0, 0, 0] self.reset_bounding_box() def flexible_boolean(input, default): if input in [True, False]: return input if input in ['f', 'F', 'false', 'False']: return False if input in ['t', 'T', 'true', 'True']: return True return default # initialize remaining parameters self.visible = flexible_boolean(kwargs, True) self._overlay = flexible_boolean(overlay, True) self._colored = flexible_boolean(colored, False) self._label_axes = flexible_boolean(label_axes, False) self._label_ticks = flexible_boolean(label_ticks, True) # setup label font self.font_face = font_face self.font_size = font_size # this is also used to reinit the # font on window close/reopen self.reset_resources()
def normalize(m): if is_sequence(m) and not isinstance(m, MatrixBase): return Matrix(m) return m
def gosper_sum(f, k): r""" Gosper's hypergeometric summation algorithm. Given a hypergeometric term ``f`` such that: .. math :: s_n = \sum_{k=0}^{n-1} f_k and `f(n)` doesn't depend on `n`, returns `g_{n} - g(0)` where `g_{n+1} - g_n = f_n`, or ``None`` if `s_n` can not be expressed in closed form as a sum of hypergeometric terms. Examples ======== >>> from sympy.concrete.gosper import gosper_sum >>> from sympy.functions import factorial >>> from sympy.abc import i, n, k >>> f = (4*k + 1)*factorial(k)/factorial(2*k + 1) >>> gosper_sum(f, (k, 0, n)) (-factorial(n) + 2*factorial(2*n + 1))/factorial(2*n + 1) >>> _.subs(n, 2) == sum(f.subs(k, i) for i in [0, 1, 2]) True >>> gosper_sum(f, (k, 3, n)) (-60*factorial(n) + factorial(2*n + 1))/(60*factorial(2*n + 1)) >>> _.subs(n, 5) == sum(f.subs(k, i) for i in [3, 4, 5]) True References ========== .. [1] Marko Petkovsek, Herbert S. Wilf, Doron Zeilberger, A = B, AK Peters, Ltd., Wellesley, MA, USA, 1997, pp. 73--100 """ indefinite = False if is_sequence(k): k, a, b = k else: indefinite = True g = gosper_term(f, k) if g is None: return None if indefinite: result = f * g else: result = (f * (g + 1)).subs(k, b) - (f * g).subs(k, a) if result is S.NaN: try: result = (f * (g + 1)).limit(k, b) - (f * g).limit(k, a) except NotImplementedError: result = None return factor(result)
def is_seq_and_not_point(a): # we cannot use isinstance(a, Point) since we cannot import Point if hasattr(a, 'is_Point') and a.is_Point: return False return is_sequence(a)
def __getitem__(self, key): """Return portion of self defined by key. If the key involves a slice then a list will be returned (if key is a single slice) or a matrix (if key was a tuple involving a slice). Examples ======== >>> from sympy import Matrix, I >>> m = Matrix([ ... [1, 2 + I], ... [3, 4 ]]) If the key is a tuple that doesn't involve a slice then that element is returned: >>> m[1, 0] 3 When a tuple key involves a slice, a matrix is returned. Here, the first column is selected (all rows, column 0): >>> m[:, 0] Matrix([ [1], [3]]) If the slice is not a tuple then it selects from the underlying list of elements that are arranged in row order and a list is returned if a slice is involved: >>> m[0] 1 >>> m[::2] [1, 3] """ if isinstance(key, tuple): i, j = key try: i, j = self.key2ij(key) return self._mat[i * self.cols + j] except (TypeError, IndexError): if isinstance(i, slice): # XXX remove list() when PY2 support is dropped i = list(range(self.rows))[i] elif is_sequence(i): pass else: i = [i] if isinstance(j, slice): # XXX remove list() when PY2 support is dropped j = list(range(self.cols))[j] elif is_sequence(j): pass else: j = [j] return self.extract(i, j) else: # row-wise decomposition of matrix if isinstance(key, slice): return self._mat[key] return self._mat[a2idx(key)]
def gcd_terms(terms, isprimitive=False, clear=True, fraction=True): """Compute the GCD of ``terms`` and put them together. ``terms`` can be an expression or a non-Basic sequence of expressions which will be handled as though they are terms from a sum. If ``isprimitive`` is True the _gcd_terms will not run the primitive method on the terms. ``clear`` controls the removal of integers from the denominator of an Add expression. When True (default), all numerical denominator will be cleared; when False the denominators will be cleared only if all terms had numerical denominators other than 1. ``fraction``, when True (default), will put the expression over a common denominator. Examples ======== >>> from sympy.core import gcd_terms >>> from sympy.abc import x, y >>> gcd_terms((x + 1)**2*y + (x + 1)*y**2) y*(x + 1)*(x + y + 1) >>> gcd_terms(x/2 + 1) (x + 2)/2 >>> gcd_terms(x/2 + 1, clear=False) x/2 + 1 >>> gcd_terms(x/2 + y/2, clear=False) (x + y)/2 >>> gcd_terms(x/2 + 1/x) (x**2 + 2)/(2*x) >>> gcd_terms(x/2 + 1/x, fraction=False) (x + 2/x)/2 >>> gcd_terms(x/2 + 1/x, fraction=False, clear=False) x/2 + 1/x >>> gcd_terms(x/2/y + 1/x/y) (x**2 + 2)/(2*x*y) >>> gcd_terms(x/2/y + 1/x/y, clear=False) (x**2/2 + 1)/(x*y) >>> gcd_terms(x/2/y + 1/x/y, clear=False, fraction=False) (x/2 + 1/x)/y The ``clear`` flag was ignored in this case because the returned expression was a rational expression, not a simple sum. See Also ======== factor_terms, sympy.polys.polytools.terms_gcd """ def mask(terms): """replace nc portions of each term with a unique Dummy symbols and return the replacements to restore them""" args = [(a, []) if a.is_commutative else a.args_cnc() for a in terms] reps = [] for i, (c, nc) in enumerate(args): if nc: nc = Mul._from_args(nc) d = Dummy() reps.append((d, nc)) c.append(d) args[i] = Mul._from_args(c) else: args[i] = c return args, dict(reps) isadd = isinstance(terms, Add) addlike = isadd or not isinstance(terms, Basic) and \ is_sequence(terms, include=set) and \ not isinstance(terms, Dict) if addlike: if isadd: # i.e. an Add terms = list(terms.args) else: terms = sympify(terms) terms, reps = mask(terms) cont, numer, denom = _gcd_terms(terms, isprimitive, fraction) numer = numer.xreplace(reps) coeff, factors = cont.as_coeff_Mul() if not clear: c, _coeff = coeff.as_coeff_Mul() if not c.is_Integer and not clear and numer.is_Add: n, d = c.as_numer_denom() _numer = numer / d if any(a.as_coeff_Mul()[0].is_Integer for a in _numer.args): numer = _numer coeff = n * _coeff return _keep_coeff(coeff, factors * numer / denom, clear=clear) if not isinstance(terms, Basic): return terms if terms.is_Atom: return terms if terms.is_Mul: c, args = terms.as_coeff_mul() return _keep_coeff( c, Mul(*[gcd_terms(i, isprimitive, clear, fraction) for i in args]), clear=clear) def handle(a): # don't treat internal args like terms of an Add if not isinstance(a, Expr): if isinstance(a, Basic): return a.func(*[handle(i) for i in a.args]) return type(a)([handle(i) for i in a]) return gcd_terms(a, isprimitive, clear, fraction) if isinstance(terms, Dict): return Dict(*[(k, handle(v)) for k, v in terms.args]) return terms.func(*[handle(i) for i in terms.args])
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 __init__(self, name, expr, argument_sequence=None): """Initialize a Routine instance. ``name`` A string with the name of this routine in the generated code ``expr`` The sympy expression that the Routine instance will represent. If given a list or tuple of expressions, the routine will be considered to have multiple return values. ``argument_sequence`` Optional list/tuple containing arguments for the routine in a preferred order. If omitted, arguments will be ordered alphabetically, but with all input aguments first, and then output or in-out arguments. A decision about whether to use output arguments or return values, is made depending on the mathematical expressions. For an expression of type Equality, the left hand side is made into an OutputArgument (or an InOutArgument if appropriate). Else, the calculated expression is the return values of the routine. A tuple of exressions can be used to create a routine with both return value(s) and output argument(s). """ arg_list = [] if is_sequence(expr): if not expr: raise ValueError("No expression given") expressions = Tuple(*expr) else: expressions = Tuple(expr) # local variables local_vars = set([i.label for i in expressions.atoms(Idx)]) # symbols that should be arguments symbols = expressions.atoms(Symbol) - local_vars # Decide whether to use output argument or return value return_val = [] output_args = [] for expr in expressions: if isinstance(expr, Equality): out_arg = expr.lhs expr = expr.rhs if isinstance(out_arg, Indexed): dims = tuple([ (S.Zero, dim - 1) for dim in out_arg.shape]) symbol = out_arg.base.label elif isinstance(out_arg, Symbol): dims = [] symbol = out_arg else: raise CodeGenError( "Only Indexed or Symbol can define output arguments") if expr.has(symbol): output_args.append( InOutArgument(symbol, out_arg, expr, dimensions=dims)) else: output_args.append(OutputArgument( symbol, out_arg, expr, dimensions=dims)) # avoid duplicate arguments symbols.remove(symbol) else: return_val.append(Result(expr)) # setup input argument list array_symbols = {} for array in expressions.atoms(Indexed): array_symbols[array.base.label] = array for symbol in sorted(symbols, key=str): if symbol in array_symbols: dims = [] array = array_symbols[symbol] for dim in array.shape: dims.append((S.Zero, dim - 1)) metadata = {'dimensions': dims} else: metadata = {} arg_list.append(InputArgument(symbol, **metadata)) output_args.sort(key=lambda x: str(x.name)) arg_list.extend(output_args) if argument_sequence is not None: # if the user has supplied IndexedBase instances, we'll accept that new_sequence = [] for arg in argument_sequence: if isinstance(arg, IndexedBase): new_sequence.append(arg.label) else: new_sequence.append(arg) argument_sequence = new_sequence missing = [x for x in arg_list if x.name not in argument_sequence] if missing: raise CodeGenArgumentListError("Argument list didn't specify: %s" % ", ".join([str(m.name) for m in missing]), missing) # create redundant arguments to produce the requested sequence name_arg_dict = dict([(x.name, x) for x in arg_list]) new_args = [] for symbol in argument_sequence: try: new_args.append(name_arg_dict[symbol]) except KeyError: new_args.append(InputArgument(symbol)) arg_list = new_args self.name = name self.arguments = arg_list self.results = return_val self.local_vars = local_vars
def dot(self, p): """Return dot product of self with another Point.""" if not is_sequence(p): p = Point(p) # raise the error via Point return Add(*(a * b for a, b in zip(self, p)))
def uniquely_named_symbol(xname, exprs=(), compare=str, modify=None, **assumptions): """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 appending numbers (default) but this can be customized with the keyword 'modify'. Parameters ========== xname : a string or a Symbol (when symbol xname <- str(xname)) compare : a single arg function that takes a symbol and returns a string to be compared with xname (the default is the str function which indicates how the name will look when it is printed, e.g. this includes underscores that appear on Dummy symbols) modify : a single arg function that changes its string argument in some way (the default is to append numbers) Examples ======== >>> from sympy.core.symbol import uniquely_named_symbol >>> from sympy.abc import x >>> uniquely_named_symbol('x', x) x0 """ from sympy.core.function import AppliedUndef def numbered_string_incr(s, start=0): if not s: return str(start) i = len(s) - 1 while i != -1: if not s[i].isdigit(): break i -= 1 n = str(int(s[i + 1:] or start - 1) + 1) return s[:i + 1] + n default = None if is_sequence(xname): xname, default = xname x = str(xname) if not exprs: return _symbol(x, default, **assumptions) if not is_sequence(exprs): exprs = [exprs] names = set().union( [i.name for e in exprs for i in e.atoms(Symbol)] + [i.func.name for e in exprs for i in e.atoms(AppliedUndef)]) if modify is None: modify = numbered_string_incr while any(x == compare(s) for s in names): x = modify(x) return _symbol(x, default, **assumptions)
def itermonomials(variables, max_degrees, min_degrees=None): r""" `max_degrees` and `min_degrees` are either both integers or both lists. Unless otherwise specified, `min_degrees` is either 0 or [0,...,0]. A generator of all monomials `monom` is returned, such that either min_degree <= total_degree(monom) <= max_degree, or min_degrees[i] <= degree_list(monom)[i] <= max_degrees[i], for all i. Case I:: `max_degrees` and `min_degrees` are both integers. =========================================================== Given a set of variables `V` and a min_degree `N` and a max_degree `M` generate a set of monomials of degree less than or equal to `N` and greater than or equal to `M`. The total number of monomials in commutative variables is huge and is given by the following formula if `M = 0`: .. math:: \frac{(\#V + N)!}{\#V! N!} For example if we would like to generate a dense polynomial of a total degree `N = 50` and `M = 0`, which is the worst case, in 5 variables, assuming that exponents and all of coefficients are 32-bit long and stored in an array we would need almost 80 GiB of memory! Fortunately most polynomials, that we will encounter, are sparse. Examples ======== Consider monomials in commutative variables `x` and `y` and non-commutative variables `a` and `b`:: >>> from sympy import symbols >>> from sympy.polys.monomials import itermonomials >>> from sympy.polys.orderings import monomial_key >>> from sympy.abc import x, y >>> sorted(itermonomials([x, y], 2), key=monomial_key('grlex', [y, x])) [1, x, y, x**2, x*y, y**2] >>> sorted(itermonomials([x, y], 3), key=monomial_key('grlex', [y, x])) [1, x, y, x**2, x*y, y**2, x**3, x**2*y, x*y**2, y**3] >>> a, b = symbols('a, b', commutative=False) >>> set(itermonomials([a, b, x], 2)) {1, a, a**2, b, b**2, x, x**2, a*b, b*a, x*a, x*b} >>> sorted(itermonomials([x, y], 2, 1), key=monomial_key('grlex', [y, x])) [x, y, x**2, x*y, y**2] Case II:: `max_degrees` and `min_degrees` are both lists. ========================================================= If max_degrees = [d_1, ..., d_n] and min_degrees = [e_1, ..., e_n], the number of monomials generated is: (d_1 - e_1 + 1) * ... * (d_n - e_n + 1) Example ======= Let us generate all monomials `monom` in variables `x`, and `y` such that [1, 2][i] <= degree_list(monom)[i] <= [2, 4][i], i = 0, 1 :: >>> from sympy import symbols >>> from sympy.polys.monomials import itermonomials >>> from sympy.polys.orderings import monomial_key >>> from itertools import product >>> from sympy.core import Mul >>> from sympy.abc import x, y >>> sorted(itermonomials([x, y], [2, 4], [1, 2]), reverse=True, key=monomial_key('lex', [x, y])) [x**2*y**4, x**2*y**3, x**2*y**2, x*y**4, x*y**3, x*y**2] """ n = len(variables) if is_sequence(max_degrees): if len(max_degrees) != n: raise ValueError('Argument sizes do not match') if min_degrees is None: min_degrees = [0] * n elif not is_sequence(min_degrees): raise ValueError('min_degrees is not a list') else: if len(min_degrees) != n: raise ValueError('Argument sizes do not match') if any(i < 0 for i in min_degrees): raise ValueError("min_degrees can't contain negative numbers") total_degree = False else: max_degree = max_degrees if max_degree < 0: raise ValueError("max_degrees can't be negative") if min_degrees is None: min_degree = 0 else: if min_degrees < 0: raise ValueError("min_degrees can't be negative") min_degree = min_degrees total_degree = True if total_degree: if min_degree > max_degree: return if not variables or max_degree == 0: yield S.One return # Force to list in case of passed tuple or other incompatible collection variables = list(variables) + [S.One] if all(variable.is_commutative for variable in variables): monomials_list_comm = [] for item in combinations_with_replacement(variables, max_degree): powers = dict() for variable in variables: powers[variable] = 0 for variable in item: if variable != 1: powers[variable] += 1 if max(powers.values()) >= min_degree: monomials_list_comm.append(Mul(*item)) for mon in set(monomials_list_comm): yield mon else: monomials_list_non_comm = [] for item in product(variables, repeat=max_degree): powers = dict() for variable in variables: powers[variable] = 0 for variable in item: if variable != 1: powers[variable] += 1 if max(powers.values()) >= min_degree: monomials_list_non_comm.append(Mul(*item)) for mon in set(monomials_list_non_comm): yield mon else: if any(min_degrees[i] > max_degrees[i] for i in range(n)): raise ValueError( 'min_degrees[i] must be <= max_degrees[i] for all i') power_lists = [] for var, min_d, max_d in zip(variables, min_degrees, max_degrees): power_lists.append([var**i for i in range(min_d, max_d + 1)]) for powers in product(*power_lists): yield Mul(*powers)
def refraction_angle(incident, medium1, medium2, normal=None, plane=None): """ This function calculates transmitted vector after refraction at planar surface. `medium1` and `medium2` can be `Medium` or any sympifiable object. If `incident` is an object of `Ray3D`, `normal` also has to be an instance of `Ray3D` in order to get the output as a `Ray3D`. Please note that if plane of separation is not provided and normal is an instance of `Ray3D`, normal will be assumed to be intersecting incident ray at the plane of separation. This will not be the case when `normal` is a `Matrix` or any other sequence. If `incident` is an instance of `Ray3D` and `plane` has not been provided and `normal` is not `Ray3D`, output will be a `Matrix`. Parameters ========== incident : Matrix, Ray3D, or sequence Incident vector medium1 : sympy.physics.optics.medium.Medium or sympifiable Medium 1 or its refractive index medium2 : sympy.physics.optics.medium.Medium or sympifiable Medium 2 or its refractive index normal : Matrix, Ray3D, or sequence Normal vector plane : Plane Plane of separation of the two media. Examples ======== >>> from sympy.physics.optics import refraction_angle >>> from sympy.geometry import Point3D, Ray3D, Plane >>> from sympy.matrices import Matrix >>> from sympy import symbols >>> n = Matrix([0, 0, 1]) >>> P = Plane(Point3D(0, 0, 0), normal_vector=[0, 0, 1]) >>> r1 = Ray3D(Point3D(-1, -1, 1), Point3D(0, 0, 0)) >>> refraction_angle(r1, 1, 1, n) Matrix([ [ 1], [ 1], [-1]]) >>> refraction_angle(r1, 1, 1, plane=P) Ray3D(Point3D(0, 0, 0), Point3D(1, 1, -1)) With different index of refraction of the two media >>> n1, n2 = symbols('n1, n2') >>> refraction_angle(r1, n1, n2, n) Matrix([ [ n1/n2], [ n1/n2], [-sqrt(3)*sqrt(-2*n1**2/(3*n2**2) + 1)]]) >>> refraction_angle(r1, n1, n2, plane=P) Ray3D(Point3D(0, 0, 0), Point3D(n1/n2, n1/n2, -sqrt(3)*sqrt(-2*n1**2/(3*n2**2) + 1))) """ # A flag to check whether to return Ray3D or not return_ray = False if plane is not None and normal is not None: raise ValueError("Either plane or normal is acceptable.") if not isinstance(incident, Matrix): if is_sequence(incident): _incident = Matrix(incident) elif isinstance(incident, Ray3D): _incident = Matrix(incident.direction_ratio) else: raise TypeError( "incident should be a Matrix, Ray3D, or sequence") else: _incident = incident # If plane is provided, get direction ratios of the normal # to the plane from the plane else go with `normal` param. if plane is not None: if not isinstance(plane, Plane): raise TypeError("plane should be an instance of geometry.plane.Plane") # If we have the plane, we can get the intersection # point of incident ray and the plane and thus return # an instance of Ray3D. if isinstance(incident, Ray3D): return_ray = True intersection_pt = plane.intersection(incident)[0] _normal = Matrix(plane.normal_vector) else: if not isinstance(normal, Matrix): if is_sequence(normal): _normal = Matrix(normal) elif isinstance(normal, Ray3D): _normal = Matrix(normal.direction_ratio) if isinstance(incident, Ray3D): intersection_pt = intersection(incident, normal) if len(intersection_pt) == 0: raise ValueError( "Normal isn't concurrent with the incident ray.") else: return_ray = True intersection_pt = intersection_pt[0] else: raise TypeError( "Normal should be a Matrix, Ray3D, or sequence") else: _normal = normal n1, n2 = None, None if isinstance(medium1, Medium): n1 = medium1.refractive_index else: n1 = sympify(medium1) if isinstance(medium2, Medium): n2 = medium2.refractive_index else: n2 = sympify(medium2) eta = n1/n2 # Relative index of refraction # Calculating magnitude of the vectors mag_incident = sqrt(sum([i**2 for i in _incident])) mag_normal = sqrt(sum([i**2 for i in _normal])) # Converting vectors to unit vectors by dividing # them with their magnitudes _incident /= mag_incident _normal /= mag_normal c1 = -_incident.dot(_normal) # cos(angle_of_incidence) cs2 = 1 - eta**2*(1 - c1**2) # cos(angle_of_refraction)**2 if cs2.is_negative: # This is the case of total internal reflection(TIR). return 0 drs = eta*_incident + (eta*c1 - sqrt(cs2))*_normal # Multiplying unit vector by its magnitude drs = drs*mag_incident if not return_ray: return drs else: return Ray3D(intersection_pt, direction_ratio=drs)
def __new__(cls, expr, *args, **kwargs): expr = sympify(expr) if not args: if expr.is_Order: variables = expr.variables point = expr.point else: variables = list(expr.free_symbols) point = [S.Zero] * len(variables) else: args = list(args if is_sequence(args) else [args]) variables, point = [], [] if is_sequence(args[0]): for a in args: v, p = list(map(sympify, a)) variables.append(v) point.append(p) else: variables = list(map(sympify, args)) point = [S.Zero] * len(variables) if not all(isinstance(v, Symbol) for v in variables): raise TypeError('Variables are not symbols, got %s' % variables) if len(list(uniq(variables))) != len(variables): raise ValueError( 'Variables are supposed to be unique symbols, got %s' % variables) if expr.is_Order: expr_vp = dict(expr.args[1:]) new_vp = dict(expr_vp) vp = dict(zip(variables, point)) for v, p in vp.items(): if v in new_vp.keys(): if p != new_vp[v]: raise NotImplementedError( "Mixing Order at different points is not supported." ) else: new_vp[v] = p if set(expr_vp.keys()) == set(new_vp.keys()): return expr else: variables = list(new_vp.keys()) point = [new_vp[v] for v in variables] if expr is S.NaN: return S.NaN if any(x in p.free_symbols for x in variables for p in point): raise ValueError('Got %s as a point.' % point) if variables: if any(p != point[0] for p in point): raise NotImplementedError if point[0] is S.Infinity: s = dict([(k, 1 / Dummy()) for k in variables]) rs = dict([(1 / v, 1 / k) for k, v in s.items()]) elif point[0] is not S.Zero: s = dict((k, Dummy() + point[0]) for k in variables) rs = dict((v - point[0], k - point[0]) for k, v in s.items()) else: s = () rs = () expr = expr.subs(s) if expr.is_Add: from sympy import expand_multinomial expr = expand_multinomial(expr) if s: args = tuple([r[0] for r in rs.items()]) else: args = tuple(variables) if len(variables) > 1: # XXX: better way? We need this expand() to # workaround e.g: expr = x*(x + y). # (x*(x + y)).as_leading_term(x, y) currently returns # x*y (wrong order term!). That's why we want to deal with # expand()'ed expr (handled in "if expr.is_Add" branch below). expr = expr.expand() if expr.is_Add: lst = expr.extract_leading_order(args) expr = Add(*[f.expr for (e, f) in lst]) elif expr: expr = expr.as_leading_term(*args) expr = expr.as_independent(*args, as_Add=False)[1] expr = expand_power_base(expr) expr = expand_log(expr) if len(args) == 1: # The definition of O(f(x)) symbol explicitly stated that # the argument of f(x) is irrelevant. That's why we can # combine some power exponents (only "on top" of the # expression tree for f(x)), e.g.: # x**p * (-x)**q -> x**(p+q) for real p, q. x = args[0] margs = list( Mul.make_args(expr.as_independent(x, as_Add=False)[1])) for i, t in enumerate(margs): if t.is_Pow: b, q = t.args if b in (x, -x) and q.is_real and not q.has(x): margs[i] = x**q elif b.is_Pow and not b.exp.has(x): b, r = b.args if b in (x, -x) and r.is_real: margs[i] = x**(r * q) elif b.is_Mul and b.args[0] is S.NegativeOne: b = -b if b.is_Pow and not b.exp.has(x): b, r = b.args if b in (x, -x) and r.is_real: margs[i] = x**(r * q) expr = Mul(*margs) expr = expr.subs(rs) if expr is S.Zero: return expr if expr.is_Order: expr = expr.expr if not expr.has(*variables): expr = S.One # create Order instance: vp = dict(zip(variables, point)) variables.sort(key=default_sort_key) point = [vp[v] for v in variables] args = (expr, ) + Tuple(*zip(variables, point)) obj = Expr.__new__(cls, *args) return obj
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, diff >>> 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 _process_limits(*symbols): """Process the list of symbols and convert them to canonical limits, storing them as Tuple(symbol, lower, upper). The orientation of the function is also returned when the upper limit is missing so (x, 1, None) becomes (x, None, 1) and the orientation is changed. """ limits = [] orientation = 1 for V in symbols: if isinstance(V, (Relational, BooleanFunction)): variable = V.atoms(Symbol).pop() V = (variable, V.as_set()) if isinstance(V, Symbol) or getattr(V, '_diff_wrt', False): if isinstance(V, Idx): if V.lower is None or V.upper is None: limits.append(Tuple(V)) else: limits.append(Tuple(V, V.lower, V.upper)) else: limits.append(Tuple(V)) continue elif is_sequence(V, Tuple): V = sympify(flatten(V)) if isinstance(V[0], (Symbol, Idx)) or getattr(V[0], '_diff_wrt', False): newsymbol = V[0] if len(V) == 2 and isinstance(V[1], Interval): V[1:] = [V[1].start, V[1].end] if len(V) == 3: if V[1] is None and V[2] is not None: nlim = [V[2]] elif V[1] is not None and V[2] is None: orientation *= -1 nlim = [V[1]] elif V[1] is None and V[2] is None: nlim = [] else: nlim = V[1:] limits.append(Tuple(newsymbol, *nlim)) if isinstance(V[0], Idx): if V[0].lower is not None and not bool( nlim[0] >= V[0].lower): raise ValueError( "Summation exceeds Idx lower range.") if V[0].upper is not None and not bool( nlim[1] <= V[0].upper): raise ValueError( "Summation exceeds Idx upper range.") continue elif len(V) == 1 or (len(V) == 2 and V[1] is None): limits.append(Tuple(newsymbol)) continue elif len(V) == 2: limits.append(Tuple(newsymbol, V[1])) continue raise ValueError('Invalid limits given: %s' % str(symbols)) return limits, orientation
def __getitem__(self, indices, **kw_args): if is_sequence(indices): # Special case needed because M[*my_tuple] is a syntax error. return Indexed(self, *indices, **kw_args) else: return Indexed(self, indices, **kw_args)
def refraction_angle(incident, medium1, medium2, normal=None, plane=None): """ This function calculates transmitted vector after refraction at planar surface. ``medium1`` and ``medium2`` can be ``Medium`` or any sympifiable object. If ``incident`` is a number then treated as angle of incidence (in radians) in which case refraction angle is returned. If ``incident`` is an object of `Ray3D`, `normal` also has to be an instance of `Ray3D` in order to get the output as a `Ray3D`. Please note that if plane of separation is not provided and normal is an instance of `Ray3D`, ``normal`` will be assumed to be intersecting incident ray at the plane of separation. This will not be the case when `normal` is a `Matrix` or any other sequence. If ``incident`` is an instance of `Ray3D` and `plane` has not been provided and ``normal`` is not `Ray3D`, output will be a `Matrix`. Parameters ========== incident : Matrix, Ray3D, sequence or a number Incident vector or angle of incidence medium1 : sympy.physics.optics.medium.Medium or sympifiable Medium 1 or its refractive index medium2 : sympy.physics.optics.medium.Medium or sympifiable Medium 2 or its refractive index normal : Matrix, Ray3D, or sequence Normal vector plane : Plane Plane of separation of the two media. Returns ======= Returns an angle of refraction or a refracted ray depending on inputs. Examples ======== >>> from sympy.physics.optics import refraction_angle >>> from sympy.geometry import Point3D, Ray3D, Plane >>> from sympy.matrices import Matrix >>> from sympy import symbols, pi >>> n = Matrix([0, 0, 1]) >>> P = Plane(Point3D(0, 0, 0), normal_vector=[0, 0, 1]) >>> r1 = Ray3D(Point3D(-1, -1, 1), Point3D(0, 0, 0)) >>> refraction_angle(r1, 1, 1, n) Matrix([ [ 1], [ 1], [-1]]) >>> refraction_angle(r1, 1, 1, plane=P) Ray3D(Point3D(0, 0, 0), Point3D(1, 1, -1)) With different index of refraction of the two media >>> n1, n2 = symbols('n1, n2') >>> refraction_angle(r1, n1, n2, n) Matrix([ [ n1/n2], [ n1/n2], [-sqrt(3)*sqrt(-2*n1**2/(3*n2**2) + 1)]]) >>> refraction_angle(r1, n1, n2, plane=P) Ray3D(Point3D(0, 0, 0), Point3D(n1/n2, n1/n2, -sqrt(3)*sqrt(-2*n1**2/(3*n2**2) + 1))) >>> round(refraction_angle(pi/6, 1.2, 1.5), 5) 0.41152 """ n1 = refractive_index_of_medium(medium1) n2 = refractive_index_of_medium(medium2) # check if an incidence angle was supplied instead of a ray try: angle_of_incidence = float(incident) except TypeError: angle_of_incidence = None try: critical_angle_ = critical_angle(medium1, medium2) except (ValueError, TypeError): critical_angle_ = None if angle_of_incidence is not None: if normal is not None or plane is not None: raise ValueError( 'Normal/plane not allowed if incident is an angle') if not 0.0 <= angle_of_incidence < pi * 0.5: raise ValueError('Angle of incidence not in range [0:pi/2)') if critical_angle_ and angle_of_incidence > critical_angle_: raise ValueError('Ray undergoes total internal reflection') return asin(n1 * sin(angle_of_incidence) / n2) if angle_of_incidence and not 0 <= angle_of_incidence < pi * 0.5: raise ValueError # Treat the incident as ray below # A flag to check whether to return Ray3D or not return_ray = False if plane is not None and normal is not None: raise ValueError("Either plane or normal is acceptable.") if not isinstance(incident, Matrix): if is_sequence(incident): _incident = Matrix(incident) elif isinstance(incident, Ray3D): _incident = Matrix(incident.direction_ratio) else: raise TypeError("incident should be a Matrix, Ray3D, or sequence") else: _incident = incident # If plane is provided, get direction ratios of the normal # to the plane from the plane else go with `normal` param. if plane is not None: if not isinstance(plane, Plane): raise TypeError( "plane should be an instance of geometry.plane.Plane") # If we have the plane, we can get the intersection # point of incident ray and the plane and thus return # an instance of Ray3D. if isinstance(incident, Ray3D): return_ray = True intersection_pt = plane.intersection(incident)[0] _normal = Matrix(plane.normal_vector) else: if not isinstance(normal, Matrix): if is_sequence(normal): _normal = Matrix(normal) elif isinstance(normal, Ray3D): _normal = Matrix(normal.direction_ratio) if isinstance(incident, Ray3D): intersection_pt = intersection(incident, normal) if len(intersection_pt) == 0: raise ValueError( "Normal isn't concurrent with the incident ray.") else: return_ray = True intersection_pt = intersection_pt[0] else: raise TypeError( "Normal should be a Matrix, Ray3D, or sequence") else: _normal = normal eta = n1 / n2 # Relative index of refraction # Calculating magnitude of the vectors mag_incident = sqrt(sum([i**2 for i in _incident])) mag_normal = sqrt(sum([i**2 for i in _normal])) # Converting vectors to unit vectors by dividing # them with their magnitudes _incident /= mag_incident _normal /= mag_normal c1 = -_incident.dot(_normal) # cos(angle_of_incidence) cs2 = 1 - eta**2 * (1 - c1**2) # cos(angle_of_refraction)**2 if cs2.is_negative: # This is the case of total internal reflection(TIR). return 0 drs = eta * _incident + (eta * c1 - sqrt(cs2)) * _normal # Multiplying unit vector by its magnitude drs = drs * mag_incident if not return_ray: return drs else: return Ray3D(intersection_pt, direction_ratio=drs)
def idiff(eq, y, x, n=1): """Return ``dy/dx`` assuming that ``eq == 0``. Parameters ========== y : the dependent variable or a list of dependent variables (with y first) x : the variable that the derivative is being taken with respect to n : the order of the derivative (default is 1) Examples ======== >>> from sympy.abc import x, y, a >>> from sympy.geometry.util import idiff >>> circ = x**2 + y**2 - 4 >>> idiff(circ, y, x) -x/y >>> idiff(circ, y, x, 2).simplify() -(x**2 + y**2)/y**3 Here, ``a`` is assumed to be independent of ``x``: >>> idiff(x + a + y, y, x) -1 Now the x-dependence of ``a`` is made explicit by listing ``a`` after ``y`` in a list. >>> idiff(x + a + y, [y, a], x) -Derivative(a, x) - 1 See Also ======== sympy.core.function.Derivative: represents unevaluated derivatives sympy.core.function.diff: explicitly differentiates wrt symbols """ if is_sequence(y): dep = set(y) y = y[0] elif isinstance(y, Symbol): dep = {y} elif isinstance(y, Function): pass else: raise ValueError("expecting x-dependent symbol(s) or function(s) but got: %s" % y) f = {s: Function(s.name)(x) for s in eq.free_symbols if s != x and s in dep} if isinstance(y, Symbol): dydx = Function(y.name)(x).diff(x) else: dydx = y.diff(x) eq = eq.subs(f) derivs = {} for i in range(n): yp = solve(eq.diff(x), dydx)[0].subs(derivs) if i == n - 1: return yp.subs([(v, k) for k, v in f.items()]) derivs[dydx] = yp eq = dydx - yp dydx = dydx.diff(x)
def __new__(cls, *args, **kwargs): args = [Tuple(*a) if is_sequence(a) else sympify(a) for a in args] return Basic.__new__(cls, *args)
def _imp_namespace(expr, namespace=None): """ Return namespace dict with function implementations We need to search for functions in anything that can be thrown at us - that is - anything that could be passed as ``expr``. Examples include sympy expressions, as well as tuples, lists and dicts that may contain sympy expressions. Parameters ---------- expr : object Something passed to lambdify, that will generate valid code from ``str(expr)``. namespace : None or mapping Namespace to fill. None results in new empty dict Returns ------- namespace : dict dict with keys of implemented function names within ``expr`` and corresponding values being the numerical implementation of function Examples ======== >>> from sympy.abc import x >>> from sympy.utilities.lambdify import implemented_function, _imp_namespace >>> from sympy import Function >>> f = implemented_function(Function('f'), lambda x: x+1) >>> g = implemented_function(Function('g'), lambda x: x*10) >>> namespace = _imp_namespace(f(g(x))) >>> sorted(namespace.keys()) ['f', 'g'] """ # Delayed import to avoid circular imports from sympy.core.function import FunctionClass if namespace is None: namespace = {} # tuples, lists, dicts are valid expressions if is_sequence(expr): for arg in expr: _imp_namespace(arg, namespace) return namespace elif isinstance(expr, dict): for key, val in expr.items(): # functions can be in dictionary keys _imp_namespace(key, namespace) _imp_namespace(val, namespace) return namespace # sympy expressions may be Functions themselves func = getattr(expr, 'func', None) if isinstance(func, FunctionClass): imp = getattr(func, '_imp_', None) if imp is not None: name = expr.func.__name__ if name in namespace and namespace[name] != imp: raise ValueError('We found more than one ' 'implementation with name ' '"%s"' % name) namespace[name] = imp # and / or they may take Functions as arguments if hasattr(expr, 'args'): for arg in expr.args: _imp_namespace(arg, namespace) return namespace
def deviation(incident, medium1, medium2, normal=None, plane=None): """ This function calculates the angle of deviation of a ray due to refraction at planar surface. Parameters ========== incident : Matrix, Ray3D, sequence or float Incident vector or angle of incidence medium1 : sympy.physics.optics.medium.Medium or sympifiable Medium 1 or its refractive index medium2 : sympy.physics.optics.medium.Medium or sympifiable Medium 2 or its refractive index normal : Matrix, Ray3D, or sequence Normal vector plane : Plane Plane of separation of the two media. Returns angular deviation between incident and refracted rays Examples ======== >>> from sympy.physics.optics import deviation >>> from sympy.geometry import Point3D, Ray3D, Plane >>> from sympy.matrices import Matrix >>> from sympy import symbols >>> n1, n2 = symbols('n1, n2') >>> n = Matrix([0, 0, 1]) >>> P = Plane(Point3D(0, 0, 0), normal_vector=[0, 0, 1]) >>> r1 = Ray3D(Point3D(-1, -1, 1), Point3D(0, 0, 0)) >>> deviation(r1, 1, 1, n) 0 >>> deviation(r1, n1, n2, plane=P) -acos(-sqrt(-2*n1**2/(3*n2**2) + 1)) + acos(-sqrt(3)/3) >>> round(deviation(0.1, 1.2, 1.5), 5) -0.02005 """ refracted = refraction_angle(incident, medium1, medium2, normal=normal, plane=plane) try: angle_of_incidence = Float(incident) except TypeError: angle_of_incidence = None if angle_of_incidence is not None: return float(refracted) - angle_of_incidence if refracted != 0: if isinstance(refracted, Ray3D): refracted = Matrix(refracted.direction_ratio) if not isinstance(incident, Matrix): if is_sequence(incident): _incident = Matrix(incident) elif isinstance(incident, Ray3D): _incident = Matrix(incident.direction_ratio) else: raise TypeError( "incident should be a Matrix, Ray3D, or sequence") else: _incident = incident if plane is None: if not isinstance(normal, Matrix): if is_sequence(normal): _normal = Matrix(normal) elif isinstance(normal, Ray3D): _normal = Matrix(normal.direction_ratio) else: raise TypeError( "normal should be a Matrix, Ray3D, or sequence") else: _normal = normal else: _normal = Matrix(plane.normal_vector) mag_incident = sqrt(sum([i**2 for i in _incident])) mag_normal = sqrt(sum([i**2 for i in _normal])) mag_refracted = sqrt(sum([i**2 for i in refracted])) _incident /= mag_incident _normal /= mag_normal refracted /= mag_refracted i = acos(_incident.dot(_normal)) r = acos(refracted.dot(_normal)) return i - r
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 _process_limits(*symbols): """Process the list of symbols and convert them to canonical limits, storing them as Tuple(symbol, lower, upper). The orientation of the function is also returned when the upper limit is missing so (x, 1, None) becomes (x, None, 1) and the orientation is changed. """ limits = [] orientation = 1 for V in symbols: if isinstance(V, (Relational, BooleanFunction)): variable = V.atoms(Symbol).pop() V = (variable, V.as_set()) if isinstance(V, Symbol) or getattr(V, '_diff_wrt', False): if isinstance(V, Idx): if V.lower is None or V.upper is None: limits.append(Tuple(V)) else: limits.append(Tuple(V, V.lower, V.upper)) else: limits.append(Tuple(V)) continue elif is_sequence(V, Tuple): if len(V) == 2 and isinstance(V[1], Range): lo = V[1].inf hi = V[1].sup dx = abs(V[1].step) V = [V[0]] + [0, (hi - lo) // dx, dx * V[0] + lo] V = sympify(flatten(V)) # a list of sympified elements if isinstance(V[0], (Symbol, Idx)) or getattr(V[0], '_diff_wrt', False): newsymbol = V[0] if len(V) == 2 and isinstance(V[1], Interval): # 2 -> 3 # Interval V[1:] = [V[1].start, V[1].end] elif len(V) == 3: # general case if V[2] is None and not V[1] is None: orientation *= -1 V = [newsymbol] + [i for i in V[1:] if i is not None] if not isinstance(newsymbol, Idx) or len(V) == 3: if len(V) == 4: limits.append(Tuple(*V)) continue if len(V) == 3: if isinstance(newsymbol, Idx): # Idx represents an integer which may have # specified values it can take on; if it is # given such a value, an error is raised here # if the summation would try to give it a larger # or smaller value than permitted. None and Symbolic # values will not raise an error. lo, hi = newsymbol.lower, newsymbol.upper try: if lo is not None and not bool(V[1] >= lo): raise ValueError( "Summation will set Idx value too low." ) except TypeError: pass try: if hi is not None and not bool(V[2] <= hi): raise ValueError( "Summation will set Idx value too high." ) except TypeError: pass limits.append(Tuple(*V)) continue if len(V) == 1 or (len(V) == 2 and V[1] is None): limits.append(Tuple(newsymbol)) continue elif len(V) == 2: limits.append(Tuple(newsymbol, V[1])) continue raise ValueError('Invalid limits given: %s' % str(symbols)) return limits, orientation