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 __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 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 sizes = [] for i in self.indices: upper = getattr(i, 'upper', None) lower = getattr(i, 'lower', None) if None in (upper, lower): raise IndexException(filldedent(""" Range is not defined for all indices in: %s""" % self)) try: size = upper - lower + 1 except TypeError: raise IndexException(filldedent(""" Shape cannot be inferred from Idx with undefined range: %s""" % self)) sizes.append(size) return Tuple(*sizes)
def _eval_derivative(self, sym): """Evaluate the derivative of the current Integral object by differentiating under the integral sign [1], using the Fundamental Theorem of Calculus [2] when possible. Whenever an Integral is encountered that is equivalent to zero or has an integrand that is independent of the variable of integration those integrals are performed. All others are returned as Integral instances which can be resolved with doit() (provided they are integrable). References: [1] http://en.wikipedia.org/wiki/Differentiation_under_the_integral_sign [2] http://en.wikipedia.org/wiki/Fundamental_theorem_of_calculus >>> from sympy import Integral >>> from sympy.abc import x, y >>> i = Integral(x + y, y, (y, 1, x)) >>> i.diff(x) Integral(x + y, (y, x)) + Integral(1, (y, y), (y, 1, x)) >>> i.doit().diff(x) == i.diff(x).doit() True >>> i.diff(y) 0 The previous must be true since there is no y in the evaluated integral: >>> i.free_symbols set([x]) >>> i.doit() -1/6 - x/2 + 2*x**3/3 """ # differentiate under the integral sign; we do not # check for regularity conditions (TODO), see issue 1116 # get limits and the function f, limits = self.function, list(self.limits) # the order matters if variables of integration appear in the limits # so work our way in from the outside to the inside. limit = limits.pop(-1) if len(limit) == 3: x, a, b = limit elif len(limit) == 2: x, b = limit a = None else: a = b = None x = limit[0] if limits: # f is the argument to an integral f = Integral(f, *tuple(limits)) # assemble the pieces rv = 0 if b is not None: rv += f.subs(x, b)*diff(b, sym) if a is not None: rv -= f.subs(x, a)*diff(a, sym) if len(limit) == 1 and sym == x: # the dummy variable *is* also the real-world variable arg = f rv += arg else: # the dummy variable might match sym but it's # only a dummy and the actual variable is determined # by the limits, so mask off the variable of integration # while differentiating u = Dummy('u') arg = f.subs(x, u).diff(sym).subs(u, x) rv += Integral(arg, Tuple(x, a, b)) return rv
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 = filter(lambda x: x.name not in argument_sequence, arg_list) 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 __new__(cls, expr, cond): if cond is True: cond = ExprCondPair.true_sentinel return Tuple.__new__(cls, expr, cond)
def _regular_point_ellipse(self, a, b, c, d, e, f): D = 4 * a * c - b**2 ok = D if not ok: raise ValueError("Rational Point on the conic does not exist") if a == 0 and c == 0: K = -1 L = 4 * (d * e - b * f) elif c != 0: K = D L = 4 * c**2 * d**2 - 4 * b * c * d * e + 4 * a * c * e**2 + 4 * b**2 * c * f - 16 * a * c**2 * f else: K = D L = 4 * a**2 * e**2 - 4 * b * a * d * e + 4 * b**2 * a * f ok = L != 0 and not (K > 0 and L < 0) if not ok: raise ValueError("Rational Point on the conic does not exist") K = Rational(K).limit_denominator(10**12) L = Rational(L).limit_denominator(10**12) k1, k2 = K.p, K.q l1, l2 = L.p, L.q g = gcd(k2, l2) a1 = (l2 * k2) / g b1 = (k1 * l2) / g c1 = -(l1 * k2) / g a2 = sign(a1) * core(abs(a1), 2) r1 = sqrt(a1 / a2) b2 = sign(b1) * core(abs(b1), 2) r2 = sqrt(b1 / b2) c2 = sign(c1) * core(abs(c1), 2) r3 = sqrt(c1 / c2) g = gcd(gcd(a2, b2), c2) a2 = a2 / g b2 = b2 / g c2 = c2 / g g1 = gcd(a2, b2) a2 = a2 / g1 b2 = b2 / g1 c2 = c2 * g1 g2 = gcd(a2, c2) a2 = a2 / g2 b2 = b2 * g2 c2 = c2 / g2 g3 = gcd(b2, c2) a2 = a2 * g3 b2 = b2 / g3 c2 = c2 / g3 x, y, z = symbols("x y z") eq = a2 * x**2 + b2 * y**2 + c2 * z**2 solutions = diophantine(eq) if len(solutions) == 0: raise ValueError("Rational Point on the conic does not exist") flag = False for sol in solutions: syms = Tuple(*sol).free_symbols rep = {s: 3 for s in syms} sol_z = sol[2] if sol_z == 0: flag = True continue if not (isinstance(sol_z, Integer) or isinstance(sol_z, int)): syms_z = sol_z.free_symbols if len(syms_z) == 1: p = next(iter(syms_z)) p_values = Complement( S.Integers, solveset(Eq(sol_z, 0), p, S.Integers)) rep[p] = next(iter(p_values)) if len(syms_z) == 2: p, q = list(ordered(syms_z)) for i in S.Integers: subs_sol_z = sol_z.subs(p, i) q_values = Complement( S.Integers, solveset(Eq(subs_sol_z, 0), q, S.Integers)) if not q_values.is_empty: rep[p] = i rep[q] = next(iter(q_values)) break if len(syms) != 0: x, y, z = tuple(s.subs(rep) for s in sol) else: x, y, z = sol flag = False break if flag: raise ValueError("Rational Point on the conic does not exist") x = (x * g3) / r1 y = (y * g2) / r2 z = (z * g1) / r3 x = x / z y = y / z if a == 0 and c == 0: x_reg = (x + y - 2 * e) / (2 * b) y_reg = (x - y - 2 * d) / (2 * b) elif c != 0: x_reg = (x - 2 * d * c + b * e) / K y_reg = (y - b * x_reg - e) / (2 * c) else: y_reg = (x - 2 * e * a + b * d) / K x_reg = (y - b * y_reg - d) / (2 * a) return x_reg, y_reg
cube_faces, octahedron_faces, dodecahedron_faces, icosahedron_faces, ) # ----------------------------------------------------------------------- # Standard Polyhedron groups # # These are generated using _pgroup_calcs() above. However to save # import time we encode them explicitly here. # ----------------------------------------------------------------------- tetrahedron = Polyhedron( Tuple(0, 1, 2, 3), Tuple(Tuple(0, 1, 2), Tuple(0, 2, 3), Tuple(0, 1, 3), Tuple(1, 2, 3)), Tuple( Perm(1, 2, 3), Perm(3)(0, 1, 2), Perm(0, 3, 2), Perm(0, 3, 1), Perm(0, 1)(2, 3), Perm(0, 2)(1, 3), Perm(0, 3)(1, 2), ), ) cube = Polyhedron( Tuple(0, 1, 2, 3, 4, 5, 6, 7), Tuple(
def shape(self): return Tuple(*numpy.shape(self.arg))
def fprint(self, printer, lhs=None): """Fortran print.""" a = IndexedBase(self.first) b = IndexedBase(self.second) slc = Slice(None, None) rank = self.rank if rank > 2: raise NotImplementedError('TODO') if rank == 2: a_inds = [[slc, 0], [slc, 1], [slc, 2]] b_inds = [[slc, 0], [slc, 1], [slc, 2]] if self.first.order == 'C': for inds in a_inds: inds.reverse() if self.second.order == 'C': for inds in b_inds: inds.reverse() a = [a[tuple(inds)] for inds in a_inds] b = [b[tuple(inds)] for inds in b_inds] cross_product = [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0] ] cross_product = Tuple(*cross_product) cross_product = printer(cross_product) first = printer(self.first) order = self.order if lhs is not None: lhs = printer(lhs) if rank == 2: alloc = 'allocate({0}(0:size({1},1)-1,0:size({1},2)-1))'.format( lhs, first) elif rank == 1: alloc = 'allocate({}(0:size({})-1)'.format(lhs, first) if rank == 2: if order == 'C': code = 'reshape({}, shape({}), order=[2,1])'.format( cross_product, first) else: code = 'reshape({}, shape({})'.format(cross_product, first) elif rank == 1: code = cross_product if lhs is not None: code = '{} = {}'.format(lhs, code) #return alloc + '\n' + code return code
def shape(self): return Tuple(self.arg.rank)
def routine(self, name, expr, argument_sequence, local_vars, settings): """Specialized Routine creation for Cython.""" if is_sequence(expr) and not isinstance(expr, (MatrixBase, MatrixExpr)): if not expr: raise ValueError("No expression given") expressions = Tuple(*expr) else: expressions = Tuple(expr) self.settings = settings # local variables idx_vars = set() symbol_idx_vars = set() for l in expressions.atoms(For): idx_vars.update({i for i in l.atoms(Idx)}) # remove symbols that have the same name of Idx in loop name_idx = [i.label.name for i in l.atoms(Idx)] symbol_idx_vars.update( {i for i in l.atoms(Symbol) if i.name in name_idx}) score_table = {} for i in idx_vars: score_table[i] = 0 def rate_index_position(p): return p * 5 arrays = expressions.atoms(Indexed) for arr in arrays: for p, ind in enumerate(arr.indices): try: score_table[ind] += rate_index_position(p) except KeyError: pass idx_order = sorted(idx_vars, key=lambda x: score_table[x]) # local variables local_vars = set() if local_vars is None else set(local_vars) # symbols that should be arguments symbol_indexed_local = set() for l in local_vars: for ll in l.atoms(IndexedBase): symbol_indexed_local.update( ll.atoms(Symbol) - ll.shape.atoms(Symbol)) symbols = expressions.free_symbols - idx_vars - local_vars - symbol_idx_vars - symbol_indexed_local new_symbols = set([]) new_symbols.update(symbols) for symbol in symbols: if isinstance(symbol, Idx): new_symbols.remove(symbol) if symbol.label in idx_vars: new_symbols.update(symbol.args[1].free_symbols) else: new_symbols.update([symbol.label]) symbols = new_symbols output_args, instructions = extract(expressions, symbols) arg_list = [] # setup input argument list array_symbols = {} for array in expressions.atoms(Indexed): array_symbols[array.base.label] = array for array in expressions.atoms(MatrixSymbol): array_symbols[array] = array for symbol in sorted(symbols, key=str): if symbol in array_symbols: dims = [] array = array_symbols[symbol] for dim in array.shape: if dim != 1: 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: msg = "Argument list didn't specify: {0} " msg = msg.format(", ".join([str(m.name) for m in missing])) raise CodeGenArgumentListError(msg, missing) # create redundant arguments to produce the requested sequence name_arg_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 return Routine(name, arg_list, instructions, idx_order, local_vars, settings)
def _eval_subs(self, old, new): """ Substitute old with new in the integrand and the limits, but don't change anything that is (or corresponds to) a dummy variable of integration. The normal substitution semantics -- traversing all arguments looking for matching patterns -- should not be applied to the Integrals since changing the integration variables should also entail a change in the integration limits (which should be done with the transform method). So this method just makes changes in the integrand and the limits. Not all instances of a given variable are conceptually the same: the first argument of the limit tuple with length greater than 1 and any corresponding variable in the integrand are dummy variables while every other symbol is a symbol that will be unchanged when the integral is evaluated. For example, the dummy variables for ``i`` can be seen as symbols with a preppended underscore: >>> from sympy import Integral >>> from sympy.abc import a, b, x, y >>> i = Integral(a + x, (a, a, b)) >>> i.as_dummy() Integral(_a + x, (_a, a, b)) If you want to change the lower limit to 1 there is no reason to prohibit this since it is not conceptually related to the integration variable, _a. Nor is there reason to disallow changing the b to 1. If a second limit were added, however, as in: >>> i = Integral(x + a, (a, a, b), (b, 1, 2)) the dummy variables become: >>> i.as_dummy() Integral(_a + x, (_a, a, _b), (_b, 1, 2)) Note that the ``b`` of the first limit is now a dummy variable since ``b`` is a dummy variable in the second limit. The "evaluate at" form of an integral allows some flexibility in how the integral will be treated by subs: if there is no second argument, none of the symbols matching the integration symbol are considered to be dummy variables, but if an explicit expression is given for a limit then the usual interpretation of the integration symbol as a dummy symbol applies: >>> Integral(x).as_dummy() # implicit integration wrt x Integral(x, x) >>> Integral(x, x).as_dummy() Integral(x, x) >>> _.subs(x, 1) Integral(1, x) >>> i = Integral(x, (x, x)) >>> i.as_dummy() Integral(_x, (_x, x)) >>> i.subs(x, 1) Integral(x, (x, 1)) Summary: no variable of the integrand or limit can be the target of substitution if it appears as a variable of integration in a limit positioned to the right of it. The only exception is for a variable that defines an indefinite integral limit (a single symbol): that symbol *can* be replaced in the integrand. >>> from sympy import Integral >>> from sympy.abc import a, b, c, x, y >>> i = Integral(a + x, (a, a, 3), (b, x, c)) >>> i.free_symbols # only these can be changed set([a, c, x]) >>> i.subs(a, c) # note that the variable of integration is unchanged Integral(a + x, (a, c, 3), (b, x, c)) >>> i.subs(a + x, b) == i # there is no x + a, only x + <a> True >>> i.subs(x, y - c) Integral(a - c + y, (a, a, 3), (b, -c + y, c)) """ integrand, limits = self.function, self.limits old_atoms = old.free_symbols limits = list(limits) dummies = set() for i in xrange(-1, -len(limits) - 1, -1): xab = limits[i] if len(xab) == 1: continue if not dummies.intersection(old_atoms): limits[i] = Tuple(xab[0], *[l._subs(old, new) for l in xab[1:]]) dummies.add(xab[0]) if not dummies.intersection(old_atoms): integrand = integrand.subs(old, new) return Integral(integrand, *limits)
def __new__(cls, function, *symbols, **assumptions): """Create an unevaluated integral. Arguments are an integrand followed by one or more limits. If no limits are given and there is only one free symbol in the expression, that symbol will be used, otherwise an error will be raised. >>> from sympy import Integral >>> from sympy.abc import x, y >>> Integral(x) Integral(x, x) >>> Integral(y) Integral(y, y) When limits are provided, they are interpreted as follows (using ``x`` as though it were the variable of integration): (x,) or x - indefinite integral (x, a) - "evaluate at" integral (x, a, b) - definite integral Although the same integral will be obtained from an indefinite integral and an "evaluate at" integral when ``a == x``, they respond differently to substitution: >>> i = Integral(x, x) >>> at = Integral(x, (x, x)) >>> i.doit() == at.doit() True >>> i.subs(x, 1) Integral(1, x) >>> at.subs(x, 1) Integral(x, (x, 1)) The ``as_dummy`` method can be used to see which symbols cannot be targeted by subs: those with a preppended underscore cannot be changed with ``subs``. (Also, the integration variables themselves -- the first element of a limit -- can never be changed by subs.) >>> i.as_dummy() Integral(x, x) >>> at.as_dummy() Integral(_x, (_x, x)) """ # Any embedded piecewise functions need to be brought out to the # top level so that integration can go into piecewise mode at the # earliest possible moment. function = piecewise_fold(sympify(function)) if function is S.NaN: return S.NaN if symbols: limits, sign = _process_limits(*symbols) else: # no symbols provided -- let's compute full anti-derivative free = function.free_symbols if len(free) != 1: raise ValueError("specify variables of integration for %s" % function) limits, sign = [Tuple(s) for s in free], 1 while isinstance(function, Integral): # denest the integrand limits = list(function.limits) + limits function = function.function obj = Expr.__new__(cls, **assumptions) arglist = [sign * function] arglist.extend(limits) obj._args = tuple(arglist) obj.is_commutative = all(s.is_commutative for s in obj.free_symbols) return obj
def __new__(cls, corners, faces=[], pgroup=[]): """ The constructor of the Polyhedron group object. It takes up to three parameters: the corners, faces, and allowed transformations. The corners/vertices are entered as a list of arbitrary expressions that are used to identify each vertex. The faces are entered as a list of tuples of indices; a tuple of indices identifies the vertices which define the face. They should be entered in a cw or ccw order; they will be standardized by reversal and rotation to be give the lowest lexical ordering. If no faces are given then no edges will be computed. >>> from sympy.combinatorics.polyhedron import Polyhedron >>> Polyhedron(list('abc'), [(1, 2, 0)]).faces {(0, 1, 2)} >>> Polyhedron(list('abc'), [(1, 0, 2)]).faces {(0, 1, 2)} The allowed transformations are entered as allowable permutations of the vertices for the polyhedron. Instance of Permutations (as with faces) should refer to the supplied vertices by index. These permutation are stored as a PermutationGroup. Examples ======== >>> from sympy.combinatorics.permutations import Permutation >>> Permutation.print_cyclic = False >>> from sympy.abc import w, x, y, z Here we construct the Polyhedron object for a tetrahedron. >>> corners = [w, x, y, z] >>> faces = [(0,1,2), (0,2,3), (0,3,1), (1,2,3)] Next, allowed transformations of the polyhedron must be given. This is given as permutations of vertices. Although the vertices of a tetrahedron can be numbered in 24 (4!) different ways, there are only 12 different orientations for a physical tetrahedron. The following permutations, applied once or twice, will generate all 12 of the orientations. (The identity permutation, Permutation(range(4)), is not included since it does not change the orientation of the vertices.) >>> pgroup = [Permutation([[0,1,2], [3]]), \ Permutation([[0,1,3], [2]]), \ Permutation([[0,2,3], [1]]), \ Permutation([[1,2,3], [0]]), \ Permutation([[0,1], [2,3]]), \ Permutation([[0,2], [1,3]]), \ Permutation([[0,3], [1,2]])] The Polyhedron is now constructed and demonstrated: >>> tetra = Polyhedron(corners, faces, pgroup) >>> tetra.size 4 >>> tetra.edges {(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)} >>> tetra.corners (w, x, y, z) It can be rotated with an arbitrary permutation of vertices, e.g. the following permutation is not in the pgroup: >>> tetra.rotate(Permutation([0, 1, 3, 2])) >>> tetra.corners (w, x, z, y) An allowed permutation of the vertices can be constructed by repeatedly applying permutations from the pgroup to the vertices. Here is a demonstration that applying p and p**2 for every p in pgroup generates all the orientations of a tetrahedron and no others: >>> all = ( (w, x, y, z), \ (x, y, w, z), \ (y, w, x, z), \ (w, z, x, y), \ (z, w, y, x), \ (w, y, z, x), \ (y, z, w, x), \ (x, z, y, w), \ (z, y, x, w), \ (y, x, z, w), \ (x, w, z, y), \ (z, x, w, y) ) >>> got = [] >>> for p in (pgroup + [p**2 for p in pgroup]): ... h = Polyhedron(corners) ... h.rotate(p) ... got.append(h.corners) ... >>> set(got) == set(all) True The make_perm method of a PermutationGroup will randomly pick permutations, multiply them together, and return the permutation that can be applied to the polyhedron to give the orientation produced by those individual permutations. Here, 3 permutations are used: >>> tetra.pgroup.make_perm(3) # doctest: +SKIP Permutation([0, 3, 1, 2]) To select the permutations that should be used, supply a list of indices to the permutations in pgroup in the order they should be applied: >>> use = [0, 0, 2] >>> p002 = tetra.pgroup.make_perm(3, use) >>> p002 Permutation([1, 0, 3, 2]) Apply them one at a time: >>> tetra.reset() >>> for i in use: ... tetra.rotate(pgroup[i]) ... >>> tetra.vertices (x, w, z, y) >>> sequentially = tetra.vertices Apply the composite permutation: >>> tetra.reset() >>> tetra.rotate(p002) >>> tetra.corners (x, w, z, y) >>> tetra.corners in all and tetra.corners == sequentially True Notes ===== Defining permutation groups --------------------------- It is not necessary to enter any permutations, nor is necessary to enter a complete set of transforations. In fact, for a polyhedron, all configurations can be constructed from just two permutations. For example, the orientations of a tetrahedron can be generated from an axis passing through a vertex and face and another axis passing through a different vertex or from an axis passing through the midpoints of two edges opposite of each other. For simplicity of presentation, consider a square -- not a cube -- with vertices 1, 2, 3, and 4: 1-----2 We could think of axes of rotation being: | | 1) through the face | | 2) from midpoint 1-2 to 3-4 or 1-3 to 2-4 3-----4 3) lines 1-4 or 2-3 To determine how to write the permutations, imagine 4 cameras, one at each corner, labeled A-D: A B A B 1-----2 1-----3 vertex index: | | | | 1 0 | | | | 2 1 3-----4 2-----4 3 2 C D C D 4 3 original after rotation along 1-4 A diagonal and a face axis will be chosen for the "permutation group" from which any orientation can be constructed. >>> pgroup = [] Imagine a clockwise rotation when viewing 1-4 from camera A. The new orientation is (in camera-order): 1, 3, 2, 4 so the permutation is given using the *indices* of the vertices as: >>> pgroup.append(Permutation((0, 2, 1, 3))) Now imagine rotating clockwise when looking down an axis entering the center of the square as viewed. The new camera-order would be 3, 1, 4, 2 so the permutation is (using indices): >>> pgroup.append(Permutation((2, 0, 3, 1))) The square can now be constructed: ** use real-world labels for the vertices, entering them in camera order ** for the faces we use zero-based indices of the vertices in *edge-order* as the face is traversed; neither the direction nor the starting point matter -- the faces are only used to define edges (if so desired). >>> square = Polyhedron((1, 2, 3, 4), [(0, 1, 3, 2)], pgroup) To rotate the square with a single permutation we can do: >>> square.rotate(square.pgroup[0]); square.corners (1, 3, 2, 4) To use more than one permutation (or to use one permutation more than once) it is more convenient to use the make_perm method: >>> p011 = square.pgroup.make_perm([0,1,1]) # diag flip + 2 rotations >>> square.reset() # return to initial orientation >>> square.rotate(p011); square.corners (4, 2, 3, 1) Thinking outside the box ------------------------ Although the Polyhedron object has a direct physical meaning, it actually has broader application. In the most general sense it is just a decorated PermutationGroup, allowing one to connect the permutations to something physical. For example, a Rubik's cube is not a proper polyhedron, but the Polyhedron class can be used to represent it in a way that helps to visualize the Rubik's cube. >>> from sympy.utilities.iterables import flatten, unflatten >>> from sympy import symbols >>> from sympy.combinatorics import RubikGroup >>> facelets = flatten([symbols(s+'1:5') for s in 'UFRBLD']) >>> def show(): ... pairs = unflatten(r2.corners, 2) ... print(pairs[::2]) ... print(pairs[1::2]) ... >>> r2 = Polyhedron(facelets, pgroup=RubikGroup(2)) >>> show() [(U1, U2), (F1, F2), (R1, R2), (B1, B2), (L1, L2), (D1, D2)] [(U3, U4), (F3, F4), (R3, R4), (B3, B4), (L3, L4), (D3, D4)] >>> r2.rotate(0) # cw rotation of F >>> show() [(U1, U2), (F3, F1), (U3, R2), (B1, B2), (L1, D1), (R3, R1)] [(L4, L2), (F4, F2), (U4, R4), (B3, B4), (L3, D2), (D3, D4)] Predefined Polyhedra ==================== For convenience, the vertices and faces are defined for the following standard solids along with a permutation group for transformations. When the polyhedron is oriented as indicated below, the vertices in a given horizontal plane are numbered in ccw direction, starting from the vertex that will give the lowest indices in a given face. (In the net of the vertices, indices preceded by "-" indicate replication of the lhs index in the net.) tetrahedron, tetrahedron_faces ------------------------------ 4 vertices (vertex up) net: 0 0-0 1 2 3-1 4 faces: (0,1,2) (0,2,3) (0,3,1) (1,2,3) cube, cube_faces ---------------- 8 vertices (face up) net: 0 1 2 3-0 4 5 6 7-4 6 faces: (0,1,2,3) (0,1,5,4) (1,2,6,5) (2,3,7,6) (0,3,7,4) (4,5,6,7) octahedron, octahedron_faces ---------------------------- 6 vertices (vertex up) net: 0 0 0-0 1 2 3 4-1 5 5 5-5 8 faces: (0,1,2) (0,2,3) (0,3,4) (0,1,4) (1,2,5) (2,3,5) (3,4,5) (1,4,5) dodecahedron, dodecahedron_faces -------------------------------- 20 vertices (vertex up) net: 0 1 2 3 4 -0 5 6 7 8 9 -5 14 10 11 12 13-14 15 16 17 18 19-15 12 faces: (0,1,2,3,4) (0,1,6,10,5) (1,2,7,11,6) (2,3,8,12,7) (3,4,9,13,8) (0,4,9,14,5) (5,10,16,15,14) ( 6,10,16,17,11) (7,11,17,18,12) (8,12,18,19,13) (9,13,19,15,14) (15,16,17,18,19) icosahedron, icosahedron_faces ------------------------------ 12 vertices (face up) net: 0 0 0 0 -0 1 2 3 4 5 -1 6 7 8 9 10 -6 11 11 11 11 -11 20 faces: (0,1,2) (0,2,3) (0,3,4) (0,4,5) (0,1,5) (1,2,6) (2,3,7) (3,4,8) (4,5,9) (1,5,10) (2,6,7) (3,7,8) (4,8,9) (5,9,10) (1,6,10) (6,7,11,) (7,8,11) (8,9,11) (9,10,11) (6,10,11) >>> from sympy.combinatorics.polyhedron import cube >>> cube.edges {(0, 1), (0, 3), (0, 4), '...', (4, 7), (5, 6), (6, 7)} If you want to use letters or other names for the corners you can still use the pre-calculated faces: >>> corners = list('abcdefgh') >>> Polyhedron(corners, cube.faces).corners (a, b, c, d, e, f, g, h) References ========== [1] www.ocf.berkeley.edu/~wwu/articles/platonicsolids.pdf """ faces = [minlex(f, directed=False, is_set=True) for f in faces] corners, faces, pgroup = args = \ [Tuple(*a) for a in (corners, faces, pgroup)] obj = Basic.__new__(cls, *args) obj._corners = tuple(corners) # in order given obj._faces = FiniteSet(faces) if pgroup and pgroup[0].size != len(corners): raise ValueError("Permutation size unequal to number of corners.") # use the identity permutation if none are given obj._pgroup = PermutationGroup(( pgroup or [Perm(range(len(corners)))] )) return obj
def _eval_subs(self, old, new): # only do substitutions in shape shape = Tuple(*self.shape)._subs(old, new) return MatrixSymbol(self.name, *shape)
def routine(self, name, expr, argument_sequence, global_vars): """Creates an Routine object that is appropriate for this language. This implementation is appropriate for at least C/Fortran. Subclasses can override this if necessary. Here, we assume at most one return value (the l-value) which must be scalar. Additional outputs are OutputArguments (e.g., pointers on right-hand-side or pass-by-reference). Matrices are always returned via OutputArguments. If ``argument_sequence`` is None, arguments will be ordered alphabetically, but with all InputArguments first, and then OutputArgument and InOutArguments. This implementation is almost the same as the CodeGen class, but expensive calls to Basic.atoms() have been replaced with cheaper equivalents. """ if is_sequence(expr) and not isinstance(expr, (MatrixBase, MatrixExpr)): if not expr: raise ValueError("No expression given") expressions = Tuple(*expr) else: expressions = Tuple(expr) expr_free_symbols = expressions.free_symbols # local variables local_vars = {i.label for i in expr_free_symbols if isinstance(i, Idx)} # global variables global_vars = set() if global_vars is None else set(global_vars) # symbols that should be arguments symbols = expr_free_symbols - local_vars - global_vars new_symbols = set([]) new_symbols.update(symbols) for symbol in symbols: if isinstance(symbol, Idx): new_symbols.remove(symbol) new_symbols.update(symbol.args[1].free_symbols) if isinstance(symbol, Indexed): new_symbols.remove(symbol) symbols = new_symbols # 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 elif isinstance(out_arg, MatrixSymbol): dims = tuple([ (S.Zero, dim - 1) for dim in out_arg.shape]) symbol = out_arg else: raise CodeGenError("Only Indexed, Symbol, or MatrixSymbol " "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) elif isinstance(expr, (ImmutableMatrix, MatrixSlice)): # Create a "dummy" MatrixSymbol to use as the Output arg out_arg = MatrixSymbol('out_%s' % abs(hash(expr)), *expr.shape) dims = tuple([(S.Zero, dim - 1) for dim in out_arg.shape]) output_args.append( OutputArgument(out_arg, out_arg, expr, dimensions=dims)) else: return_val.append(Result(expr)) arg_list = [] # setup input argument list array_symbols = {} for array in [i for i in expr_free_symbols if isinstance(i, Indexed)]: array_symbols[array.base.label] = array for array in [i for i in expr_free_symbols if isinstance(i, MatrixSymbol)]: array_symbols[array] = 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: msg = "Argument list didn't specify: {0} " msg = msg.format(", ".join([str(m.name) for m in missing])) raise CodeGenArgumentListError(msg, missing) # create redundant arguments to produce the requested sequence name_arg_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 return Routine(name, arg_list, return_val, local_vars, global_vars)
def get_with_clauses(expr): # ... def _format_str(a): if isinstance(a, str): return a.strip('\'') else: return a # ... # ... d_attributs = {} d_args = {} # ... # ... we first create a dictionary of attributs if isinstance(expr, Variable): if expr.cls_base: d_attributs = expr.cls_base.attributs_as_dict elif isinstance(expr, ConstructorCall): attrs = expr.attributs for i in attrs: d_attributs[str(i).replace('self.', '')] = i # ... # ... if not d_attributs: raise ValueError('Can not find attributs') # ... # ... if isinstance(expr, Variable): cls_base = expr.cls_base if not cls_base: return None if not (('openacc' in cls_base.options) and ('with' in cls_base.options)): return None elif isinstance(expr, ConstructorCall): # arguments[0] is 'self' # TODO must be improved in syntax, so that a['value'] is a sympy object for a in expr.arguments[1:]: if isinstance(a, dict): # we add '_' tp be conform with the private variables convention d_args['_{0}'.format(a['key'])] = a['value'] else: return None # ... # ... get initial values for all attributs # TODO do we keep 'self' hard coded? d = {} for k, v in d_attributs.items(): i = DottedName('self', k) d[k] = get_initial_value(expr, i) # ... # ... update the dictionary with the class parameters for k, v in d_args.items(): d[k] = d_args[k] # ... # ... initial values for clauses _async = None _wait = None _num_gangs = None _num_workers = None _vector_length = None _device_type = None _if = None _reduction = None _copy = None _copyin = None _copyout = None _create = None _present = None _deviceptr = None _private = None _firstprivate = None _default = None # ... # ... async if not (d['_async'] is None): if not isinstance(d['_async'], Nil): ls = d['_async'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] _async = ACC_Async(*ls) # ... # ... copy if not (d['_copy'] is None): if not isinstance(d['_copy'], Nil): ls = d['_copy'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] _copy = ACC_Copy(*ls) # ... # ... copyin if not (d['_copyin'] is None): if not isinstance(d['_copyin'], Nil): ls = d['_copyin'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] _copyin = ACC_Copyin(*ls) # ... # ... copyout if not (d['_copyout'] is None): if not isinstance(d['_copyout'], Nil): ls = d['_copyout'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] _copyout = ACC_Copyout(*ls) # ... # ... create if not (d['_create'] is None): if not isinstance(d['_create'], Nil): ls = d['_create'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] _create = ACC_Copyin(*ls) # ... # ... default if not (d['_default'] is None): if not isinstance(d['_default'], Nil): ls = d['_default'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls[0] = _format_str(ls[0]) _default = ACC_Default(*ls) # ... # ... deviceptr if not (d['_deviceptr'] is None): if not isinstance(d['_deviceptr'], Nil): ls = d['_deviceptr'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] _deviceptr = ACC_DevicePtr(*ls) # ... # ... devicetype if not (d['_device_type'] is None): if not isinstance(d['_device_type'], Nil): ls = d['_device_type'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] _device_type = ACC_DeviceType(*ls) # ... # ... firstprivate if not (d['_firstprivate'] is None): if not isinstance(d['_firstprivate'], Nil): ls = d['_firstprivate'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] _firstprivate = ACC_FirstPrivate(*ls) # ... # ... if # TODO improve this to take any boolean expression for arg. # see OpenACC specifications if not (d['_if'] is None): if not isinstance(d['_if'], Nil): arg = d['_if'] ls = [arg] _if = ACC_If(*ls) # ... # ... num_gangs # TODO improve this to take any int expression for arg. # see OpenACC specifications if not (d['_num_gangs'] is None): if not isinstance(d['_num_gangs'], Nil): arg = d['_num_gangs'] ls = [arg] _num_gangs = ACC_NumGangs(*ls) # ... # ... num_workers # TODO improve this to take any int expression for arg. # see OpenACC specifications if not (d['_num_workers'] is None): if not isinstance(d['_num_workers'], Nil): arg = d['_num_workers'] ls = [arg] _num_workers = ACC_NumWorkers(*ls) # ... # ... present if not (d['_present'] is None): if not isinstance(d['_present'], Nil): ls = d['_present'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] _present = ACC_Present(*ls) # ... # ... private if not (d['_private'] is None): if not isinstance(d['_private'], Nil): ls = d['_private'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] _private = ACC_Private(*ls) # ... # ... reduction if not (d['_reduction'] is None): if not isinstance(d['_reduction'], Nil): ls = d['_reduction'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] _reduction = ACC_Reduction(*ls) # ... # ... vector_length if not (d['_vector_length'] is None): if not isinstance(d['_vector_length'], Nil): arg = d['_vector_length'] ls = [arg] _vector_length = ACC_VectorLength(*ls) # ... # ... wait if not (d['_wait'] is None): if not isinstance(d['_wait'], Nil): ls = d['_wait'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] _wait = ACC_Wait(*ls) # ... # ... clauses = (_async, _wait, _num_gangs, _num_workers, _vector_length, _device_type, _if, _reduction, _copy, _copyin, _copyout, _create, _present, _deviceptr, _private, _firstprivate, _default) clauses = [i for i in clauses if not (i is None)] clauses = Tuple(*clauses) # ... return clauses
def __new__(cls, expr, cond): return Tuple.__new__(cls, expr, cond)
def get_for_clauses(expr): # ... def _format_str(a): if isinstance(a, str): return a.strip('\'') else: return a # ... # ... d_attributs = {} d_args = {} # ... # ... we first create a dictionary of attributs if isinstance(expr, Variable): if expr.cls_base: d_attributs = expr.cls_base.attributs_as_dict elif isinstance(expr, ConstructorCall): attrs = expr.attributs for i in attrs: d_attributs[str(i).replace('self.', '')] = i # ... # ... if not d_attributs: raise ValueError('Can not find attributs') # ... # ... if isinstance(expr, Variable): cls_base = expr.cls_base if not cls_base: return None, None if not (('openacc' in cls_base.options) and ('iterable' in cls_base.options)): return None, None elif isinstance(expr, ConstructorCall): # arguments[0] is 'self' # TODO must be improved in syntax, so that a['value'] is a sympy object for a in expr.arguments[1:]: if isinstance(a, dict): # we add '_' tp be conform with the private variables convention d_args['_{0}'.format(a['key'])] = a['value'] else: return None, None # ... # ... get initial values for all attributs # TODO do we keep 'self' hard coded? d = {} for k, v in d_attributs.items(): i = DottedName('self', k) d[k] = get_initial_value(expr, i) # ... # ... update the dictionary with the class parameters for k, v in d_args.items(): d[k] = d_args[k] # ... # ... initial values for clauses _collapse = None _gang = None _worker = None _vector = None _seq = None _auto = None _tile = None _device_type = None _independent = None _private = None _reduction = None # ... # ... auto if not (d['_auto'] is None): if not isinstance(d['_auto'], Nil): _auto = ACC_Auto() # ... # ... collapse if not (d['_collapse'] is None): if not isinstance(d['_collapse'], Nil): ls = [d['_collapse']] _collapse = ACC_Collapse(*ls) # ... # ... device_type if not (d['_device_type'] is None): if not isinstance(d['_device_type'], Nil): ls = d['_device_type'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] _device_type = ACC_DeviceType(*ls) # ... # ... gang if not (d['_gang'] is None): if not isinstance(d['_gang'], Nil): ls = d['_gang'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] _gang = ACC_Gang(*ls) # ... # ... independent if not (d['_independent'] is None): if not isinstance(d['_independent'], Nil): _independent = ACC_Independent() # ... # ... private if not (d['_private'] is None): if not isinstance(d['_private'], Nil): ls = d['_private'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] _private = ACC_Private(*ls) # ... # ... reduction if not (d['_reduction'] is None): if not isinstance(d['_reduction'], Nil): ls = d['_reduction'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] _reduction = ACC_Reduction(*ls) # ... # ... seq if not (d['_seq'] is None): if not isinstance(d['_seq'], Nil): _seq = ACC_Seq() # ... # ... tile if not (d['_tile'] is None): if not isinstance(d['_tile'], Nil): ls = d['_tile'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] _tile = ACC_Tile(*ls) # ... # ... vector if not (d['_vector'] is None): if not isinstance(d['_vector'], Nil): ls = d['_vector'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] _vector = ACC_Vector(*ls) # ... # ... worker if not (d['_worker'] is None): if not isinstance(d['_worker'], Nil): ls = d['_worker'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] _worker = ACC_Worker(*ls) # ... # ... clauses = (_collapse, _gang, _worker, _vector, _seq, _auto, _tile, _device_type, _independent, _private, _reduction) clauses = [i for i in clauses if not (i is None)] clauses = Tuple(*clauses) # ... return clauses
def __new__(cls, expr, cond): if cond == True: return Tuple.__new__(cls, expr, true) elif cond == False: return Tuple.__new__(cls, expr, false) return Tuple.__new__(cls, expr, cond)
def args(self): return (Tuple(*self._basis), Tuple(*self._options.gens))
def rational_parametrization(self, parameters=('t', 's'), reg_point=None): """ Returns the rational parametrization of implict region. Examples ======== >>> from sympy import Eq >>> from sympy.abc import x, y, z, s, t >>> from sympy.vector import ImplicitRegion >>> parabola = ImplicitRegion((x, y), y**2 - 4*x) >>> parabola.rational_parametrization() (4/t**2, 4/t) >>> circle = ImplicitRegion((x, y), Eq(x**2 + y**2, 4)) >>> circle.rational_parametrization() (4*t/(t**2 + 1), 4*t**2/(t**2 + 1) - 2) >>> I = ImplicitRegion((x, y), x**3 + x**2 - y**2) >>> I.rational_parametrization() (t**2 - 1, t*(t**2 - 1)) >>> cubic_curve = ImplicitRegion((x, y), x**3 + x**2 - y**2) >>> cubic_curve.rational_parametrization(parameters=(t)) (t**2 - 1, t*(t**2 - 1)) >>> sphere = ImplicitRegion((x, y, z), x**2 + y**2 + z**2 - 4) >>> sphere.rational_parametrization(parameters=(t, s)) (-2 + 4/(s**2 + t**2 + 1), 4*s/(s**2 + t**2 + 1), 4*t/(s**2 + t**2 + 1)) For some conics, regular_points() is unable to find a point on curve. To calulcate the parametric representation in such cases, user need to determine a point on the region and pass it using reg_point. >>> c = ImplicitRegion((x, y), (x - 1/2)**2 + (y)**2 - (1/4)**2) >>> c.rational_parametrization(reg_point=(3/4, 0)) (0.75 - 0.5/(t**2 + 1), -0.5*t/(t**2 + 1)) References ========== - Christoph M. Hoffmann, "Conversion Methods between Parametric and Implicit Curves and Surfaces", Purdue e-Pubs, 1990. Available: https://docs.lib.purdue.edu/cgi/viewcontent.cgi?article=1827&context=cstech """ equation = self.equation degree = self.degree if degree == 1: if len(self.variables) == 1: return (equation, ) elif len(self.variables) == 2: x, y = self.variables y_par = list(solveset(equation, y))[0] return x, y_par else: raise NotImplementedError() point = () # Finding the (n - 1) fold point of the monoid of degree if degree == 2: # For degree 2 curves, either a regular point or a singular point can be used. if reg_point is not None: # Using point provided by the user as regular point point = reg_point else: if len(self.singular_points()) != 0: point = list(self.singular_points())[0] else: point = self.regular_point() if len(self.singular_points()) != 0: singular_points = self.singular_points() for spoint in singular_points: syms = Tuple(*spoint).free_symbols rep = {s: 2 for s in syms} if len(syms) != 0: spoint = tuple(s.subs(rep) for s in spoint) if self.multiplicity(spoint) == degree - 1: point = spoint break if len(point) == 0: # The region in not a monoid raise NotImplementedError() modified_eq = equation # Shifting the region such that fold point moves to origin for i, var in enumerate(self.variables): modified_eq = modified_eq.subs(var, var + point[i]) modified_eq = expand(modified_eq) hn = hn_1 = 0 for term in modified_eq.args: if total_degree(term) == degree: hn += term else: hn_1 += term hn_1 = -1 * hn_1 if not isinstance(parameters, tuple): parameters = (parameters, ) if len(self.variables) == 2: parameter1 = parameters[0] if parameter1 == 's': # To avoid name conflict between parameters s = _symbol('s_', real=True) else: s = _symbol('s', real=True) t = _symbol(parameter1, real=True) hn = hn.subs({self.variables[0]: s, self.variables[1]: t}) hn_1 = hn_1.subs({self.variables[0]: s, self.variables[1]: t}) x_par = (s * (hn_1 / hn)).subs(s, 1) + point[0] y_par = (t * (hn_1 / hn)).subs(s, 1) + point[1] return x_par, y_par elif len(self.variables) == 3: parameter1, parameter2 = parameters if parameter1 == 'r' or parameter2 == 'r': # To avoid name conflict between parameters r = _symbol('r_', real=True) else: r = _symbol('r', real=True) s = _symbol(parameter2, real=True) t = _symbol(parameter1, real=True) hn = hn.subs({ self.variables[0]: r, self.variables[1]: s, self.variables[2]: t }) hn_1 = hn_1.subs({ self.variables[0]: r, self.variables[1]: s, self.variables[2]: t }) x_par = (r * (hn_1 / hn)).subs(r, 1) + point[0] y_par = (s * (hn_1 / hn)).subs(r, 1) + point[1] z_par = (t * (hn_1 / hn)).subs(r, 1) + point[2] return x_par, y_par, z_par raise NotImplementedError()
def get_with_clauses(expr): # ... def _format_str(a): if isinstance(a, str): return a.strip('\'') else: return a # ... # ... d_attributs = {} d_args = {} # ... # ... we first create a dictionary of attributs if isinstance(expr, Variable): if expr.cls_base: d_attributs = expr.cls_base.attributs_as_dict elif isinstance(expr, ConstructorCall): attrs = expr.attributs for i in attrs: d_attributs[str(i).replace('self.', '')] = i # ... # ... if not d_attributs: raise ValueError('Can not find attributs') # ... # ... if isinstance(expr, Variable): cls_base = expr.cls_base if not cls_base: return None if not(('openmp' in cls_base.options) and ('with' in cls_base.options)): return None elif isinstance(expr, ConstructorCall): # arguments[0] is 'self' # TODO must be improved in syntax, so that a['value'] is a sympy object for a in expr.arguments[1:]: if isinstance(a, dict): # we add '_' tp be conform with the private variables convention d_args['_{0}'.format(a['key'])] = a['value'] else: return None # ... # ... get initial values for all attributs # TODO do we keep 'self' hard coded? d = {} for k,v in d_attributs.items(): i = DottedName('self', k) d[k] = get_initial_value(expr, i) # ... # ... update the dictionary with the class parameters for k,v in d_args.items(): d[k] = d_args[k] # ... # ... initial values for clauses private = None firstprivate = None shared = None reduction = None copyin = None default = None proc_bind = None num_threads = None if_test = None # ... # ... private if not(d['_private'] is None): if not isinstance(d['_private'], Nil): ls = d['_private'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] private = OMP_Private(*ls) # ... # ... firstprivate if not(d['_firstprivate'] is None): if not isinstance(d['_firstprivate'], Nil): ls = d['_firstprivate'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] firstprivate = OMP_FirstPrivate(*ls) # ... # ... shared if not(d['_shared'] is None): if not isinstance(d['_shared'], Nil): ls = d['_shared'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] shared = OMP_Shared(*ls) # ... # ... reduction if not(d['_reduction'] is None): if not isinstance(d['_reduction'], Nil): ls = d['_reduction'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] reduction = OMP_Reduction(*ls) # ... # ... copyin if not(d['_copyin'] is None): if not isinstance(d['_copyin'], Nil): ls = d['_copyin'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] copyin = OMP_Copyin(*ls) # ... # ... default if not(d['_default'] is None): if not isinstance(d['_default'], Nil): ls = d['_default'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls[0] = _format_str(ls[0]) default = OMP_Default(*ls) # ... # ... proc_bind if not(d['_proc_bind'] is None): if not isinstance(d['_proc_bind'], Nil): ls = d['_proc_bind'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls[0] = _format_str(ls[0]) proc_bind = OMP_ProcBind(*ls) # ... # ... num_threads # TODO improve this to take any int expression for arg. # see OpenMP specifications for num_threads clause if not(d['_num_threads'] is None): if not isinstance(d['_num_threads'], Nil): arg = d['_num_threads'] ls = [arg] num_threads = OMP_NumThread(*ls) # ... # ... if_test # TODO improve this to take any boolean expression for arg. # see OpenMP specifications for if_test clause if not(d['_if_test'] is None): if not isinstance(d['_if_test'], Nil): arg = d['_if_test'] ls = [arg] if_test = OMP_If(*ls) # ... # ... clauses = (private, firstprivate, shared, reduction, default, copyin, proc_bind, num_threads, if_test) clauses = [i for i in clauses if not(i is None)] clauses = Tuple(*clauses) # ... return clauses
def __str__(self): from sympy.core import Tuple return self.__class__.__name__ + str(Tuple(*[x[0] for x in self.args]))
def get_for_clauses(expr): # ... def _format_str(a): if isinstance(a, str): return a.strip('\'') else: return a # ... # ... d_attributs = {} d_args = {} # ... # ... we first create a dictionary of attributs if isinstance(expr, Variable): if expr.cls_base: d_attributs = expr.cls_base.attributs_as_dict elif isinstance(expr, ConstructorCall): attrs = expr.attributs for i in attrs: d_attributs[str(i).replace('self.', '')] = i # ... # ... if not d_attributs: raise ValueError('Can not find attributs') # ... # ... if isinstance(expr, Variable): cls_base = expr.cls_base if not cls_base: return None, None if not(('openmp' in cls_base.options) and ('iterable' in cls_base.options)): return None, None elif isinstance(expr, ConstructorCall): # arguments[0] is 'self' # TODO must be improved in syntax, so that a['value'] is a sympy object for a in expr.arguments[1:]: if isinstance(a, dict): # we add '_' tp be conform with the private variables convention d_args['_{0}'.format(a['key'])] = a['value'] else: return None, None # ... # ... get initial values for all attributs # TODO do we keep 'self' hard coded? d = {} for k,v in d_attributs.items(): i = DottedName('self', k) d[k] = get_initial_value(expr, i) # ... # ... update the dictionary with the class parameters for k,v in d_args.items(): d[k] = d_args[k] # ... # ... initial values for clauses nowait = None collapse = None private = None firstprivate = None lastprivate = None reduction = None schedule = None ordered = None linear = None # ... # ... nowait nowait = d['_nowait'] # ... # ... collapse if not(d['_collapse'] is None): if not isinstance(d['_collapse'], Nil): ls = [d['_collapse']] collapse = OMP_Collapse(*ls) # ... # ... private if not(d['_private'] is None): if not isinstance(d['_private'], Nil): ls = d['_private'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] private = OMP_Private(*ls) # ... # ... firstprivate if not(d['_firstprivate'] is None): if not isinstance(d['_firstprivate'], Nil): ls = d['_firstprivate'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] firstprivate = OMP_FirstPrivate(*ls) # ... # ... lastprivate if not(d['_lastprivate'] is None): if not isinstance(d['_lastprivate'], Nil): ls = d['_lastprivate'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] lastprivate = OMP_LastPrivate(*ls) # ... # ... reduction if not(d['_reduction'] is None): if not isinstance(d['_reduction'], Nil): ls = d['_reduction'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls = [_format_str(a) for a in ls] reduction = OMP_Reduction(*ls) # ... # ... schedule if not(d['_schedule'] is None): if not isinstance(d['_schedule'], Nil): ls = d['_schedule'] if not isinstance(ls, (list, tuple, Tuple)): ls = [ls] ls[0] = _format_str(ls[0]) schedule = OMP_Schedule(*ls) # ... # ... ordered if not(d['_ordered'] is None): if not isinstance(d['_ordered'], Nil): ls = d['_ordered'] args = [] if isinstance(ls, (int, sp_Integer)): args.append(ls) ordered = OMP_Ordered(*args) # ... # ... linear if not(d['_linear'] is None): if not isinstance(d['_linear'], Nil): # we need to convert Tuple to list here ls = list(d['_linear']) if len(ls) < 2: raise ValueError('Expecting at least 2 entries, ' 'given {0}'.format(len(ls))) variables = [a.strip('\'') for a in ls[0:-1]] ls[0:-1] = variables linear = OMP_Linear(*ls) # ... # ... clauses = (private, firstprivate, lastprivate, reduction, schedule, ordered, collapse, linear) clauses = [i for i in clauses if not(i is None)] clauses = Tuple(*clauses) # ... # ... info = {} info['nowait'] = nowait # ... return info, clauses
def __str__(self): from sympy.core import Tuple return "ProductOrder" + str(Tuple(*[x[0] for x in self.args]))
def __new__(cls, partition, integer=None): """ Generates a new IntegerPartition object from a list or dictionary. Explantion ========== The partition can be given as a list of positive integers or a dictionary of (integer, multiplicity) items. If the partition is preceded by an integer an error will be raised if the partition does not sum to that given integer. Examples ======== >>> from sympy.combinatorics.partitions import IntegerPartition >>> a = IntegerPartition([5, 4, 3, 1, 1]) >>> a IntegerPartition(14, (5, 4, 3, 1, 1)) >>> print(a) [5, 4, 3, 1, 1] >>> IntegerPartition({1:3, 2:1}) IntegerPartition(5, (2, 1, 1, 1)) If the value that the partition should sum to is given first, a check will be made to see n error will be raised if there is a discrepancy: >>> IntegerPartition(10, [5, 4, 3, 1]) Traceback (most recent call last): ... ValueError: The partition is not valid """ if integer is not None: integer, partition = partition, integer if isinstance(partition, (dict, Dict)): _ = [] for k, v in sorted(list(partition.items()), reverse=True): if not v: continue k, v = as_int(k), as_int(v) _.extend([k]*v) partition = tuple(_) else: partition = tuple(sorted(map(as_int, partition), reverse=True)) sum_ok = False if integer is None: integer = sum(partition) sum_ok = True else: integer = as_int(integer) if not sum_ok and sum(partition) != integer: raise ValueError("Partition did not add to %s" % integer) if any(i < 1 for i in partition): raise ValueError("All integer summands must be greater than one") obj = Basic.__new__(cls, Integer(integer), Tuple(*partition)) obj.partition = list(partition) obj.integer = integer return obj
def _eval_subs(self, old, new): """ Substitute old with new in the integrand and the limits, but don't change anything that is (or corresponds to) a variable of integration. The normal substitution semantics -- traversing all arguments looking for matching patterns -- should not be applied to the Integrals since changing the integration variables should also entail a change in the integration limits (which should be done with the transform method). So this method just makes changes in the integrand and the limits. Not all instances of a given variable are conceptually the same: the first argument of the limit tuple and any corresponding variable in the integrand are dummy variables while every other symbol is a symbol that will be unchanged when the integral is evaluated. For example, in Integral(x + a, (a, a, b)) the dummy variables are shown below with angle-brackets around them and will not be changed by this function: Integral(x + <a>, (<a>, a, b)) If you want to change the lower limit to 1 there is no reason to prohibit this since it is not conceptually related to the integration variable, <a>. Nor is there reason to disallow changing the b to 1. If a second limit were added, however, as in: Integral(x + a, (a, a, b), (b, 1, 2)) the dummy variables become: Integral(x + <a>, (<a>, a, <b>), (<b>, a, b)) Note that the `b` of the first limit is now a dummy variable since `b` is a dummy variable in the second limit. Summary: no variable of the integrand or limit can be the target of substitution if it appears as a variable of integration in a limit positioned to the right of it. >>> from sympy import Integral >>> from sympy.abc import a, b, c, x, y >>> i = Integral(a + x, (a, a, 3), (b, x, c)) >>> list(i.free_symbols) # only these can be changed [x, a, c] >>> i.subs(a, c) # note that the variable of integration is unchanged Integral(a + x, (a, c, 3), (b, x, c)) >>> i.subs(a + x, b) == i # there is no x + a, only x + <a> True >>> i.subs(x, y - c) Integral(a + y - c, (a, a, 3), (b, y - c, c)) """ if self == old: return new integrand, limits = self.function, self.limits old_atoms = old.free_symbols limits = list(limits) # make limits explicit if they are to be targeted by old: # Integral(x, x) -> Integral(x, (x, x)) if old = x if old.is_Symbol: for i, l in enumerate(limits): if len(l) == 1 and l[0] == old: limits[i] = Tuple(l[0], l[0]) dummies = set() for i in xrange(-1, -len(limits) - 1, -1): xab = limits[i] if not dummies.intersection(old_atoms): limits[i] = Tuple(xab[0], *[l.subs(old, new) for l in xab[1:]]) dummies.add(xab[0]) if not dummies.intersection(old_atoms): integrand = integrand.subs(old, new) return Integral(integrand, *limits)
def _handle_irel(self, x, handler): """Return either None (if the conditions of self depend only on x) else a Piecewise expression whose expressions (handled by the handler that was passed) are paired with the governing x-independent relationals, e.g. Piecewise((A, a(x) & b(y)), (B, c(x) | c(y)) -> Piecewise( (handler(Piecewise((A, a(x) & True), (B, c(x) | True)), b(y) & c(y)), (handler(Piecewise((A, a(x) & True), (B, c(x) | False)), b(y)), (handler(Piecewise((A, a(x) & False), (B, c(x) | True)), c(y)), (handler(Piecewise((A, a(x) & False), (B, c(x) | False)), True)) """ # identify governing relationals rel = self.atoms(Relational) irel = list( ordered([ r for r in rel if x not in r.free_symbols and r not in (S.true, S.false) ])) if irel: args = {} exprinorder = [] for truth in product((1, 0), repeat=len(irel)): reps = dict(zip(irel, truth)) # only store the true conditions since the false are implied # when they appear lower in the Piecewise args if 1 not in truth: cond = None # flag this one so it doesn't get combined else: andargs = Tuple(*[i for i in reps if reps[i]]) free = list(andargs.free_symbols) if len(free) == 1: from sympy.solvers.inequalities import ( reduce_inequalities, _solve_inequality) try: t = reduce_inequalities(andargs, free[0]) # ValueError when there are potentially # nonvanishing imaginary parts except (ValueError, NotImplementedError): # at least isolate free symbol on left t = And(*[ _solve_inequality(a, free[0], linear=True) for a in andargs ]) else: t = And(*andargs) if t is S.false: continue # an impossible combination cond = t expr = handler(self.xreplace(reps)) if isinstance(expr, self.func) and len(expr.args) == 1: expr, econd = expr.args[0] cond = And(econd, True if cond is None else cond) # the ec pairs are being collected since all possibilities # are being enumerated, but don't put the last one in since # its expr might match a previous expression and it # must appear last in the args if cond is not None: args.setdefault(expr, []).append(cond) # but since we only store the true conditions we must maintain # the order so that the expression with the most true values # comes first exprinorder.append(expr) # convert collected conditions as args of Or for k in args: args[k] = Or(*args[k]) # take them in the order obtained args = [(e, args[e]) for e in uniq(exprinorder)] # add in the last arg args.append((expr, True)) # if any condition reduced to True, it needs to go last # and there should only be one of them or else the exprs # should agree trues = [i for i in range(len(args)) if args[i][1] is S.true] if not trues: # make the last one True since all cases were enumerated e, c = args[-1] args[-1] = (e, S.true) else: assert len(set([e for e, c in [args[i] for i in trues]])) == 1 args.append(args.pop(trues.pop())) while trues: args.pop(trues.pop()) return Piecewise(*args)
def test_has(): a, b, c = symbols("a, b, c", cls=Dummy) f = Hyper_Function([2, -a], [b]) assert f.has(a) assert f.has(Tuple(b)) assert not f.has(c)
def cse(exprs, symbols=None, optimizations=None, postprocess=None, order='canonical', ignore=()): """ Perform common subexpression elimination on an expression. Parameters ========== exprs : list of sympy expressions, or a single sympy expression The expressions to reduce. symbols : infinite iterator yielding unique Symbols The symbols used to label the common subexpressions which are pulled out. The ``numbered_symbols`` generator is useful. The default is a stream of symbols of the form "x0", "x1", etc. This must be an infinite iterator. optimizations : list of (callable, callable) pairs The (preprocessor, postprocessor) pairs of external optimization functions. Optionally 'basic' can be passed for a set of predefined basic optimizations. Such 'basic' optimizations were used by default in old implementation, however they can be really slow on larger expressions. Now, no pre or post optimizations are made by default. postprocess : a function which accepts the two return values of cse and returns the desired form of output from cse, e.g. if you want the replacements reversed the function might be the following lambda: lambda r, e: return reversed(r), e order : string, 'none' or 'canonical' The order by which Mul and Add arguments are processed. If set to 'canonical', arguments will be canonically ordered. If set to 'none', ordering will be faster but dependent on expressions hashes, thus machine dependent and variable. For large expressions where speed is a concern, use the setting order='none'. ignore : iterable of Symbols Substitutions containing any Symbol from ``ignore`` will be ignored. Returns ======= replacements : list of (Symbol, expression) pairs All of the common subexpressions that were replaced. Subexpressions earlier in this list might show up in subexpressions later in this list. reduced_exprs : list of sympy expressions The reduced expressions with all of the replacements above. Examples ======== >>> from sympy import cse, SparseMatrix >>> from sympy.abc import x, y, z, w >>> cse(((w + x + y + z)*(w + y + z))/(w + x)**3) ([(x0, w + y + z)], [x0*(x + x0)/(w + x)**3]) Note that currently, y + z will not get substituted if -y - z is used. >>> cse(((w + x + y + z)*(w - y - z))/(w + x)**3) ([(x0, w + x)], [(w - y - z)*(x0 + y + z)/x0**3]) List of expressions with recursive substitutions: >>> m = SparseMatrix([x + y, x + y + z]) >>> cse([(x+y)**2, x + y + z, y + z, x + z + y, m]) ([(x0, x + y), (x1, x0 + z)], [x0**2, x1, y + z, x1, Matrix([ [x0], [x1]])]) Note: the type and mutability of input matrices is retained. >>> isinstance(_[1][-1], SparseMatrix) True The user may disallow substitutions containing certain symbols: >>> cse([y**2*(x + 1), 3*y**2*(x + 1)], ignore=(y,)) ([(x0, x + 1)], [x0*y**2, 3*x0*y**2]) """ from sympy.matrices import (MatrixBase, Matrix, ImmutableMatrix, SparseMatrix, ImmutableSparseMatrix) # Handle the case if just one expression was passed. if isinstance(exprs, (Basic, MatrixBase)): exprs = [exprs] copy = exprs temp = [] for e in exprs: if isinstance(e, (Matrix, ImmutableMatrix)): temp.append(Tuple(*e._mat)) elif isinstance(e, (SparseMatrix, ImmutableSparseMatrix)): temp.append(Tuple(*e._smat.items())) else: temp.append(e) exprs = temp del temp if optimizations is None: optimizations = list() elif optimizations == 'basic': optimizations = basic_optimizations # Preprocess the expressions to give us better optimization opportunities. reduced_exprs = [preprocess_for_cse(e, optimizations) for e in exprs] excluded_symbols = set().union( *[expr.atoms(Symbol) for expr in reduced_exprs]) if symbols is None: symbols = numbered_symbols() else: # In case we get passed an iterable with an __iter__ method instead of # an actual iterator. symbols = iter(symbols) symbols = filter_symbols(symbols, excluded_symbols) # Find other optimization opportunities. opt_subs = opt_cse(reduced_exprs, order) # Main CSE algorithm. replacements, reduced_exprs = tree_cse(reduced_exprs, symbols, opt_subs, order, ignore) # Postprocess the expressions to return the expressions to canonical form. exprs = copy for i, (sym, subtree) in enumerate(replacements): subtree = postprocess_for_cse(subtree, optimizations) replacements[i] = (sym, subtree) reduced_exprs = [ postprocess_for_cse(e, optimizations) for e in reduced_exprs ] # Get the matrices back for i, e in enumerate(exprs): if isinstance(e, (Matrix, ImmutableMatrix)): reduced_exprs[i] = Matrix(e.rows, e.cols, reduced_exprs[i]) if isinstance(e, ImmutableMatrix): reduced_exprs[i] = reduced_exprs[i].as_immutable() elif isinstance(e, (SparseMatrix, ImmutableSparseMatrix)): m = SparseMatrix(e.rows, e.cols, {}) for k, v in reduced_exprs[i]: m[k] = v if isinstance(e, ImmutableSparseMatrix): m = m.as_immutable() reduced_exprs[i] = m if postprocess is None: return replacements, reduced_exprs return postprocess(replacements, reduced_exprs)