Ejemplo n.º 1
0
    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
Ejemplo n.º 2
0
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)
Ejemplo n.º 3
0
        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
Ejemplo n.º 4
0
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))])
Ejemplo n.º 5
0
        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()
Ejemplo n.º 6
0
    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
Ejemplo n.º 7
0
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)
Ejemplo n.º 8
0
 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
Ejemplo n.º 9
0
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)
Ejemplo n.º 10
0
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)
Ejemplo n.º 11
0
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],
        ]
    )
Ejemplo n.º 12
0
 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)]
Ejemplo n.º 14
0
        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()
Ejemplo n.º 15
0
Archivo: lwe.py Proyecto: sagemath/sage
    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)
Ejemplo n.º 16
0
    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)
Ejemplo n.º 17
0
        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)
Ejemplo n.º 18
0
Archivo: lwe.py Proyecto: sagemath/sage
    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)
Ejemplo n.º 19
0
    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
Ejemplo n.º 20
0
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
Ejemplo n.º 21
0
    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)
Ejemplo n.º 22
0
        [----+--------------+--------------+----]
        [   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,
Ejemplo n.º 23
0
        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()
Ejemplo n.º 24
0
    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
Ejemplo n.º 25
0
 def composition(self, ex, operator):
     if operator in self._functions:
         return SR.var(str(operator))
     else:
         return super().composition(ex, operator)
Ejemplo n.º 26
0
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)
Ejemplo n.º 27
0
        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()
Ejemplo n.º 28
0
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
Ejemplo n.º 29
0
    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)
Ejemplo n.º 30
0
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)
Ejemplo n.º 31
0
        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
Ejemplo n.º 33
0
 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')
Ejemplo n.º 34
0
        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()
Ejemplo n.º 35
0
        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()
Ejemplo n.º 36
0
        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()
Ejemplo n.º 37
0
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)
Ejemplo n.º 38
0
    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)
Ejemplo n.º 39
0
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)