def length_rational_fraction(self, var='b'): r""" Return the generating series for the number of lengths with the given boundaries """ from sage.symbolic.ring import SR F = SR.one() for dart in range(self._total_darts): if not self._active_darts[dart]: continue i = self._dart_to_edge_index[dart] j1, j2 = self._edge_cycles[i] if j1 == dart: continue else: assert j2 == dart f1 = self._dart_to_face_index[j1] f2 = self._dart_to_face_index[j2] b1 = SR.var('%s%d' %(var, f1)) b2 = SR.var('%s%d' %(var, f2)) F *= b1*b2 / (1 - b1*b2) return F
def mma_free_integrator(expression, v, a=None, b=None): """ sage: from sage.symbolic.integration.external import mma_free_integrator sage: mma_free_integrator(sin(x), x) # optional - internet -cos(x) """ import urllib, re # We need to integrate against x vars = [str(x) for x in expression.variables()] if any(len(x)>1 for x in vars): raise NotImplementedError("Mathematica online integrator can only handle single letter variables.") x = SR.var('x') if repr(v) != 'x': for i in range(ord('a'), ord('z')+1): if chr(i) not in vars: shadow_x = SR.var(chr(i)) break expression = expression.subs({x:shadow_x}).subs({dvar: x}) params = urllib.urlencode({'expr': expression._mathematica_init_(), 'random': 'false'}) page = urllib.urlopen("http://integrals.wolfram.com/index.jsp", params).read() page = page[page.index('"inputForm"'):page.index('"outputForm"')] page = re.sub("\s", "", page) mexpr = re.match(r".*Integrate.*==</em><br/>(.*)</p>", page).groups()[0] try: ans = SR(mexpr.lower().replace('[', '(').replace(']', ')')) if repr(v) != 'x': ans = ans.subs({x:v}).subs({shadow_x:x}) return ans except TypeError: raise ValueError("Unable to parse: %s" % mexpr)
def laplace(cls, self, parameters, variable, x='x', s='t'): r""" Returns the Laplace transform of self with respect to the variable var. INPUT: - ``x`` - variable of self - ``s`` - variable of Laplace transform. We assume that a piecewise function is 0 outside of its domain and that the left-most endpoint of the domain is 0. EXAMPLES:: sage: x, s, w = var('x, s, w') sage: f = piecewise([[(0,1),1],[[1,2], 1-x]]) sage: f.laplace(x, s) -e^(-s)/s + (s + 1)*e^(-2*s)/s^2 + 1/s - e^(-s)/s^2 sage: f.laplace(x, w) -e^(-w)/w + (w + 1)*e^(-2*w)/w^2 + 1/w - e^(-w)/w^2 :: sage: y, t = var('y, t') sage: f = piecewise([[[1,2], 1-y]]) sage: f.laplace(y, t) (t + 1)*e^(-2*t)/t^2 - e^(-t)/t^2 :: sage: s = var('s') sage: t = var('t') sage: f1(t) = -t sage: f2(t) = 2 sage: f = piecewise([[[0,1],f1],[(1,infinity),f2]]) sage: f.laplace(t,s) (s + 1)*e^(-s)/s^2 + 2*e^(-s)/s - 1/s^2 """ from sage.all import assume, exp, forget x = SR.var(x) s = SR.var(s) assume(s>0) result = 0 for domain, f in parameters: for interval in domain: a = interval.lower() b = interval.upper() result += (SR(f)*exp(-s*x)).integral(x,a,b) forget(s>0) return result
def spin_polynomial(part, weight, length): """ Returns the spin polynomial associated to ``part``, ``weight``, and ``length``. EXAMPLES:: sage: from sage.combinat.ribbon_tableau import spin_polynomial sage: spin_polynomial([6,6,6],[4,2],3) t^6 + t^5 + 2*t^4 + t^3 + t^2 sage: spin_polynomial([6,6,6],[4,1,1],3) t^6 + 2*t^5 + 3*t^4 + 2*t^3 + t^2 sage: spin_polynomial([3,3,3,2,1], [2,2], 3) t^(7/2) + t^(5/2) sage: spin_polynomial([3,3,3,2,1], [2,1,1], 3) 2*t^(7/2) + 2*t^(5/2) + t^(3/2) sage: spin_polynomial([3,3,3,2,1], [1,1,1,1], 3) 3*t^(7/2) + 5*t^(5/2) + 3*t^(3/2) + sqrt(t) sage: spin_polynomial([5,4,3,2,1,1,1], [2,2,1], 3) 2*t^(9/2) + 6*t^(7/2) + 2*t^(5/2) sage: spin_polynomial([[6]*6, [3,3]], [4,4,2], 3) 3*t^9 + 5*t^8 + 9*t^7 + 6*t^6 + 3*t^5 """ from sage.symbolic.ring import SR sp = spin_polynomial_square(part,weight,length) t = SR.var('t') c = sp.coefficients(sparse=False) return sum([c[i]*t**(QQ(i)/2) for i in range(len(c))])
def fourier_series_sine_coefficient(cls, self, parameters, variable, n, L): r""" Returns the n-th Fourier series coefficient of `\sin(n\pi x/L)`, `b_n`. INPUT: - ``self`` - the function f(x), defined over -L x L - ``n`` - an integer n0 - ``L`` - (the period)/2 OUTPUT: `b_n = \frac{1}{L}\int_{-L}^L f(x)\sin(n\pi x/L)dx` EXAMPLES:: sage: f(x) = x^2 sage: f = piecewise([[(-1,1),f]]) sage: f.fourier_series_sine_coefficient(2,1) # L=1, n=2 0 """ from sage.all import sin, pi x = SR.var('x') result = 0 for domain, f in parameters: for interval in domain: a = interval.lower() b = interval.upper() result += (f*sin(pi*x*n/L)/L).integrate(x, a, b) return SR(result).simplify_trig()
def show(self, show_hyperboloid=True, **graphics_options): r""" Plot ``self``. EXAMPLES:: sage: from sage.geometry.hyperbolic_space.hyperbolic_geodesic import * sage: g = HyperbolicPlane().HM().random_geodesic() sage: g.show() Graphics3d Object """ x = SR.var('x') opts = self.graphics_options() opts.update(graphics_options) v1, u2 = [vector(k.coordinates()) for k in self.endpoints()] # Lorentzian Gram Shmidt. The original vectors will be # u1, u2 and the orthogonal ones will be v1, v2. Except # v1 = u1, and I don't want to declare another variable, # hence the odd naming convention above. # We need the Lorentz dot product of v1 and u2. v1_ldot_u2 = u2[0]*v1[0] + u2[1]*v1[1] - u2[2]*v1[2] v2 = u2 + v1_ldot_u2 * v1 v2_norm = sqrt(v2[0]**2 + v2[1]**2 - v2[2]**2) v2 = v2 / v2_norm v2_ldot_u2 = u2[0]*v2[0] + u2[1]*v2[1] - u2[2]*v2[2] # Now v1 and v2 are Lorentz orthogonal, and |v1| = -1, |v2|=1 # That is, v1 is unit timelike and v2 is unit spacelike. # This means that cosh(x)*v1 + sinh(x)*v2 is unit timelike. hyperbola = cosh(x)*v1 + sinh(x)*v2 endtime = arcsinh(v2_ldot_u2) from sage.plot.plot3d.all import parametric_plot3d pic = parametric_plot3d(hyperbola, (x, 0, endtime), **graphics_options) if show_hyperboloid: pic += self._model.get_background_graphic() return pic
def random_expr( size, nvars=1, ncoeffs=None, var_frac=0.5, internal=full_internal, nullary=full_nullary, nullary_frac=0.2, coeff_generator=QQ.random_element, verbose=False, ): r""" Produce a random symbolic expression of the given size. By default, the expression involves (at most) one variable, an arbitrary number of coefficients, and all of the symbolic functions and constants (from the probability lists full_internal and full_nullary). It is possible to adjust the ratio of leaves between symbolic constants, variables, and coefficients (var_frac gives the fraction of variables, and nullary_frac the fraction of symbolic constants; the remaining leaves are coefficients). The actual mix of symbolic constants and internal nodes can be modified by specifying different probability lists. To use a different type for coefficients, you can specify coeff_generator, which should be a function that will return a random coefficient every time it is called. This function will often raise an error because it tries to create an erroneous expression (such as a division by zero). EXAMPLES:: sage: from sage.symbolic.random_tests import * sage: set_random_seed(53) sage: random_expr(50, nvars=3, coeff_generator=CDF.random_element) # random (v1^(0.97134084277 + 0.195868299334*I)/csc(-pi + v1^2 + v3) + sgn(1/ ((-v3 - 0.760455994772 - 0.554367254855*I)*erf(v3 + 0.982759757946 - 0.0352136502348*I)) + binomial(arccoth(v1^pi), 0.760455994772 + 0.554367254855*I) + arccosh(2*v2 - (v2 + 0.841911550437 - 0.303757179824*I)/sinh_integral(pi) + arccoth(v3 + 0.530133230474 + 0.532140303485*I))))/v2 sage: random_expr(5, verbose=True) # random About to apply <built-in function inv> to [31] About to apply sgn to [v1] About to apply <built-in function add> to [1/31, sgn(v1)] sgn(v1) + 1/31 """ vars = [(1.0, SR.var("v%d" % (n + 1))) for n in range(nvars)] if ncoeffs is None: ncoeffs = size coeffs = [(1.0, coeff_generator()) for _ in range(ncoeffs)] leaves = [(var_frac, vars), (1.0 - var_frac - nullary_frac, coeffs), (nullary_frac, nullary)] leaves = normalize_prob_list(leaves) internal = normalize_prob_list(internal) return random_expr_helper(size, internal, leaves, verbose)
def test_issue_4023(): from sage.symbolic.ring import SR from sage.functions.all import log from sympy import integrate, simplify a,x = SR.var("a x") i = integrate(log(x)/a, (x, a, a + 1)) i2 = simplify(i) s = SR(i2) assert s == (a*log(1 + a) - a*log(a) + log(1 + a) - 1)/a
def _sympysage_symbol(self): """ EXAMPLES:: sage: from sympy import Symbol sage: assert x._sympy_() == Symbol('x') sage: assert x == Symbol('x')._sage_() """ from sage.symbolic.ring import SR return SR.var(self.name)
def check_expression(expr, var_symbols, only_from_sympy=False): """ Does ``eval(expr)`` both in Sage and SymPy and does other checks. EXAMPLES:: sage: from sage.interfaces.sympy import check_expression sage: check_expression("1.123*x", "x") """ from sage import __dict__ as sagedict from sage.symbolic.ring import SR from sympy import (__dict__ as sympydict, Basic, S, var as svar) # evaluate the expression in the context of Sage: if var_symbols: SR.var(var_symbols) is_different = False try: e_sage = SR(expr) assert not isinstance(e_sage, Basic) except (NameError, TypeError): is_different = True pass # evaluate the expression in the context of SymPy: if var_symbols: sympy_vars = svar(var_symbols) b = globals().copy() b.update(sympydict) assert "sin" in b b.update(sympydict) e_sympy = eval(expr, b) assert isinstance(e_sympy, Basic) # Sympy func may have specific _sage_ method if is_different: _sage_method = getattr(e_sympy.func, "_sage_") e_sage = _sage_method(S(e_sympy)) # Do the actual checks: if not only_from_sympy: assert S(e_sage) == e_sympy assert e_sage == SR(e_sympy)
def matrice_systeme(systeme, variables): """ Renvoie une matrice par block représentant un programme linéaire sous forme standard. INPUT:: - ``systeme`` -- Un programme linéaire sous forme standard - ``variables`` -- La liste des variables du système EXAMPLES:: sage: x = x1,x2,x3 = var('x1,x2,x3') sage: Chvatal13 = [[2*x1 + 3*x2 + x3 <= 5, ....: 4*x1 + x2 + 2*x3 <= 11, ....: 3*x1 + 4*x2 + 2*x3 <= 8], ....: 5*x1 + 4*x2 + 3*x3] sage: m = matrice_systeme(Chvatal13, x); m [ z|s1 s2 s3|x1 x2 x3| 0] [--+--------+--------+--] [ 1| 0 0 0|-5 -4 -3| 0] [--+--------+--------+--] [ 0| 1 0 0| 2 3 1| 5] [ 0| 0 1 0| 4 1 2|11] [ 0| 0 0 1| 3 4 2| 8] """ def liste_coeffs(expression): return [expression.coeff(v) for v in variables] inequations = systeme[0] m = matrix([liste_coeffs(ineq.lhs()) for ineq in inequations]) rhs = vector(ineq.rhs() for ineq in inequations).column() slack = SR.var(",".join("s%s" % i for i in range(1, len(inequations) + 1))) z = SR.var("z") return block_matrix( [ [z, matrix([slack]), matrix([variables]), ZZ(0)], [ZZ(1), ZZ(0), -matrix([liste_coeffs(systeme[1])]), ZZ(0)], [ZZ(0), ZZ(1), m, rhs], ] )
def test_undefined_function(): from sage.symbolic.ring import SR from sage.calculus.var import function from sympy import Symbol, Function f = function('f') sf = Function('f') x,y = SR.var('x y') sx = Symbol('x') sy = Symbol('y') assert f(x)._sympy_() == sf(sx) assert f(x) == sf(sx)._sage_() assert f(x,y)._sympy_() == sf(sx, sy) assert f(x,y) == sf(sx, sy)._sage_() assert f._sympy_() == sf assert f == sf._sage_()
def weight_integrand(self, simplify_factor=True): """ Weight integrand as a rational function. The Jacobian determinant of some coordinate transformation. """ def arg(x,y): return arctan(y/x) # up to a constant, but it doesn't matter def phi(x,y,a,b): z = (a+I*b-x-I*y)*(a - I*b - x - I*y) w = z.real() q = z.imag() return arg(w,q).full_simplify() n = len(self.internal_vertices()) coordinates = lambda v: SR.var(chr(97+2*(v-1)) + ',' + chr(97+2*(v-1)+1)) \ if v in self.internal_vertices() else \ [(0,0), (1,0)][self.ground_vertices().index(v)] internal_coordinates = sum((list(coordinates(v)) for v in sorted(self.internal_vertices())), []) U = CoordinatePatch(internal_coordinates) F = DifferentialForms(U) psi = 0 two_forms = [] for v in self.internal_vertices(): x,y = coordinates(v) outgoing_edges = self.outgoing_edges([v]) left_target = filter(lambda (x, y, z): z == 'L', outgoing_edges)[0][1] right_target = filter(lambda (x, y, z): z == 'R', outgoing_edges)[0][1] one_forms = [] for target in [left_target, right_target]: a,b = coordinates(target) one_form = DifferentialForm(F, 1) for v in internal_coordinates: index = internal_coordinates.index(v) one_form[index] = phi(x,y,a,b).diff(v) if simplify_factor: one_form[index] = SR(one_form[index]).full_simplify() one_forms.append(one_form) two_form = one_forms[0]*one_forms[1] two_forms.append(two_form) import operator two_n_form = reduce(operator.mul, two_forms, 1) return two_n_form[range(0,2*n)]
def fourier_series_cosine_coefficient(cls, self, parameters, variable, n, L): r""" Returns the n-th Fourier series coefficient of `\cos(n\pi x/L)`, `a_n`. INPUT: - ``self`` - the function f(x), defined over -L x L - ``n`` - an integer n=0 - ``L`` - (the period)/2 OUTPUT: `a_n = \frac{1}{L}\int_{-L}^L f(x)\cos(n\pi x/L)dx` EXAMPLES:: sage: f(x) = x^2 sage: f = piecewise([[(-1,1),f]]) sage: f.fourier_series_cosine_coefficient(2,1) pi^(-2) sage: f(x) = x^2 sage: f = piecewise([[(-pi,pi),f]]) sage: f.fourier_series_cosine_coefficient(2,pi) 1 sage: f1(x) = -1 sage: f2(x) = 2 sage: f = piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]]) sage: f.fourier_series_cosine_coefficient(5,pi) -3/5/pi """ from sage.all import cos, pi x = SR.var('x') result = 0 for domain, f in parameters: for interval in domain: a = interval.lower() b = interval.upper() result += (f*cos(pi*x*n/L)/L).integrate(x, a, b) return SR(result).simplify_trig()
def __init__(self, n, delta=0.01, m=None): """ Construct LWE instance parameterised by security parameter ``n`` where the modulus ``q`` and the ``stddev`` of the noise is chosen as in [LP2011]_. INPUT: - ``n`` - security parameter (integer > 0) - ``delta`` - error probability per symbol (default: 0.01) - ``m`` - number of allowed samples or ``None`` in which case ``m=2*n + 128`` as in [LP2011]_ (default: ``None``) EXAMPLES:: sage: from sage.crypto.lwe import LindnerPeikert sage: LindnerPeikert(n=20) LWE(20, 2053, Discrete Gaussian sampler over the Integers with sigma = 3.600954 and c = 0, 'noise', 168) """ if m is None: m = 2*n + 128 # Find c>=1 such that c*exp((1-c**2)/2))**(2*n) == 2**-40 # (c*exp((1-c**2)/2))**(2*n) == 2**-40 # log((c*exp((1-c**2)/2))**(2*n)) == -40*log(2) # (2*n)*log(c*exp((1-c**2)/2)) == -40*log(2) # 2*n*(log(c)+log(exp((1-c**2)/2))) == -40*log(2) # 2*n*(log(c)+(1-c**2)/2) == -40*log(2) # 2*n*log(c)+n*(1-c**2) == -40*log(2) # 2*n*log(c)+n*(1-c**2) + 40*log(2) == 0 c = SR.var('c') c = find_root(2*n*log(c)+n*(1-c**2) + 40*log(2) == 0, 1, 10) # Upper bound on s**2/t s_t_bound = (sqrt(2) * pi / c / sqrt(2*n*log(2/delta))).n() # Interpretation of "choose q just large enough to allow for a Gaussian parameter s>=8" in [LP2011]_ q = next_prime(floor(2**round(log(256 / s_t_bound, 2)))) # Gaussian parameter as defined in [LP2011]_ s = sqrt(s_t_bound*floor(q/4)) # Transform s into stddev stddev = s/sqrt(2*pi.n()) D = DiscreteGaussianDistributionIntegerSampler(stddev) LWE.__init__(self, n=n, q=q, D=D, secret_dist='noise', m=m)
def __call__(self, *args): """ EXAMPLES:: sage: from sage.symbolic.operators import FDerivativeOperator sage: x,y = var('x,y') sage: f = function('foo') sage: op = FDerivativeOperator(f, [0,1]) sage: op(x,y) diff(foo(x, y), x, y) sage: op(x,x^2) D[0, 1](foo)(x, x^2) TESTS: We should be able to operate on functions evaluated at a point, not just a symbolic variable, :trac:`12796`:: sage: from sage.symbolic.operators import FDerivativeOperator sage: f = function('f') sage: op = FDerivativeOperator(f, [0]) sage: op(1) D[0](f)(1) """ if (not all(is_SymbolicVariable(x) for x in args) or len(args) != len(set(args))): # An evaluated derivative of the form f'(1) is not a # symbolic variable, yet we would like to treat it # like one. So, we replace the argument `1` with a # temporary variable e.g. `t0` and then evaluate the # derivative f'(t0) symbolically at t0=1. See trac # #12796. temp_args=[SR.var("t%s"%i) for i in range(len(args))] vars=[temp_args[i] for i in self._parameter_set] return self._f(*temp_args).diff(*vars).function(*temp_args)(*args) vars = [args[i] for i in self._parameter_set] return self._f(*args).diff(*vars)
def __call__(cls, self, parameters, variable, value=None, **kwds): """ Call the piecewise function EXAMPLES:: sage: f = piecewise([([0,0], sin(x)), ((0,2), cos(x))]); f piecewise(x|-->sin(x) on {0}, x|-->cos(x) on (0, 2); x) sage: f(0) 0 sage: f(1) cos(1) sage: f(2) Traceback (most recent call last): ... ValueError: point 2 is not in the domain """ self = piecewise(parameters, var=variable) substitution = dict() for k, v in kwds.items(): substitution[SR.var(k)] = v if value is not None: substitution[variable] = value return self.subs(substitution)
def __init__(self, N, delta=0.01, m=None): """ Construct a Ring-LWE oracle in dimension ``n=phi(N)`` where the modulus ``q`` and the ``stddev`` of the noise is chosen as in [LP2011]_. INPUT: - ``N`` - index of cyclotomic polynomial (integer > 0, must be power of 2) - ``delta`` - error probability per symbol (default: 0.01) - ``m`` - number of allowed samples or ``None`` in which case ``3*n`` is used (default: ``None``) EXAMPLES:: sage: from sage.crypto.lwe import RingLindnerPeikert sage: RingLindnerPeikert(N=16) RingLWE(16, 1031, Discrete Gaussian sampler for polynomials of degree < 8 with σ=2.803372 in each component, x^8 + 1, 'noise', 24) """ n = euler_phi(N) if m is None: m = 3*n # Find c>=1 such that c*exp((1-c**2)/2))**(2*n) == 2**-40 # i.e c>=1 such that 2*n*log(c)+n*(1-c**2) + 40*log(2) == 0 c = SR.var('c') c = find_root(2*n*log(c)+n*(1-c**2) + 40*log(2) == 0, 1, 10) # Upper bound on s**2/t s_t_bound = (sqrt(2) * pi / c / sqrt(2*n*log(2/delta))).n() # Interpretation of "choose q just large enough to allow for a Gaussian parameter s>=8" in [LP2011]_ q = next_prime(floor(2**round(log(256 / s_t_bound, 2)))) # Gaussian parameter as defined in [LP2011]_ s = sqrt(s_t_bound*floor(q/4)) # Transform s into stddev stddev = s/sqrt(2*pi.n()) D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], n, stddev) RingLWE.__init__(self, N=N, q=q, D=D, poly=None, secret_dist='noise', m=m)
def inverse(self, chartname1=None, chartname2=None): r""" Returns the inverse diffeomorphism. INPUT: - ``chartname1`` -- (default: None) string defining the chart in which the computation of the inverse is performed; if none is provided, the default chart of self.manifold1 will be used - ``chartname2`` -- (default: None) string defining the chart in which the computation of the inverse is performed; if none is provided, the default chart of self.manifold2 will be used OUTPUT: - the inverse diffeomorphism EXAMPLES: The inverse of a rotation in the plane:: sage: m = Manifold(2, "plane") sage: c_cart = Chart(m, 'x y', 'cart') sage: # A pi/3 rotation around the origin: sage: rot = Diffeomorphism(m, m, ((x - sqrt(3)*y)/2, (sqrt(3)*x + y)/2)) sage: p = Point(m,(1,2)) sage: q = rot(p) sage: irot = rot.inverse() sage: p1 = irot(q) sage: p1 == p True """ from sage.symbolic.ring import SR from sage.symbolic.relation import solve from utilities import simplify_chain if self._inverse is not None: return self._inverse if chartname1 is None: chartname1 = self.manifold1.def_chart.name if chartname2 is None: chartname2 = self.manifold2.def_chart.name coord_map = self.coord_expression[(chartname1, chartname2)] chart1 = self.manifold1.atlas[chartname1] chart2 = self.manifold2.atlas[chartname2] n1 = len(chart1.xx) n2 = len(chart2.xx) # New symbolic variables (different from chart2.xx to allow for a # correct solution even when chart2 = chart1): x2 = [ SR.var('xxxx' + str(i)) for i in range(n2) ] equations = [x2[i] == coord_map.functions[i] for i in range(n2) ] solutions = solve(equations, chart1.xx, solution_dict=True) if len(solutions) == 0: raise ValueError("No solution found") if len(solutions) > 1: raise ValueError("Non-unique solution found") #!# This should be the Python 2.7 form: # substitutions = {x2[i]: chart2.xx[i] for i in range(n2)} # # Here we use a form compatible with Python 2.6: substitutions = dict([(x2[i], chart2.xx[i]) for i in range(n2)]) inv_functions = [solutions[0][chart1.xx[i]].subs(substitutions) for i in range(n1)] for i in range(n1): x = inv_functions[i] try: inv_functions[i] = simplify_chain(x) except AttributeError: pass self._inverse = Diffeomorphism(self.manifold2, self.manifold1, inv_functions, chartname2, chartname1) return self._inverse
def bezier3d(path, **options): """ Draw a 3-dimensional bezier path. Input is similar to bezier_path, but each point in the path and each control point is required to have 3 coordinates. INPUT: - ``path`` -- a list of curves, which each is a list of points. See further detail below. - ``thickness`` -- (default: 2) - ``color`` -- a string (``"red"``, ``"green"`` etc) or a tuple (r, g, b) with r, g, b numbers between 0 and 1 - ``opacity`` -- (default: 1) if less than 1 then is transparent - ``aspect_ratio`` -- (default:[1,1,1]) The path is a list of curves, and each curve is a list of points. Each point is a tuple (x,y,z). The first curve contains the endpoints as the first and last point in the list. All other curves assume a starting point given by the last entry in the preceding list, and take the last point in the list as their opposite endpoint. A curve can have 0, 1 or 2 control points listed between the endpoints. In the input example for path below, the first and second curves have 2 control points, the third has one, and the fourth has no control points:: path = [[p1, c1, c2, p2], [c3, c4, p3], [c5, p4], [p5], ...] In the case of no control points, a straight line will be drawn between the two endpoints. If one control point is supplied, then the curve at each of the endpoints will be tangent to the line from that endpoint to the control point. Similarly, in the case of two control points, at each endpoint the curve will be tangent to the line connecting that endpoint with the control point immediately after or immediately preceding it in the list. So in our example above, the curve between p1 and p2 is tangent to the line through p1 and c1 at p1, and tangent to the line through p2 and c2 at p2. Similarly, the curve between p2 and p3 is tangent to line(p2,c3) at p2 and tangent to line(p3,c4) at p3. Curve(p3,p4) is tangent to line(p3,c5) at p3 and tangent to line(p4,c5) at p4. Curve(p4,p5) is a straight line. EXAMPLES:: sage: path = [[(0,0,0),(.5,.1,.2),(.75,3,-1),(1,1,0)],[(.5,1,.2),(1,.5,0)],[(.7,.2,.5)]] sage: b = bezier3d(path, color='green') sage: b Graphics3d Object To construct a simple curve, create a list containing a single list:: sage: path = [[(0,0,0),(1,0,0),(0,1,0),(0,1,1)]] sage: curve = bezier3d(path, thickness=5, color='blue') sage: curve Graphics3d Object """ from . import parametric_plot3d as P3D from sage.modules.free_module_element import vector from sage.symbolic.ring import SR p0 = vector(path[0][-1]) t = SR.var('t') if len(path[0]) > 2: B = (1-t)**3*vector(path[0][0])+3*t*(1-t)**2*vector(path[0][1])+3*t**2*(1-t)*vector(path[0][-2])+t**3*p0 G = P3D.parametric_plot3d(list(B), (0, 1), color=options['color'], aspect_ratio=options['aspect_ratio'], thickness=options['thickness'], opacity=options['opacity']) else: G = line3d([path[0][0], p0], color=options['color'], thickness=options['thickness'], opacity=options['opacity']) for curve in path[1:]: if len(curve) > 1: p1 = vector(curve[0]) p2 = vector(curve[-2]) p3 = vector(curve[-1]) B = (1-t)**3*p0+3*t*(1-t)**2*p1+3*t**2*(1-t)*p2+t**3*p3 G += P3D.parametric_plot3d(list(B), (0, 1), color=options['color'], aspect_ratio=options['aspect_ratio'], thickness=options['thickness'], opacity=options['opacity']) else: G += line3d([p0,curve[0]], color=options['color'], thickness=options['thickness'], opacity=options['opacity']) p0 = curve[-1] return G
def __classcall_private__(cls, universe, *predicates, vars=None, names=None, category=None): r""" Normalize init arguments. TESTS:: sage: ConditionSet(ZZ, names=["x"]) is ConditionSet(ZZ, names=x) True sage: ConditionSet(RR, x > 0, names=x) is ConditionSet(RR, (x > 0).function(x)) True """ if category is None: category = Sets() if isinstance(universe, Parent): if universe in Sets().Finite(): category &= Sets().Finite() if universe in EnumeratedSets(): category &= EnumeratedSets() if vars is not None: if names is not None: raise ValueError( 'cannot use names and vars at the same time; they are aliases' ) names, vars = vars, None if names is not None: names = normalize_names(-1, names) callable_symbolic_predicates = [] other_predicates = [] for predicate in predicates: if isinstance(predicate, Expression) and predicate.is_callable(): if names is None: names = tuple(str(var) for var in predicate.args()) elif len(names) != len(predicate.args()): raise TypeError('mismatch in number of arguments') if vars is None: vars = predicate.args() callable_symbolic_predicates.append(predicate) elif isinstance(predicate, Expression): if names is None: raise TypeError( 'use callable symbolic expressions or provide variable names' ) if vars is None: from sage.symbolic.ring import SR vars = tuple(SR.var(name) for name in names) callable_symbolic_predicates.append(predicate.function(*vars)) else: other_predicates.append(predicate) predicates = list( _stable_uniq(callable_symbolic_predicates + other_predicates)) if not other_predicates and not callable_symbolic_predicates: if names is None and category is None: # No conditions, no variable names, no category, just use Set. return Set(universe) if any(predicate.args() != vars for predicate in callable_symbolic_predicates): # TODO: Implement safe renaming of the arguments of a callable symbolic expressions raise NotImplementedError( 'all callable symbolic expressions must use the same arguments' ) if names is None: names = ("x", ) return super().__classcall__(cls, universe, *predicates, names=names, category=category)
[----+--------------+--------------+----] [ 1| 0 0 0| 5/2 7/2 -1/2|25/2] [----+--------------+--------------+----] [ 0| 1 0 0| 1/2 3/2 1/2| 5/2] [ 0| 0 1 0| -2 -5 0| 1] [ 0| 0 0 1|-3/2 -1/2 1/2| 1/2] """ m = copy(m) m.swap_columns(i, j) m[1:] = m[1:].echelon_form() return m ############################################################################## # Un certain nombre de programmes linéaires, du Chvatal et d'ailleurs x1, x2, x3, x4, x5, x6 = SR.var("x1,x2,x3,x4,x5,x6") e, e1, e2, e3, e4 = SR.var("e,e1,e2,e3,e4") x0 = SR.var("x0") ## Chvatal7a #### Chvatal7a = [[x1 <= 3, x2 <= 7], 3 + x1 + x2, NonNegative] ## Chvatal7b #### Chvatal7b = [[x1 + x2 <= 2, -2 * x1 - 2 * x2 <= -10], 3 * x1 - x2, NonNegative] ## Chvatal7c #### Chvatal7c = [[-2 * x1 + x2 <= -1, -x1 - 2 * x2 <= -2], x1 - x2, NonNegative] ## extra #### extra = [[x1 + x2 <= 1], x1 + x2, NonNegative] ## Chvatal13 #### Chvatal13 = [ [2 * x1 + 3 * x2 + x3 <= 5, 4 * x1 + x2 + 2 * x3 <= 11, 3 * x1 + 4 * x2 + 2 * x3 <= 8], 5 * x1 + 4 * x2 + 3 * x3,
def convolution(cls, self, parameters, variable, other): """ Return the convolution function, `f*g(t)=\int_{-\infty}^\infty f(u)g(t-u)du`, for compactly supported `f,g`. EXAMPLES:: sage: x = PolynomialRing(QQ,'x').gen() sage: f = piecewise([[[0,1],1]]) ## example 0 sage: g = f.convolution(f); g piecewise(x|-->x on (0, 1], x|-->-x + 2 on (1, 2]; x) sage: h = f.convolution(g); h piecewise(x|-->1/2*x^2 on (0, 1], x|-->-x^2 + 3*x - 3/2 on (1, 2], x|-->1/2*x^2 - 3*x + 9/2 on (2, 3]; x) sage: f = piecewise([[(0,1),1],[(1,2),2],[(2,3),1]]) ## example 1 sage: g = f.convolution(f) sage: h = f.convolution(g); h piecewise(x|-->1/2*x^2 on (0, 1], x|-->2*x^2 - 3*x + 3/2 on (1, 3], x|-->-2*x^2 + 21*x - 69/2 on (3, 4], x|-->-5*x^2 + 45*x - 165/2 on (4, 5], x|-->-2*x^2 + 15*x - 15/2 on (5, 6], x|-->2*x^2 - 33*x + 273/2 on (6, 8], x|-->1/2*x^2 - 9*x + 81/2 on (8, 9]; x) sage: f = piecewise([[(-1,1),1]]) ## example 2 sage: g = piecewise([[(0,3),x]]) sage: f.convolution(g) piecewise(x|-->1/2*x^2 + x + 1/2 on (-1, 1], x|-->2*x on (1, 2], x|-->-1/2*x^2 + x + 4 on (2, 4]; x) sage: g = piecewise([[(0,3),1],[(3,4),2]]) sage: f.convolution(g) piecewise(x|-->x + 1 on (-1, 1], x|-->2 on (1, 2], x|-->x on (2, 3], x|-->-x + 6 on (3, 4], x|-->-2*x + 10 on (4, 5]; x) Check that the bugs raised in :trac:`12123` are fixed:: sage: f = piecewise([[(-2, 2), 2]]) sage: g = piecewise([[(0, 2), 3/4]]) sage: f.convolution(g) piecewise(x|-->3/2*x + 3 on (-2, 0], x|-->3 on (0, 2], x|-->-3/2*x + 6 on (2, 4]; x) sage: f = piecewise([[(-1, 1), 1]]) sage: g = piecewise([[(0, 1), x], [(1, 2), -x + 2]]) sage: f.convolution(g) piecewise(x|-->1/2*x^2 + x + 1/2 on (-1, 0], x|-->-1/2*x^2 + x + 1/2 on (0, 2], x|-->1/2*x^2 - 3*x + 9/2 on (2, 3]; x) """ from sage.symbolic.integration.integral import definite_integral f = self g = other if len(f.end_points())*len(g.end_points()) == 0: raise ValueError('one of the piecewise functions is nowhere defined') M = min(min(f.end_points()),min(g.end_points())) N = max(max(f.end_points()),max(g.end_points())) tt = SR.var('tt') uu = SR.var('uu') conv = 0 fd,f0 = parameters[0] gd,g0 = next(other.items()) if len(f)==1 and len(g)==1: f = f.unextend_zero() g = g.unextend_zero() a1 = fd[0].lower() a2 = fd[0].upper() b1 = gd[0].lower() b2 = gd[0].upper() i1 = f0.subs({variable: uu}) i2 = g0.subs({variable: tt-uu}) fg1 = definite_integral(i1*i2, uu, a1, tt-b1).subs(tt = variable) fg2 = definite_integral(i1*i2, uu, tt-b2, tt-b1).subs(tt = variable) fg3 = definite_integral(i1*i2, uu, tt-b2, a2).subs(tt = variable) fg4 = definite_integral(i1*i2, uu, a1, a2).subs(tt = variable) if a1-b1<a2-b2: if a2+b1!=a1+b2: h = piecewise([[(a1+b1,a1+b2),fg1],[(a1+b2,a2+b1),fg2],[(a2+b1,a2+b2),fg3]]) else: h = piecewise([[(a1+b1,a1+b2),fg1],[(a1+b2,a2+b2),fg3]]) else: if a1+b2!=a2+b1: h = piecewise([[(a1+b1,a2+b1),fg1],[(a2+b1,a1+b2),fg4],[(a1+b2,a2+b2),fg3]]) else: h = piecewise([[(a1+b1,a2+b1),fg1],[(a2+b1,a2+b2),fg3]]) return (piecewise([[(minus_infinity,infinity),0]]).piecewise_add(h)).unextend_zero() if len(f)>1 or len(g)>1: z = piecewise([[(0,0),0]]) for fpiece in f.pieces(): for gpiece in g.pieces(): h = gpiece.convolution(fpiece) z = z.piecewise_add(h) return z.unextend_zero()
def inverse(self, chart1=None, chart2=None): r""" Return the inverse diffeomorphism. INPUT: - ``chart1`` -- (default: None) chart in which the computation of the inverse is performed if necessary; if none is provided, the default chart of the start domain will be used - ``chart2`` -- (default: None) chart in which the computation of the inverse is performed if necessary; if none is provided, the default chart of the arrival domain will be used OUTPUT: - the inverse diffeomorphism EXAMPLES: The inverse of a rotation in the Euclidean plane:: sage: m = Manifold(2, 'R^2', r'\RR^2') sage: c_cart.<x,y> = m.chart('x y') sage: # A pi/3 rotation around the origin: sage: rot = Diffeomorphism(m, m, ((x - sqrt(3)*y)/2, (sqrt(3)*x + y)/2), name='R') sage: rot.inverse() diffeomorphism 'R^(-1)' on the 2-dimensional manifold 'R^2' sage: rot.inverse().view() R^(-1): R^2 --> R^2, (x, y) |--> (1/2*sqrt(3)*y + 1/2*x, -1/2*sqrt(3)*x + 1/2*y) Checking that applying successively the diffeomorphism and its inverse results in the identity:: sage: (a, b) = var('a b') sage: p = Point(m, (a,b)) # a generic point on M sage: q = rot(p) sage: p1 = rot.inverse()(q) sage: p1 == p True """ from sage.symbolic.ring import SR from sage.symbolic.relation import solve from utilities import simplify_chain if self._inverse is not None: return self._inverse if chart1 is None: chart1 = self.domain1.def_chart if chart2 is None: chart2 = self.domain2.def_chart coord_map = self.coord_expression[(chart1, chart2)] n1 = len(chart1.xx) n2 = len(chart2.xx) # New symbolic variables (different from chart2.xx to allow for a # correct solution even when chart2 = chart1): x2 = [ SR.var('xxxx' + str(i)) for i in range(n2) ] equations = [ x2[i] == coord_map.functions[i].express for i in range(n2) ] solutions = solve(equations, chart1.xx, solution_dict=True) if len(solutions) == 0: raise ValueError("No solution found") if len(solutions) > 1: raise ValueError("Non-unique solution found") #!# This should be the Python 2.7 form: # substitutions = {x2[i]: chart2.xx[i] for i in range(n2)} # # Here we use a form compatible with Python 2.6: substitutions = dict([(x2[i], chart2.xx[i]) for i in range(n2)]) inv_functions = [solutions[0][chart1.xx[i]].subs(substitutions) for i in range(n1)] for i in range(n1): x = inv_functions[i] try: inv_functions[i] = simplify_chain(x) except AttributeError: pass if self.name is None: name = None else: name = self.name + '^(-1)' if self.latex_name is None: latex_name = None else: latex_name = self.latex_name + r'^{-1}' self._inverse = Diffeomorphism(self.domain2, self.domain1, inv_functions, chart2, chart1, name=name, latex_name=latex_name) return self._inverse
def composition(self, ex, operator): if operator in self._functions: return SR.var(str(operator)) else: return super().composition(ex, operator)
def mma_free_integrator(expression, v, a=None, b=None): """ Integration using Mathematica's online integrator EXAMPLES:: sage: from sage.symbolic.integration.external import mma_free_integrator sage: mma_free_integrator(sin(x), x) # optional - internet -cos(x) TESTS: Check that :trac:`18212` is resolved:: sage: var('y') # optional - internet y sage: integral(sin(y)^2, y, algorithm='mathematica_free') # optional - internet -1/2*cos(y)*sin(y) + 1/2*y sage: mma_free_integrator(exp(-x^2)*log(x), x) # optional - internet 1/2*sqrt(pi)*erf(x)*log(x) - x*hypergeometric((1/2, 1/2), (3/2, 3/2), -x^2) """ import re # import compatible with py2 and py3 from six.moves.urllib.request import urlopen from six.moves.urllib.parse import urlencode # We need to integrate against x vars = [str(x) for x in expression.variables()] if any(len(x)>1 for x in vars): raise NotImplementedError("Mathematica online integrator can only handle single letter variables.") x = SR.var('x') if repr(v) != 'x': for i in range(ord('a'), ord('z')+1): if chr(i) not in vars: shadow_x = SR.var(chr(i)) break expression = expression.subs({x:shadow_x}).subs({v: x}) params = urlencode({'expr': expression._mathematica_init_(), 'random': 'false'}) page = urlopen("http://integrals.wolfram.com/home.jsp", params).read() page = page[page.index('"inputForm"'):page.index('"outputForm"')] page = re.sub("\s", "", page) mexpr = re.match(r".*Integrate.*==</em><br/>(.*)</p>", page).groups()[0] try: from sage.libs.pynac.pynac import symbol_table from sage.interfaces.mathematica import _un_camel as un_camel from sage.symbolic.constants import constants_name_table as constants from sage.calculus.calculus import symbolic_expression_from_string from sage.calculus.calculus import _find_func as find_func expr = mexpr.replace('\n',' ').replace('\r', '') expr = expr.replace('[', '(').replace(']', ')') expr = expr.replace('{', '[').replace('}', ']') lsymbols = symbol_table['mathematica'].copy() autotrans = [str.lower, # Try it in lower case un_camel, # Convert `CamelCase` to `camel_case` lambda x: x # Try the original name ] # Find the MMA funcs/vars/constants - they start with a letter. # Exclude exponents (e.g. 'e8' from 4.e8) p = re.compile('(?<!\.)[a-zA-Z]\w*') for m in p.finditer(expr): # If the function, variable or constant is already in the # translation dictionary, then just move on. if m.group() in lsymbols: pass # Now try to translate all other functions -- try each strategy # in `autotrans` and check if the function exists in Sage elif m.end() < len(expr) and expr[m.end()] == '(': for t in autotrans: f = find_func(t(m.group()), create_when_missing = False) if f is not None: lsymbols[m.group()] = f break else: raise NotImplementedError("Don't know a Sage equivalent for Mathematica function '%s'." % m.group()) # Check if Sage has an equivalent constant else: for t in autotrans: if t(m.group()) in constants: lsymbols[m.group()] = constants[t(m.group())] break ans = symbolic_expression_from_string(expr, lsymbols, accept_sequence=True) if repr(v) != 'x': ans = ans.subs({x:v}).subs({shadow_x:x}) return ans except TypeError: raise ValueError("Unable to parse: %s" % mexpr)
def convolution(cls, self, parameters, variable, other): """ Return the convolution function, `f*g(t)=\int_{-\infty}^\infty f(u)g(t-u)du`, for compactly supported `f,g`. EXAMPLES:: sage: x = PolynomialRing(QQ,'x').gen() sage: f = piecewise([[[0,1],1]]) ## example 0 sage: g = f.convolution(f); g piecewise(x|-->x on (0, 1], x|-->-x + 2 on (1, 2]; x) sage: h = f.convolution(g); h piecewise(x|-->1/2*x^2 on (0, 1], x|-->-x^2 + 3*x - 3/2 on (1, 2], x|-->1/2*x^2 - 3*x + 9/2 on (2, 3]; x) sage: f = piecewise([[(0,1),1],[(1,2),2],[(2,3),1]]) ## example 1 sage: g = f.convolution(f) sage: h = f.convolution(g); h piecewise(x|-->1/2*x^2 on (0, 1], x|-->2*x^2 - 3*x + 3/2 on (1, 3], x|-->-2*x^2 + 21*x - 69/2 on (3, 4], x|-->-5*x^2 + 45*x - 165/2 on (4, 5], x|-->-2*x^2 + 15*x - 15/2 on (5, 6], x|-->2*x^2 - 33*x + 273/2 on (6, 8], x|-->1/2*x^2 - 9*x + 81/2 on (8, 9]; x) sage: f = piecewise([[(-1,1),1]]) ## example 2 sage: g = piecewise([[(0,3),x]]) sage: f.convolution(g) piecewise(x|-->1/2*x^2 + x + 1/2 on (-1, 1], x|-->2*x on (1, 2], x|-->-1/2*x^2 + x + 4 on (2, 4]; x) sage: g = piecewise([[(0,3),1],[(3,4),2]]) sage: f.convolution(g) piecewise(x|-->x + 1 on (-1, 1], x|-->2 on (1, 2], x|-->x on (2, 3], x|-->-x + 6 on (3, 4], x|-->-2*x + 10 on (4, 5]; x) Check that the bugs raised in :trac:`12123` are fixed:: sage: f = piecewise([[(-2, 2), 2]]) sage: g = piecewise([[(0, 2), 3/4]]) sage: f.convolution(g) piecewise(x|-->3/2*x + 3 on (-2, 0], x|-->3 on (0, 2], x|-->-3/2*x + 6 on (2, 4]; x) sage: f = piecewise([[(-1, 1), 1]]) sage: g = piecewise([[(0, 1), x], [(1, 2), -x + 2]]) sage: f.convolution(g) piecewise(x|-->1/2*x^2 + x + 1/2 on (-1, 0], x|-->-1/2*x^2 + x + 1/2 on (0, 2], x|-->1/2*x^2 - 3*x + 9/2 on (2, 3]; x) """ from sage.symbolic.integration.integral import definite_integral f = self g = other if len(f.end_points()) * len(g.end_points()) == 0: raise ValueError( 'one of the piecewise functions is nowhere defined') M = min(min(f.end_points()), min(g.end_points())) N = max(max(f.end_points()), max(g.end_points())) tt = SR.var('tt') uu = SR.var('uu') conv = 0 fd, f0 = parameters[0] gd, g0 = next(other.items()) if len(f) == 1 and len(g) == 1: f = f.unextend_zero() g = g.unextend_zero() a1 = fd[0].lower() a2 = fd[0].upper() b1 = gd[0].lower() b2 = gd[0].upper() i1 = f0.subs({variable: uu}) i2 = g0.subs({variable: tt - uu}) fg1 = definite_integral(i1 * i2, uu, a1, tt - b1).subs(tt=variable) fg2 = definite_integral(i1 * i2, uu, tt - b2, tt - b1).subs(tt=variable) fg3 = definite_integral(i1 * i2, uu, tt - b2, a2).subs(tt=variable) fg4 = definite_integral(i1 * i2, uu, a1, a2).subs(tt=variable) if a1 - b1 < a2 - b2: if a2 + b1 != a1 + b2: h = piecewise([[(a1 + b1, a1 + b2), fg1], [(a1 + b2, a2 + b1), fg2], [(a2 + b1, a2 + b2), fg3]]) else: h = piecewise([[(a1 + b1, a1 + b2), fg1], [(a1 + b2, a2 + b2), fg3]]) else: if a1 + b2 != a2 + b1: h = piecewise([[(a1 + b1, a2 + b1), fg1], [(a2 + b1, a1 + b2), fg4], [(a1 + b2, a2 + b2), fg3]]) else: h = piecewise([[(a1 + b1, a2 + b1), fg1], [(a2 + b1, a2 + b2), fg3]]) return (piecewise([[(minus_infinity, infinity), 0]]).piecewise_add(h)).unextend_zero() if len(f) > 1 or len(g) > 1: z = piecewise([[(0, 0), 0]]) for fpiece in f.pieces(): for gpiece in g.pieces(): h = gpiece.convolution(fpiece) z = z.piecewise_add(h) return z.unextend_zero()
def bezier3d(path, **options): """ Draw a 3-dimensional bezier path. Input is similar to bezier_path, but each point in the path and each control point is required to have 3 coordinates. INPUT: - ``path`` -- a list of curves, which each is a list of points. See further detail below. - ``thickness`` -- (default: 2) - ``color`` -- a string (``"red"``, ``"green"`` etc) or a tuple (r, g, b) with r, g, b numbers between 0 and 1 - ``opacity`` -- (default: 1) if less than 1 then is transparent - ``aspect_ratio`` -- (default:[1,1,1]) The path is a list of curves, and each curve is a list of points. Each point is a tuple (x,y,z). The first curve contains the endpoints as the first and last point in the list. All other curves assume a starting point given by the last entry in the preceding list, and take the last point in the list as their opposite endpoint. A curve can have 0, 1 or 2 control points listed between the endpoints. In the input example for path below, the first and second curves have 2 control points, the third has one, and the fourth has no control points:: path = [[p1, c1, c2, p2], [c3, c4, p3], [c5, p4], [p5], ...] In the case of no control points, a straight line will be drawn between the two endpoints. If one control point is supplied, then the curve at each of the endpoints will be tangent to the line from that endpoint to the control point. Similarly, in the case of two control points, at each endpoint the curve will be tangent to the line connecting that endpoint with the control point immediately after or immediately preceding it in the list. So in our example above, the curve between p1 and p2 is tangent to the line through p1 and c1 at p1, and tangent to the line through p2 and c2 at p2. Similarly, the curve between p2 and p3 is tangent to line(p2,c3) at p2 and tangent to line(p3,c4) at p3. Curve(p3,p4) is tangent to line(p3,c5) at p3 and tangent to line(p4,c5) at p4. Curve(p4,p5) is a straight line. EXAMPLES:: sage: path = [[(0,0,0),(.5,.1,.2),(.75,3,-1),(1,1,0)],[(.5,1,.2),(1,.5,0)],[(.7,.2,.5)]] sage: b = bezier3d(path, color='green') sage: b Graphics3d Object To construct a simple curve, create a list containing a single list:: sage: path = [[(0,0,0),(1,0,0),(0,1,0),(0,1,1)]] sage: curve = bezier3d(path, thickness=5, color='blue') sage: curve Graphics3d Object """ from . import parametric_plot3d as P3D from sage.modules.free_module_element import vector from sage.symbolic.ring import SR p0 = vector(path[0][-1]) t = SR.var('t') if len(path[0]) > 2: B = (1 - t)**3 * vector(path[0][0]) + 3 * t * (1 - t)**2 * vector( path[0][1]) + 3 * t**2 * (1 - t) * vector(path[0][-2]) + t**3 * p0 G = P3D.parametric_plot3d(list(B), (0, 1), color=options['color'], aspect_ratio=options['aspect_ratio'], thickness=options['thickness'], opacity=options['opacity']) else: G = line3d([path[0][0], p0], color=options['color'], thickness=options['thickness'], opacity=options['opacity']) for curve in path[1:]: if len(curve) > 1: p1 = vector(curve[0]) p2 = vector(curve[-2]) p3 = vector(curve[-1]) B = (1 - t)**3 * p0 + 3 * t * (1 - t)**2 * p1 + 3 * t**2 * ( 1 - t) * p2 + t**3 * p3 G += P3D.parametric_plot3d(list(B), (0, 1), color=options['color'], aspect_ratio=options['aspect_ratio'], thickness=options['thickness'], opacity=options['opacity']) else: G += line3d([p0, curve[0]], color=options['color'], thickness=options['thickness'], opacity=options['opacity']) p0 = curve[-1] return G
def __call__(self, function_pieces, **kwds): r""" Piecewise functions INPUT: - ``function_pieces`` -- a list of pairs consisting of a domain and a symbolic function. - ``var=x`` -- a symbolic variable or ``None`` (default). The real variable in which the function is piecewise in. OUTPUT: A piecewise-defined function. A ``ValueError`` will be raised if the domains of the pieces are not pairwise disjoint. EXAMPLES:: sage: my_abs = piecewise([((-1, 0), -x), ([0, 1], x)], var=x); my_abs piecewise(x|-->-x on (-1, 0), x|-->x on [0, 1]; x) sage: [ my_abs(i/5) for i in range(-4, 5)] [4/5, 3/5, 2/5, 1/5, 0, 1/5, 2/5, 3/5, 4/5] TESTS:: sage: piecewise([([-1, 0], -x), ([0, 1], x)], var=x) Traceback (most recent call last): ... ValueError: domains must be pairwise disjoint sage: step = piecewise([((-1, 0), -1), ([0, 0], 0), ((0, 1), 1)], var=x); step piecewise(x|-->-1 on (-1, 0), x|-->0 on {0}, x|-->1 on (0, 1); x) sage: step(-1/2), step(0), step(1/2) (-1, 0, 1) """ from types import FunctionType var = kwds.pop('var', None) parameters = [] domain_list = [] for piece in function_pieces: domain, function = piece if not isinstance(domain, RealSet): domain = RealSet(domain) if domain.is_empty(): continue if isinstance(function, FunctionType): if var is None: var = SR.var('x') if get_function_code(function).co_argcount == 0: function = function() else: function = function(var) function = SR(function) if var is None and len(function.variables()) > 0: var = function.variables()[0] parameters.append((domain, function)) domain_list.append(domain) if not RealSet.are_pairwise_disjoint(*domain_list): raise ValueError('domains must be pairwise disjoint') if var is None: var = self.default_variable() parameters = SR._force_pyobject(tuple(parameters), recursive=False) return BuiltinFunction.__call__(self, parameters, var, **kwds)
def revolution_plot3d(curve,trange,phirange=None,parallel_axis='z',axis=(0,0),print_vector=False,show_curve=False,**kwds): """ Return a plot of a revolved curve. There are three ways to call this function: - ``revolution_plot3d(f,trange)`` where `f` is a function located in the `x z` plane. - ``revolution_plot3d((f_x,f_z),trange)`` where `(f_x,f_z)` is a parametric curve on the `x z` plane. - ``revolution_plot3d((f_x,f_y,f_z),trange)`` where `(f_x,f_y,f_z)` can be any parametric curve. INPUT: - ``curve`` - A curve to be revolved, specified as a function, a 2-tuple or a 3-tuple. - ``trange`` - A 3-tuple `(t,t_{\min},t_{\max})` where t is the independent variable of the curve. - ``phirange`` - A 2-tuple of the form `(\phi_{\min},\phi_{\max})`, (default `(0,\pi)`) that specifies the angle in which the curve is to be revolved. - ``parallel_axis`` - A string (Either 'x', 'y', or 'z') that specifies the coordinate axis parallel to the revolution axis. - ``axis`` - A 2-tuple that specifies the position of the revolution axis. If parallel is: - 'z' - then axis is the point in which the revolution axis intersects the `x y` plane. - 'x' - then axis is the point in which the revolution axis intersects the `y z` plane. - 'y' - then axis is the point in which the revolution axis intersects the `x z` plane. - ``print_vector`` - If True, the parametrization of the surface of revolution will be printed. - ``show_curve`` - If True, the curve will be displayed. EXAMPLES: Let's revolve a simple function around different axes:: sage: u = var('u') sage: f = u^2 sage: revolution_plot3d(f, (u,0,2), show_curve=True, opacity=0.7).show(aspect_ratio=(1,1,1)) .. PLOT:: u = var('u') f = u**2 P = revolution_plot3d(f, (u,0,2), show_curve=True, opacity=0.7).plot() sphinx_plot(P) If we move slightly the axis, we get a goblet-like surface:: sage: revolution_plot3d(f, (u,0,2), axis=(1,0.2), show_curve=True, opacity=0.5).show(aspect_ratio=(1,1,1)) .. PLOT:: u = var('u') f = u**2 P = revolution_plot3d(f, (u,0,2), axis=(1,0.2), show_curve=True, opacity=0.5).plot() sphinx_plot(P) A common problem in calculus books, find the volume within the following revolution solid:: sage: line = u sage: parabola = u^2 sage: sur1 = revolution_plot3d(line, (u,0,1), opacity=0.5, rgbcolor=(1,0.5,0), show_curve=True, parallel_axis='x') sage: sur2 = revolution_plot3d(parabola, (u,0,1), opacity=0.5, rgbcolor=(0,1,0), show_curve=True, parallel_axis='x') sage: (sur1+sur2).show() .. PLOT:: u = var('u') line = u parabola = u**2 sur1 = revolution_plot3d(line, (u,0,1), opacity=0.5, rgbcolor=(1,0.5,0), show_curve=True, parallel_axis='x') sur2 = revolution_plot3d(parabola, (u,0,1), opacity=0.5, rgbcolor=(0,1,0), show_curve=True, parallel_axis='x') P = sur1 + sur2 sphinx_plot(P) Now let's revolve a parametrically defined circle. We can play with the topology of the surface by changing the axis, an axis in `(0,0)` (as the previous one) will produce a sphere-like surface:: sage: u = var('u') sage: circle = (cos(u), sin(u)) sage: revolution_plot3d(circle, (u,0,2*pi), axis=(0,0), show_curve=True, opacity=0.5).show(aspect_ratio=(1,1,1)) .. PLOT:: u = var('u') circle = (cos(u), sin(u)) P = revolution_plot3d(circle, (u,0,2*pi), axis=(0,0), show_curve=True, opacity=0.5) sphinx_plot(P) An axis on `(0,y)` will produce a cylinder-like surface:: sage: revolution_plot3d(circle, (u,0,2*pi), axis=(0,2), show_curve=True, opacity=0.5).show(aspect_ratio=(1,1,1)) .. PLOT:: u = var('u') circle = (cos(u), sin(u)) P = revolution_plot3d(circle, (u,0,2*pi), axis=(0,2), show_curve=True, opacity=0.5) sphinx_plot(P) And any other axis will produce a torus-like surface:: sage: revolution_plot3d(circle, (u,0,2*pi), axis=(2,0), show_curve=True, opacity=0.5).show(aspect_ratio=(1,1,1)) .. PLOT:: u = var('u') circle = (cos(u), sin(u)) P = revolution_plot3d(circle, (u,0,2*pi), axis=(2,0), show_curve=True, opacity=0.5) sphinx_plot(P) Now, we can get another goblet-like surface by revolving a curve in 3d:: sage: u = var('u') sage: curve = (u, cos(4*u), u^2) sage: P = revolution_plot3d(curve, (u,0,2), show_curve=True, parallel_axis='z',axis=(1,.2), opacity=0.5) sage: P.show(aspect_ratio=(1,1,1)) .. PLOT:: u = var('u') curve = (u, cos(4*u), u**2) P = revolution_plot3d(curve, (u,0,2), show_curve=True, parallel_axis='z', axis=(1,.2), opacity=0.5) sphinx_plot(P) A curvy curve with only a quarter turn:: sage: u = var('u') sage: curve = (sin(3*u), .8*cos(4*u), cos(u)) sage: revolution_plot3d(curve, (u,0,pi), (0,pi/2), show_curve=True, parallel_axis='z', opacity=0.5).show(aspect_ratio=(1,1,1),frame=False) .. PLOT:: u = var('u') curve = (sin(3*u), .8*cos(4*u), cos(u)) P = revolution_plot3d(curve, (u,0,pi), (0,pi/2), show_curve=True, parallel_axis='z', opacity=0.5) sphinx_plot(P) One can also color the surface using a coloring function of two parameters and a colormap as follows:: sage: u, phi = var('u,phi') sage: def cf(u,phi): return sin(phi+u) ^ 2 sage: curve = (1+u^2/4, 0, u) sage: revolution_plot3d(curve, (u,-2,2), (0,2*pi), parallel_axis='z', color=(cf, colormaps.PiYG)).show(aspect_ratio=(1,1,1)) .. PLOT:: u, phi = var('u,phi') def cf(u, phi): return sin(phi+u) ** 2 curve = (1+u**2/4, 0, u) P = revolution_plot3d(curve, (u,-2,2), (0,2*pi), parallel_axis='z', color=(cf, colormaps.PiYG)) sphinx_plot(P) The first parameter of the coloring function will be identified with the parameter of the curve, and the second with the angle parameter. .. WARNING:: This kind of coloring using a colormap can be visualized using Jmol, Tachyon (option ``viewer='tachyon'``) and Canvas3D (option ``viewer='canvas3d'`` in the notebook). """ from sage.symbolic.ring import SR from sage.symbolic.constants import pi from sage.functions.other import sqrt from sage.functions.trig import sin from sage.functions.trig import cos from sage.functions.trig import atan2 if parallel_axis not in ['x', 'y', 'z']: raise ValueError("parallel_axis must be either 'x', 'y', or 'z'.") vart = trange[0] if str(vart) == 'phi': phi = SR.var('fi') else: phi = SR.var('phi') if phirange is None: # this if-else provides a phirange phirange = (phi, 0, 2 * pi) elif len(phirange) == 3: phi = phirange[0] pass else: phirange = (phi, phirange[0], phirange[1]) if isinstance(curve, (tuple, list)): #this if-else provides a vector v to be plotted #if curve is a tuple or a list of length 2, it is interpreted as a parametric curve #in the x-z plane. #if it is of length 3 it is interpreted as a parametric curve in 3d space if len(curve) == 2: x = curve[0] y = 0 z = curve[1] elif len(curve) == 3: x = curve[0] y = curve[1] z = curve[2] else: x = vart y = 0 z = curve phase = 0 if parallel_axis == 'z': x0 = axis[0] y0 = axis[1] # (0,0) must be handled separately for the phase value if x0 != 0 or y0 != 0: phase = atan2(y - y0, x - x0) R = sqrt((x-x0)**2 + (y-y0)**2) v = (R*cos(phi+phase)+x0, R*sin(phi+phase)+y0, z) elif parallel_axis == 'x': y0 = axis[0] z0 = axis[1] # (0,0) must be handled separately for the phase value if z0 != 0 or y0 != 0: phase = atan2(z - z0, y - y0) R = sqrt((y-y0)**2 + (z-z0)**2) v = (x, R*cos(phi+phase)+y0, R*sin(phi+phase)+z0) elif parallel_axis == 'y': x0 = axis[0] z0 = axis[1] # (0,0) must be handled separately for the phase value if z0 != 0 or x0 != 0: phase = atan2(z - z0, x - x0) R = sqrt((x-x0)**2 + (z-z0)**2) v = (R*cos(phi+phase)+x0, y, R*sin(phi+phase)+z0) if print_vector: print(v) if show_curve: curveplot = parametric_plot3d((x, y, z), trange, thickness=2, rgbcolor=(1, 0, 0)) return parametric_plot3d(v, trange, phirange, **kwds) + curveplot return parametric_plot3d(v, trange, phirange, **kwds)
def fourier_series_cosine_coefficient(self, parameters, variable, n, L=None): r""" Return the `n`-th cosine coefficient of the Fourier series of the periodic function `f` extending the piecewise-defined function ``self``. Given an integer `n\geq 0`, the `n`-th cosine coefficient of the Fourier series of `f` is defined by .. MATH:: a_n = \frac{1}{L}\int_{-L}^L f(x)\cos\left(\frac{n\pi x}{L}\right) dx, where `L` is the half-period of `f`. For `n\geq 1`, `a_n` is the coefficient of `\cos(n\pi x/L)` in the Fourier series of `f`, while `a_0` is twice the coefficient of the constant term `\cos(0 x)`, i.e. twice the mean value of `f` over one period (cf. :meth:`fourier_series_partial_sum`). INPUT: - ``n`` -- a non-negative integer - ``L`` -- (default: ``None``) the half-period of `f`; if none is provided, `L` is assumed to be the half-width of the domain of ``self`` OUTPUT: - the Fourier coefficient `a_n`, as defined above EXAMPLES: A triangle wave function of period 2:: sage: f = piecewise([((0,1), x), ((1,2), 2-x)]) sage: f.fourier_series_cosine_coefficient(0) 1 sage: f.fourier_series_cosine_coefficient(3) -4/9/pi^2 If the domain of the piecewise-defined function encompasses more than one period, the half-period must be passed as the second argument; for instance:: sage: f2 = piecewise([((0,1), x), ((1,2), 2-x), ....: ((2,3), x-2), ((3,4), 2-(x-2))]) sage: bool(f2.restriction((0,2)) == f) # f2 extends f on (0,4) True sage: f2.fourier_series_cosine_coefficient(3, 1) # half-period = 1 -4/9/pi^2 The default half-period is 2 and one has:: sage: f2.fourier_series_cosine_coefficient(3) # half-period = 2 0 The Fourier coefficient `-4/(9\pi^2)` obtained above is actually recovered for `n=6`:: sage: f2.fourier_series_cosine_coefficient(6) -4/9/pi^2 Other examples:: sage: f(x) = x^2 sage: f = piecewise([[(-1,1),f]]) sage: f.fourier_series_cosine_coefficient(2) pi^(-2) sage: f1(x) = -1 sage: f2(x) = 2 sage: f = piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]]) sage: f.fourier_series_cosine_coefficient(5,pi) -3/5/pi """ from sage.all import cos, pi L0 = (self.domain().sup() - self.domain().inf()) / 2 if not L: L = L0 else: m = L0 / L if not (m.is_integer() and m > 0): raise ValueError("the width of the domain of " + "{} is not a multiple ".format(self) + "of the given period") x = SR.var('x') result = 0 for domain, f in parameters: for interval in domain: a = interval.lower() b = interval.upper() result += (f * cos(pi * x * n / L)).integrate(x, a, b) return SR(result / L0).simplify_trig()
[----+--------------+--------------+----] [ 1| 0 0 0| 5/2 7/2 -1/2|25/2] [----+--------------+--------------+----] [ 0| 1 0 0| 1/2 3/2 1/2| 5/2] [ 0| 0 1 0| -2 -5 0| 1] [ 0| 0 0 1|-3/2 -1/2 1/2| 1/2] """ m = copy(m) m.swap_columns(i, j) m[1:] = m[1:].echelon_form() return m ############################################################################## # Un certain nombre de programmes linéaires, du Chvatal et d'ailleurs x1, x2, x3, x4, x5, x6 = SR.var('x1,x2,x3,x4,x5,x6') e, e1, e2, e3, e4 = SR.var('e,e1,e2,e3,e4') x0 = SR.var('x0') ## Chvatal7a #### Chvatal7a = [[x1 <= 3, x2 <= 7], 3 + x1 + x2, NonNegative] ## Chvatal7b #### Chvatal7b = [[x1 + x2 <= 2, -2 * x1 - 2 * x2 <= -10], 3 * x1 - x2, NonNegative] ## Chvatal7c #### Chvatal7c = [[-2 * x1 + x2 <= -1, -x1 - 2 * x2 <= -2], x1 - x2, NonNegative] ## extra #### extra = [[x1 + x2 <= 1], x1 + x2, NonNegative] ## Chvatal13 #### Chvatal13 = [[ 2 * x1 + 3 * x2 + x3 <= 5, 4 * x1 + x2 + 2 * x3 <= 11, 3 * x1 + 4 * x2 + 2 * x3 <= 8
def _compute_init_vector(self, point, pt0, pr0, pth0, pph0, r_increase, th_increase, verbose): r""" Computes the initial 4-momentum vector `p` from the constants of motion """ BLchart = self._spacetime.boyer_lindquist_coordinates() basis = BLchart.frame().at(point) r, th = BLchart(point)[1:3] a, m = self._a, self._m r2 = r**2 a2 = a**2 rho2 = r2 + (a * cos(th))**2 Delta = r2 - 2 * m * r + a2 if pt0 is None: if (self._mu is not None and pr0 is not None and pth0 is not None and pph0 is not None): xxx = SR.var('xxx') v = self._spacetime.tangent_space(point)( (xxx, pr0, pth0, pph0), basis=basis) muv2 = -self._spacetime.metric().at(point)(v, v) muv2 = muv2.substitute(self._numerical_substitutions()) solutions = solve(muv2 == self._mu**2, xxx, solution_dict=True) if verbose: print("Solutions for p^t:") pretty_print(solutions) for sol in solutions: if sol[xxx] > 0: pt0 = sol[xxx] break else: # pt0 <= 0 might occur in the ergoregion pt0 = solutions[0][xxx] try: pt0 = RR(pt0) except TypeError: # pt0 contains some symbolic expression pass else: if self._E is None: raise ValueError("the constant E must be provided") if self._L is None: raise ValueError("the constant L must be provided") E, L = self._E, self._L pt0 = ((r2 + a2) / Delta * ((r2 + a2) * E - a * L) + a * (L - a * E * sin(th)**2)) / rho2 if pph0 is None: if self._E is None: raise ValueError("the constant E must be provided") if self._L is None: raise ValueError("the constant L must be provided") E, L = self._E, self._L pph0 = (L / sin(th)**2 - a * E + a / Delta * ((r2 + a2) * E - a * L)) / rho2 if pr0 is None: if self._E is None: raise ValueError("the constant E must be provided") if self._L is None: raise ValueError("the constant L must be provided") if self._mu is None: raise ValueError("the constant mu must be provided") if self._Q is None: raise ValueError("the constant Q must be provided") E, L, Q = self._E, self._L, self._Q mu2 = self._mu**2 E2_mu2 = E**2 - mu2 pr0 = sqrt((E2_mu2) * r**4 + 2 * m * mu2 * r**3 + (a2 * E2_mu2 - L**2 - Q) * r**2 + 2 * m * (Q + (L - a * E)**2) * r - a2 * Q) / rho2 if not r_increase: pr0 = -pr0 if pth0 is None: if self._E is None: raise ValueError("the constant E must be provided") if self._L is None: raise ValueError("the constant L must be provided") if self._mu is None: raise ValueError("the constant mu must be provided") if self._Q is None: raise ValueError("the constant Q must be provided") E2 = self._E**2 L2 = self._L**2 mu2 = self._mu**2 Q = self._Q pth0 = sqrt(Q + cos(th)**2 * (a2 * (E2 - mu2) - L2 / sin(th)**2)) / rho2 if not th_increase: pth0 = -pth0 return self._spacetime.tangent_space(point)((pt0, pr0, pth0, pph0), basis=basis, name='p')
def fourier_series_cosine_coefficient(self, parameters, variable, n, L=None): r""" Return the `n`-th cosine coefficient of the Fourier series of the periodic function `f` extending the piecewise-defined function ``self``. Given an integer `n\geq 0`, the `n`-th cosine coefficient of the Fourier series of `f` is defined by .. MATH:: a_n = \frac{1}{L}\int_{-L}^L f(x)\cos\left(\frac{n\pi x}{L}\right) dx, where `L` is the half-period of `f`. For `n\geq 1`, `a_n` is the coefficient of `\cos(n\pi x/L)` in the Fourier series of `f`, while `a_0` is twice the coefficient of the constant term `\cos(0 x)`, i.e. twice the mean value of `f` over one period (cf. :meth:`fourier_series_partial_sum`). INPUT: - ``n`` -- a non-negative integer - ``L`` -- (default: ``None``) the half-period of `f`; if none is provided, `L` is assumed to be the half-width of the domain of ``self`` OUTPUT: - the Fourier coefficient `a_n`, as defined above EXAMPLES: A triangle wave function of period 2:: sage: f = piecewise([((0,1), x), ((1,2), 2-x)]) sage: f.fourier_series_cosine_coefficient(0) 1 sage: f.fourier_series_cosine_coefficient(3) -4/9/pi^2 If the domain of the piecewise-defined function encompasses more than one period, the half-period must be passed as the second argument; for instance:: sage: f2 = piecewise([((0,1), x), ((1,2), 2-x), ....: ((2,3), x-2), ((3,4), 2-(x-2))]) sage: bool(f2.restriction((0,2)) == f) # f2 extends f on (0,4) True sage: f2.fourier_series_cosine_coefficient(3, 1) # half-period = 1 -4/9/pi^2 The default half-period is 2 and one has:: sage: f2.fourier_series_cosine_coefficient(3) # half-period = 2 0 The Fourier coefficient `-4/(9\pi^2)` obtained above is actually recovered for `n=6`:: sage: f2.fourier_series_cosine_coefficient(6) -4/9/pi^2 Other examples:: sage: f(x) = x^2 sage: f = piecewise([[(-1,1),f]]) sage: f.fourier_series_cosine_coefficient(2) pi^(-2) sage: f1(x) = -1 sage: f2(x) = 2 sage: f = piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]]) sage: f.fourier_series_cosine_coefficient(5,pi) -3/5/pi """ from sage.all import cos, pi L0 = (self.domain().sup() - self.domain().inf()) / 2 if not L: L = L0 else: m = L0 / L if not (m.is_integer() and m > 0): raise ValueError("the width of the domain of " + "{} is not a multiple ".format(self) + "of the given period") x = SR.var('x') result = 0 for domain, f in parameters: for interval in domain: a = interval.lower() b = interval.upper() result += (f*cos(pi*x*n/L)).integrate(x, a, b) return SR(result/L0).simplify_trig()
def fourier_series_sine_coefficient(self, parameters, variable, n, L=None): r""" Return the `n`-th sine coefficient of the Fourier series of the periodic function `f` extending the piecewise-defined function ``self``. Given an integer `n\geq 0`, the `n`-th sine coefficient of the Fourier series of `f` is defined by .. MATH:: b_n = \frac{1}{L}\int_{-L}^L f(x)\sin\left(\frac{n\pi x}{L}\right) dx, where `L` is the half-period of `f`. The number `b_n` is the coefficient of `\sin(n\pi x/L)` in the Fourier series of `f` (cf. :meth:`fourier_series_partial_sum`). INPUT: - ``n`` -- a non-negative integer - ``L`` -- (default: ``None``) the half-period of `f`; if none is provided, `L` is assumed to be the half-width of the domain of ``self`` OUTPUT: - the Fourier coefficient `b_n`, as defined above EXAMPLES: A square wave function of period 2:: sage: f = piecewise([((-1,0), -1), ((0,1), 1)]) sage: f.fourier_series_sine_coefficient(1) 4/pi sage: f.fourier_series_sine_coefficient(2) 0 sage: f.fourier_series_sine_coefficient(3) 4/3/pi If the domain of the piecewise-defined function encompasses more than one period, the half-period must be passed as the second argument; for instance:: sage: f2 = piecewise([((-1,0), -1), ((0,1), 1), ....: ((1,2), -1), ((2,3), 1)]) sage: bool(f2.restriction((-1,1)) == f) # f2 extends f on (-1,3) True sage: f2.fourier_series_sine_coefficient(1, 1) # half-period = 1 4/pi sage: f2.fourier_series_sine_coefficient(3, 1) # half-period = 1 4/3/pi The default half-period is 2 and one has:: sage: f2.fourier_series_sine_coefficient(1) # half-period = 2 0 sage: f2.fourier_series_sine_coefficient(3) # half-period = 2 0 The Fourier coefficients obtained from ``f`` are actually recovered for `n=2` and `n=6` respectively:: sage: f2.fourier_series_sine_coefficient(2) 4/pi sage: f2.fourier_series_sine_coefficient(6) 4/3/pi """ from sage.all import sin, pi L0 = (self.domain().sup() - self.domain().inf()) / 2 if not L: L = L0 else: m = L0 / L if not (m.is_integer() and m > 0): raise ValueError("the width of the domain of " + "{} is not a multiple ".format(self) + "of the given period") x = SR.var('x') result = 0 for domain, f in parameters: for interval in domain: a = interval.lower() b = interval.upper() result += (f * sin(pi * x * n / L)).integrate(x, a, b) return SR(result / L0).simplify_trig()
def fourier_series_sine_coefficient(self, parameters, variable, n, L=None): r""" Return the `n`-th sine coefficient of the Fourier series of the periodic function `f` extending the piecewise-defined function ``self``. Given an integer `n\geq 0`, the `n`-th sine coefficient of the Fourier series of `f` is defined by .. MATH:: b_n = \frac{1}{L}\int_{-L}^L f(x)\sin\left(\frac{n\pi x}{L}\right) dx, where `L` is the half-period of `f`. The number `b_n` is the coefficient of `\sin(n\pi x/L)` in the Fourier series of `f` (cf. :meth:`fourier_series_partial_sum`). INPUT: - ``n`` -- a non-negative integer - ``L`` -- (default: ``None``) the half-period of `f`; if none is provided, `L` is assumed to be the half-width of the domain of ``self`` OUTPUT: - the Fourier coefficient `b_n`, as defined above EXAMPLES: A square wave function of period 2:: sage: f = piecewise([((-1,0), -1), ((0,1), 1)]) sage: f.fourier_series_sine_coefficient(1) 4/pi sage: f.fourier_series_sine_coefficient(2) 0 sage: f.fourier_series_sine_coefficient(3) 4/3/pi If the domain of the piecewise-defined function encompasses more than one period, the half-period must be passed as the second argument; for instance:: sage: f2 = piecewise([((-1,0), -1), ((0,1), 1), ....: ((1,2), -1), ((2,3), 1)]) sage: bool(f2.restriction((-1,1)) == f) # f2 extends f on (-1,3) True sage: f2.fourier_series_sine_coefficient(1, 1) # half-period = 1 4/pi sage: f2.fourier_series_sine_coefficient(3, 1) # half-period = 1 4/3/pi The default half-period is 2 and one has:: sage: f2.fourier_series_sine_coefficient(1) # half-period = 2 0 sage: f2.fourier_series_sine_coefficient(3) # half-period = 2 0 The Fourier coefficients obtained from ``f`` are actually recovered for `n=2` and `n=6` respectively:: sage: f2.fourier_series_sine_coefficient(2) 4/pi sage: f2.fourier_series_sine_coefficient(6) 4/3/pi """ from sage.all import sin, pi L0 = (self.domain().sup() - self.domain().inf()) / 2 if not L: L = L0 else: m = L0 / L if not (m.is_integer() and m > 0): raise ValueError("the width of the domain of " + "{} is not a multiple ".format(self) + "of the given period") x = SR.var('x') result = 0 for domain, f in parameters: for interval in domain: a = interval.lower() b = interval.upper() result += (f*sin(pi*x*n/L)).integrate(x, a, b) return SR(result/L0).simplify_trig()
def revolution_plot3d(curve, trange, phirange=None, parallel_axis='z', axis=(0, 0), print_vector=False, show_curve=False, **kwds): r""" Return a plot of a revolved curve. There are three ways to call this function: - ``revolution_plot3d(f,trange)`` where `f` is a function located in the `x z` plane. - ``revolution_plot3d((f_x,f_z),trange)`` where `(f_x,f_z)` is a parametric curve on the `x z` plane. - ``revolution_plot3d((f_x,f_y,f_z),trange)`` where `(f_x,f_y,f_z)` can be any parametric curve. INPUT: - ``curve`` - A curve to be revolved, specified as a function, a 2-tuple or a 3-tuple. - ``trange`` - A 3-tuple `(t,t_{\min},t_{\max})` where t is the independent variable of the curve. - ``phirange`` - A 2-tuple of the form `(\phi_{\min},\phi_{\max})`, (default `(0,\pi)`) that specifies the angle in which the curve is to be revolved. - ``parallel_axis`` - A string (Either 'x', 'y', or 'z') that specifies the coordinate axis parallel to the revolution axis. - ``axis`` - A 2-tuple that specifies the position of the revolution axis. If parallel is: - 'z' - then axis is the point in which the revolution axis intersects the `x y` plane. - 'x' - then axis is the point in which the revolution axis intersects the `y z` plane. - 'y' - then axis is the point in which the revolution axis intersects the `x z` plane. - ``print_vector`` - If True, the parametrization of the surface of revolution will be printed. - ``show_curve`` - If True, the curve will be displayed. EXAMPLES: Let's revolve a simple function around different axes:: sage: u = var('u') sage: f = u^2 sage: revolution_plot3d(f, (u,0,2), show_curve=True, opacity=0.7).show(aspect_ratio=(1,1,1)) .. PLOT:: u = var('u') f = u**2 P = revolution_plot3d(f, (u,0,2), show_curve=True, opacity=0.7).plot() sphinx_plot(P) If we move slightly the axis, we get a goblet-like surface:: sage: revolution_plot3d(f, (u,0,2), axis=(1,0.2), show_curve=True, opacity=0.5).show(aspect_ratio=(1,1,1)) .. PLOT:: u = var('u') f = u**2 P = revolution_plot3d(f, (u,0,2), axis=(1,0.2), show_curve=True, opacity=0.5).plot() sphinx_plot(P) A common problem in calculus books, find the volume within the following revolution solid:: sage: line = u sage: parabola = u^2 sage: sur1 = revolution_plot3d(line, (u,0,1), opacity=0.5, rgbcolor=(1,0.5,0), show_curve=True, parallel_axis='x') sage: sur2 = revolution_plot3d(parabola, (u,0,1), opacity=0.5, rgbcolor=(0,1,0), show_curve=True, parallel_axis='x') sage: (sur1+sur2).show() .. PLOT:: u = var('u') line = u parabola = u**2 sur1 = revolution_plot3d(line, (u,0,1), opacity=0.5, rgbcolor=(1,0.5,0), show_curve=True, parallel_axis='x') sur2 = revolution_plot3d(parabola, (u,0,1), opacity=0.5, rgbcolor=(0,1,0), show_curve=True, parallel_axis='x') P = sur1 + sur2 sphinx_plot(P) Now let's revolve a parametrically defined circle. We can play with the topology of the surface by changing the axis, an axis in `(0,0)` (as the previous one) will produce a sphere-like surface:: sage: u = var('u') sage: circle = (cos(u), sin(u)) sage: revolution_plot3d(circle, (u,0,2*pi), axis=(0,0), show_curve=True, opacity=0.5).show(aspect_ratio=(1,1,1)) .. PLOT:: u = var('u') circle = (cos(u), sin(u)) P = revolution_plot3d(circle, (u,0,2*pi), axis=(0,0), show_curve=True, opacity=0.5) sphinx_plot(P) An axis on `(0,y)` will produce a cylinder-like surface:: sage: revolution_plot3d(circle, (u,0,2*pi), axis=(0,2), show_curve=True, opacity=0.5).show(aspect_ratio=(1,1,1)) .. PLOT:: u = var('u') circle = (cos(u), sin(u)) P = revolution_plot3d(circle, (u,0,2*pi), axis=(0,2), show_curve=True, opacity=0.5) sphinx_plot(P) And any other axis will produce a torus-like surface:: sage: revolution_plot3d(circle, (u,0,2*pi), axis=(2,0), show_curve=True, opacity=0.5).show(aspect_ratio=(1,1,1)) .. PLOT:: u = var('u') circle = (cos(u), sin(u)) P = revolution_plot3d(circle, (u,0,2*pi), axis=(2,0), show_curve=True, opacity=0.5) sphinx_plot(P) Now, we can get another goblet-like surface by revolving a curve in 3d:: sage: u = var('u') sage: curve = (u, cos(4*u), u^2) sage: P = revolution_plot3d(curve, (u,0,2), show_curve=True, parallel_axis='z',axis=(1,.2), opacity=0.5) sage: P.show(aspect_ratio=(1,1,1)) .. PLOT:: u = var('u') curve = (u, cos(4*u), u**2) P = revolution_plot3d(curve, (u,0,2), show_curve=True, parallel_axis='z', axis=(1,.2), opacity=0.5) sphinx_plot(P) A curvy curve with only a quarter turn:: sage: u = var('u') sage: curve = (sin(3*u), .8*cos(4*u), cos(u)) sage: revolution_plot3d(curve, (u,0,pi), (0,pi/2), show_curve=True, parallel_axis='z', opacity=0.5).show(aspect_ratio=(1,1,1),frame=False) .. PLOT:: u = var('u') curve = (sin(3*u), .8*cos(4*u), cos(u)) P = revolution_plot3d(curve, (u,0,pi), (0,pi/2), show_curve=True, parallel_axis='z', opacity=0.5) sphinx_plot(P) One can also color the surface using a coloring function of two parameters and a colormap as follows:: sage: u, phi = var('u,phi') sage: def cf(u,phi): return sin(phi+u) ^ 2 sage: curve = (1+u^2/4, 0, u) sage: revolution_plot3d(curve, (u,-2,2), (0,2*pi), parallel_axis='z', color=(cf, colormaps.PiYG)).show(aspect_ratio=(1,1,1)) .. PLOT:: u, phi = var('u,phi') def cf(u, phi): return sin(phi+u) ** 2 curve = (1+u**2/4, 0, u) P = revolution_plot3d(curve, (u,-2,2), (0,2*pi), parallel_axis='z', color=(cf, colormaps.PiYG)) sphinx_plot(P) The first parameter of the coloring function will be identified with the parameter of the curve, and the second with the angle parameter. .. WARNING:: This kind of coloring using a colormap can be visualized using Jmol, Tachyon (option ``viewer='tachyon'``) and Canvas3D (option ``viewer='canvas3d'`` in the notebook). """ from sage.symbolic.ring import SR from sage.symbolic.constants import pi from sage.functions.other import sqrt from sage.functions.trig import sin from sage.functions.trig import cos from sage.functions.trig import atan2 if parallel_axis not in ['x', 'y', 'z']: raise ValueError("parallel_axis must be either 'x', 'y', or 'z'.") vart = trange[0] if str(vart) == 'phi': phi = SR.var('fi') else: phi = SR.var('phi') if phirange is None: # this if-else provides a phirange phirange = (phi, 0, 2 * pi) elif len(phirange) == 3: phi = phirange[0] pass else: phirange = (phi, phirange[0], phirange[1]) if isinstance(curve, (tuple, list)): #this if-else provides a vector v to be plotted #if curve is a tuple or a list of length 2, it is interpreted as a parametric curve #in the x-z plane. #if it is of length 3 it is interpreted as a parametric curve in 3d space if len(curve) == 2: x = curve[0] y = 0 z = curve[1] elif len(curve) == 3: x = curve[0] y = curve[1] z = curve[2] else: x = vart y = 0 z = curve phase = 0 if parallel_axis == 'z': x0 = axis[0] y0 = axis[1] # (0,0) must be handled separately for the phase value if x0 != 0 or y0 != 0: phase = atan2(y - y0, x - x0) R = sqrt((x - x0)**2 + (y - y0)**2) v = (R * cos(phi + phase) + x0, R * sin(phi + phase) + y0, z) elif parallel_axis == 'x': y0 = axis[0] z0 = axis[1] # (0,0) must be handled separately for the phase value if z0 != 0 or y0 != 0: phase = atan2(z - z0, y - y0) R = sqrt((y - y0)**2 + (z - z0)**2) v = (x, R * cos(phi + phase) + y0, R * sin(phi + phase) + z0) elif parallel_axis == 'y': x0 = axis[0] z0 = axis[1] # (0,0) must be handled separately for the phase value if z0 != 0 or x0 != 0: phase = atan2(z - z0, x - x0) R = sqrt((x - x0)**2 + (z - z0)**2) v = (R * cos(phi + phase) + x0, y, R * sin(phi + phase) + z0) if print_vector: print(v) if show_curve: curveplot = parametric_plot3d((x, y, z), trange, thickness=2, rgbcolor=(1, 0, 0)) return parametric_plot3d(v, trange, phirange, **kwds) + curveplot return parametric_plot3d(v, trange, phirange, **kwds)
def __call__(self, function_pieces, **kwds): r""" Piecewise functions INPUT: - ``function_pieces`` -- a list of pairs consisting of a domain and a symbolic function. - ``var=x`` -- a symbolic variable or ``None`` (default). The real variable in which the function is piecewise in. OUTPUT: A piecewise-defined function. A ``ValueError`` will be raised if the domains of the pieces are not pairwise disjoint. EXAMPLES:: sage: my_abs = piecewise([((-1, 0), -x), ([0, 1], x)], var=x); my_abs piecewise(x|-->-x on (-1, 0), x|-->x on [0, 1]; x) sage: [ my_abs(i/5) for i in range(-4, 5)] [4/5, 3/5, 2/5, 1/5, 0, 1/5, 2/5, 3/5, 4/5] TESTS:: sage: piecewise([([-1, 0], -x), ([0, 1], x)], var=x) Traceback (most recent call last): ... ValueError: domains must be pairwise disjoint sage: step = piecewise([((-1, 0), -1), ([0, 0], 0), ((0, 1), 1)], var=x); step piecewise(x|-->-1 on (-1, 0), x|-->0 on {0}, x|-->1 on (0, 1); x) sage: step(-1/2), step(0), step(1/2) (-1, 0, 1) """ from types import FunctionType var = kwds.pop('var', None) parameters = [] domain_list = [] for piece in function_pieces: domain, function = piece if not isinstance(domain, RealSet): domain = RealSet(domain) if domain.is_empty(): continue if isinstance(function, FunctionType): if var is None: var = SR.var('x') if function.func_code.co_argcount == 0: function = function() else: function = function(var) function = SR(function) if var is None and len(function.variables()) > 0: var = function.variables()[0] parameters.append((domain, function)) domain_list.append(domain) if not RealSet.are_pairwise_disjoint(*domain_list): raise ValueError('domains must be pairwise disjoint') if var is None: var = self.default_variable() parameters = SR._force_pyobject(tuple(parameters), recursive=False) return BuiltinFunction.__call__(self, parameters, var, **kwds)
def symbolic_expression(x): """ Create a symbolic expression or vector of symbolic expressions from x. INPUT: - ``x`` - an object OUTPUT: - a symbolic expression. EXAMPLES:: sage: a = symbolic_expression(3/2); a 3/2 sage: type(a) <class 'sage.symbolic.expression.Expression'> sage: R.<x> = QQ[]; type(x) <class 'sage.rings.polynomial.polynomial_rational_flint.Polynomial_rational_flint'> sage: a = symbolic_expression(2*x^2 + 3); a 2*x^2 + 3 sage: type(a) <class 'sage.symbolic.expression.Expression'> sage: from sage.structure.element import Expression sage: isinstance(a, Expression) True sage: a in SR True sage: a.parent() Symbolic Ring Note that equations exist in the symbolic ring:: sage: E = EllipticCurve('15a'); E Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 10*x - 10 over Rational Field sage: symbolic_expression(E) x*y + y^2 + y == x^3 + x^2 - 10*x - 10 sage: symbolic_expression(E) in SR True If ``x`` is a list or tuple, create a vector of symbolic expressions:: sage: v=symbolic_expression([x,1]); v (x, 1) sage: v.base_ring() Symbolic Ring sage: v=symbolic_expression((x,1)); v (x, 1) sage: v.base_ring() Symbolic Ring sage: v=symbolic_expression((3,1)); v (3, 1) sage: v.base_ring() Symbolic Ring sage: E = EllipticCurve('15a'); E Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 10*x - 10 over Rational Field sage: v=symbolic_expression([E,E]); v (x*y + y^2 + y == x^3 + x^2 - 10*x - 10, x*y + y^2 + y == x^3 + x^2 - 10*x - 10) sage: v.base_ring() Symbolic Ring Likewise, if ``x`` is a vector, create a vector of symbolic expressions:: sage: u = vector([1, 2, 3]) sage: v = symbolic_expression(u); v (1, 2, 3) sage: v.parent() Vector space of dimension 3 over Symbolic Ring If ``x`` is a list or tuple of lists/tuples/vectors, create a matrix of symbolic expressions:: sage: M = symbolic_expression([[1, x, x^2], (x, x^2, x^3), vector([x^2, x^3, x^4])]); M [ 1 x x^2] [ x x^2 x^3] [x^2 x^3 x^4] sage: M.parent() Full MatrixSpace of 3 by 3 dense matrices over Symbolic Ring If ``x`` is a matrix, create a matrix of symbolic expressions:: sage: A = matrix([[1, 2, 3], [4, 5, 6]]) sage: B = symbolic_expression(A); B [1 2 3] [4 5 6] sage: B.parent() Full MatrixSpace of 2 by 3 dense matrices over Symbolic Ring If ``x`` is a function, for example defined by a ``lambda`` expression, create a symbolic function:: sage: f = symbolic_expression(lambda z: z^2 + 1); f z |--> z^2 + 1 sage: f.parent() Callable function ring with argument z sage: f(7) 50 If ``x`` is a list or tuple of functions, or if ``x`` is a function that returns a list or tuple, create a callable symbolic vector:: sage: symbolic_expression([lambda mu, nu: mu^2 + nu^2, lambda mu, nu: mu^2 - nu^2]) (mu, nu) |--> (mu^2 + nu^2, mu^2 - nu^2) sage: f = symbolic_expression(lambda uwu: [1, uwu, uwu^2]); f uwu |--> (1, uwu, uwu^2) sage: f.parent() Vector space of dimension 3 over Callable function ring with argument uwu sage: f(5) (1, 5, 25) sage: f(5).parent() Vector space of dimension 3 over Symbolic Ring TESTS: Lists, tuples, and vectors of length 0 become vectors over a symbolic ring:: sage: symbolic_expression([]).parent() Vector space of dimension 0 over Symbolic Ring sage: symbolic_expression(()).parent() Vector space of dimension 0 over Symbolic Ring sage: symbolic_expression(vector(QQ, 0)).parent() Vector space of dimension 0 over Symbolic Ring If a matrix has dimension 0, the result is still a matrix over a symbolic ring:: sage: symbolic_expression(matrix(QQ, 2, 0)).parent() Full MatrixSpace of 2 by 0 dense matrices over Symbolic Ring sage: symbolic_expression(matrix(QQ, 0, 3)).parent() Full MatrixSpace of 0 by 3 dense matrices over Symbolic Ring Also functions defined using ``def`` can be used, but we do not advertise it as a use case:: sage: def sos(x, y): ....: return x^2 + y^2 sage: symbolic_expression(sos) (x, y) |--> x^2 + y^2 Functions that take a varying number of arguments or keyword-only arguments are not accepted:: sage: def variadic(x, *y): ....: return x sage: symbolic_expression(variadic) Traceback (most recent call last): ... TypeError: unable to convert <function variadic at 0x...> to a symbolic expression sage: def function_with_keyword_only_arg(x, *, sign=1): ....: return sign * x sage: symbolic_expression(function_with_keyword_only_arg) Traceback (most recent call last): ... TypeError: unable to convert <function function_with_keyword_only_arg at 0x...> to a symbolic expression """ from sage.symbolic.expression import Expression from sage.symbolic.ring import SR from sage.modules.free_module_element import is_FreeModuleElement from sage.structure.element import is_Matrix if isinstance(x, Expression): return x elif hasattr(x, '_symbolic_'): return x._symbolic_(SR) elif isinstance(x, (tuple, list)) or is_FreeModuleElement(x): expressions = [symbolic_expression(item) for item in x] if not expressions: # Make sure it is symbolic also when length is 0 return vector(SR, 0) if is_FreeModuleElement(expressions[0]): return matrix(expressions) return vector(expressions) elif is_Matrix(x): if not x.nrows() or not x.ncols(): # Make sure it is symbolic and of correct dimensions # also when a matrix dimension is 0 return matrix(SR, x.nrows(), x.ncols()) rows = [symbolic_expression(row) for row in x.rows()] return matrix(rows) elif callable(x): from inspect import signature, Parameter try: s = signature(x) except ValueError: pass else: if all(param.kind in (Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD) for param in s.parameters.values()): vars = [SR.var(name) for name in s.parameters.keys()] result = x(*vars) if isinstance(result, (tuple, list)): return vector(SR, result).function(*vars) else: return SR(result).function(*vars) return SR(x)