Example #1
0
def equify(f):
    """
    Returns the equation rewritten as a symbolic function to give
    negative values when True, positive when False.

    EXAMPLES::

        sage: from sage.plot.contour_plot import equify
        sage: var('x, y')
        (x, y)
        sage: equify(x^2 < 2)
        x^2 - 2
        sage: equify(x^2 > 2)
        -x^2 + 2
        sage: equify(x*y > 1)
        -x*y + 1
        sage: equify(y > 0)
        -y
        sage: f=equify(lambda x,y: x>y)
        sage: f(1,2)
        1
        sage: f(2,1)
        -1
    """
    import operator
    from sage.calculus.all import symbolic_expression
    from sage.symbolic.expression import is_Expression
    if not is_Expression(f):
        return lambda x, y: -1 if f(x, y) else 1

    op = f.operator()
    if op is operator.gt or op is operator.ge:
        return symbolic_expression(f.rhs() - f.lhs())
    else:
        return symbolic_expression(f.lhs() - f.rhs())
Example #2
0
def equify(f):
    """
    Returns the equation rewritten as a symbolic function to give
    negative values when True, positive when False.
    
    EXAMPLES::
    
        sage: from sage.plot.contour_plot import equify
        sage: var('x, y')
        (x, y)
        sage: equify(x^2 < 2)
        x^2 - 2
        sage: equify(x^2 > 2)
        -x^2 + 2
        sage: equify(x*y > 1)
        -x*y + 1
        sage: equify(y > 0)
        -y
        sage: f=equify(lambda x,y: x>y)
        sage: f(1,2)
        1
        sage: f(2,1)
        -1
    """
    import operator
    from sage.calculus.all import symbolic_expression
    from sage.symbolic.expression import is_Expression
    if not is_Expression(f):
        return lambda x,y: -1 if f(x,y) else 1

    op = f.operator()
    if op is operator.gt or op is operator.ge:
        return symbolic_expression(f.rhs() - f.lhs())
    else:
        return symbolic_expression(f.lhs() - f.rhs())
Example #3
0
 def set_output(self, value):
     self._has_output = True
     self._type = str(type(value))
     from sage.plot.graphics import is_Graphics
     from sage.symbolic.expression import is_Expression
     if is_Graphics(value):
         self._text = str(value)
         self._png = 'graphics_'+str(self.index())+'.png'
         self._mime = CommandLog.OUTPUT_PNG
         value.save(self._png, transparent=True)
     elif is_Expression(value):
         self._text = str(value)
         self._latex = str(value._latex_())
         self._mime = CommandLog.OUTPUT_LATEX
     else:
         self._text = value.__repr__()
         self._mime = CommandLog.OUTPUT_TEXT
Example #4
0
    def _eval_(self, x):
        """
        EXAMPLES::

            sage: sec(pi/4)
            sqrt(2)
            sage: sec(x).subs(x==pi/4)
            sqrt(2)
            sage: sec(pi/7)
            sec(1/7*pi)
            sage: sec(x)
            sec(x)
        """
        cos_x = cos(x)
        if is_Expression(cos_x) and cos_x.operator() is cos:
            return None
        else:
            return 1/cos_x
Example #5
0
    def _eval_(self, x):
        """
        EXAMPLES::

            sage: csc(pi/4)
            sqrt(2)
            sage: csc(x).subs(x==pi/4)
            sqrt(2)
            sage: csc(pi/7)
            csc(1/7*pi)
            sage: csc(x)
            csc(x)
        """
        sin_x = sin(x)
        if is_Expression(sin_x) and sin_x.operator() is sin:
            return None
        else:
            return 1/sin_x
Example #6
0
    def _eval_(self, x):
        """
        EXAMPLES::

            sage: cot(pi/4)
            1
            sage: cot(x).subs(x==pi/4)
            1
            sage: cot(pi/7)
            cot(1/7*pi)
            sage: cot(x)
            cot(x)
        """
        tan_x = tan(x)
        if is_Expression(tan_x) and tan_x.operator() is tan:
            return None
        else:
            return 1/tan_x
Example #7
0
def is_CallableSymbolicExpression(x):
    r"""
    Returns ``True`` if ``x`` is a callable symbolic
    expression.

    EXAMPLES::

        sage: from sage.symbolic.callable import is_CallableSymbolicExpression
        sage: var('a x y z')
        (a, x, y, z)
        sage: f(x,y) = a + 2*x + 3*y + z
        sage: is_CallableSymbolicExpression(f)
        True
        sage: is_CallableSymbolicExpression(a+2*x)
        False
        sage: def foo(n): return n^2
        ...
        sage: is_CallableSymbolicExpression(foo)
        False
    """
    from sage.symbolic.expression import is_Expression
    return is_Expression(x) and isinstance(x.parent(), CallableSymbolicExpressionRing_class)
Example #8
0
def is_CallableSymbolicExpression(x):
    r"""
    Returns ``True`` if ``x`` is a callable symbolic
    expression.

    EXAMPLES::

        sage: from sage.symbolic.callable import is_CallableSymbolicExpression
        sage: var('a x y z')
        (a, x, y, z)
        sage: f(x,y) = a + 2*x + 3*y + z
        sage: is_CallableSymbolicExpression(f)
        True
        sage: is_CallableSymbolicExpression(a+2*x)
        False
        sage: def foo(n): return n^2
        ...
        sage: is_CallableSymbolicExpression(foo)
        False
    """
    from sage.symbolic.expression import is_Expression
    return is_Expression(x) and isinstance(x.parent(), CallableSymbolicExpressionRing_class)
Example #9
0
def implicit_plot(f, xrange, yrange, **options):
    r"""
    ``implicit_plot`` takes a function of two variables, `f(x,y)`
    and plots the curve `f(x,y) = 0` over the specified
    ``xrange`` and ``yrange`` as demonstrated below.

    ``implicit_plot(f, (xmin, xmax), (ymin, ymax), ...)``

    ``implicit_plot(f, (x, xmin, xmax), (y, ymin, ymax), ...)``

    INPUT:

    - ``f`` -- a function of two variables or equation in two variables

    - ``(xmin, xmax)`` -- 2-tuple, the range of ``x`` values or ``(x,xmin,xmax)``

    - ``(ymin, ymax)`` -- 2-tuple, the range of ``y`` values or ``(y,ymin,ymax)``

    The following inputs must all be passed in as named parameters:

    - ``plot_points`` -- integer (default: 150); number of points to plot
      in each direction of the grid

    - ``fill`` -- boolean (default: ``False``); if ``True``, fill the region
      `f(x,y) < 0`.

    - ``linewidth`` -- integer (default: None), if a single integer all levels
      will be of the width given, otherwise the levels will be plotted with the
      widths in the order given.

    - ``linestyle`` -- string (default: None), the style of the line to be
      plotted, one of: ``"solid"``, ``"dashed"``, ``"dashdot"`` or
      ``"dotted"``, respectively ``"-"``, ``"--"``, ``"-."``, or ``":"``.

    - ``color`` -- string (default: ``blue``), the color of the plot. Colors are
      defined in :mod:`sage.plot.colors`; try ``colors?`` to see them all.

    - ``legend_label`` -- the label for this item in the legend

    - ``base`` - (default: 10) the base of the logarithm if
      a logarithmic scale is set. This must be greater than 1. The base
      can be also given as a list or tuple ``(basex, basey)``.
      ``basex`` sets the base of the logarithm along the horizontal
      axis and ``basey`` sets the base along the vertical axis.

    - ``scale`` -- (default: ``"linear"``) string. The scale of the axes.
      Possible values are ``"linear"``, ``"loglog"``, ``"semilogx"``,
      ``"semilogy"``.

      The scale can be also be given as single argument that is a list
      or tuple ``(scale, base)`` or ``(scale, basex, basey)``.

      The ``"loglog"`` scale sets both the horizontal and vertical axes to
      logarithmic scale. The ``"semilogx"`` scale sets the horizontal axis
      to logarithmic scale. The ``"semilogy"`` scale sets the vertical axis
      to logarithmic scale. The ``"linear"`` scale is the default value
      when :class:`~sage.plot.graphics.Graphics` is initialized.

    EXAMPLES:

    A simple circle with a radius of 2. Note that
    since the input function is an expression, we need to explicitly
    declare the variables in 3-tuples for the range::

        sage: var("x y")
        (x, y)
        sage: implicit_plot(x^2+y^2-2, (x,-3,3), (y,-3,3))
        Graphics object consisting of 1 graphics primitive

    I can do the same thing, but using a callable function so I don't need
    to explicitly define the variables in the ranges, and filling the inside::

        sage: f(x,y) = x^2 + y^2 - 2
        sage: implicit_plot(f, (-3, 3), (-3, 3),fill=True)
        Graphics object consisting of 1 graphics primitive

    The same circle but with a different line width::

        sage: implicit_plot(f, (-3,3), (-3,3), linewidth=6)
        Graphics object consisting of 1 graphics primitive

    And again the same circle but this time with a dashdot border::

        sage: implicit_plot(f, (-3,3), (-3,3), linestyle='dashdot')
        Graphics object consisting of 1 graphics primitive

    You can also plot an equation::

        sage: var("x y")
        (x, y)
        sage: implicit_plot(x^2+y^2 == 2, (x,-3,3), (y,-3,3))
        Graphics object consisting of 1 graphics primitive

    You can even change the color of the plot::

        sage: implicit_plot(x^2+y^2 == 2, (x,-3,3), (y,-3,3), color="red")
        Graphics object consisting of 1 graphics primitive

    Here is a beautiful (and long) example which also tests that all
    colors work with this::

        sage: G = Graphics()
        sage: counter = 0
        sage: for col in colors.keys(): # long time
        ...       G += implicit_plot(x^2+y^2==1+counter*.1, (x,-4,4),(y,-4,4),color=col)
        ...       counter += 1
        sage: G.show(frame=False)

    We can define a level-`n` approximation of the boundary of the
    Mandelbrot set::

        sage: def mandel(n):
        ...       c = polygen(CDF, 'c')
        ...       z = 0
        ...       for i in range(n):
        ...           z = z*z + c
        ...       def f(x, y):
        ...           val = z(CDF(x, y))
        ...           return val.norm() - 4
        ...       return f

    The first-level approximation is just a circle::

        sage: implicit_plot(mandel(1), (-3, 3), (-3, 3))
        Graphics object consisting of 1 graphics primitive

    A third-level approximation starts to get interesting::

        sage: implicit_plot(mandel(3), (-2, 1), (-1.5, 1.5))
        Graphics object consisting of 1 graphics primitive

    The seventh-level approximation is a degree 64 polynomial, and
    ``implicit_plot`` does a pretty good job on this part of the curve.
    (``plot_points=200`` looks even better, but it takes over a second.)

    ::

        sage: implicit_plot(mandel(7), (-0.3, 0.05), (-1.15, -0.9),plot_points=50)
        Graphics object consisting of 1 graphics primitive

    When making a filled implicit plot using a python function rather than a
    symbolic expression the user should increase the number of plot points to
    avoid artifacts::

        sage: implicit_plot(lambda x,y: x^2+y^2-2, (x,-3,3), (y,-3,3), fill=True, plot_points=500) # long time
        Graphics object consisting of 1 graphics primitive

    An example of an implicit plot on 'loglog' scale::

        sage: implicit_plot(x^2+y^2 == 200, (x,1,200), (y,1,200), scale='loglog')
        Graphics object consisting of 1 graphics primitive

    TESTS::

        sage: f(x,y) = x^2 + y^2 - 2
        sage: implicit_plot(f, (-3, 3), (-3, 3),fill=5)
        Traceback (most recent call last):
        ...
        ValueError: fill=5 is not supported
    """
    from sage.symbolic.expression import is_SymbolicEquation
    if is_SymbolicEquation(f):
        if f.operator() != operator.eq:
            raise ValueError(
                "input to implicit plot must be function or equation")
        f = f.lhs() - f.rhs()
    linewidths = options.pop('linewidth', None)
    linestyles = options.pop('linestyle', None)

    if 'color' in options:
        options['cmap'] = [options.pop('color', None)]

    if options['fill'] is True:
        options.pop('fill')
        options.pop('contours', None)
        options.pop('cmap', None)
        from sage.symbolic.expression import is_Expression
        if not is_Expression(f):
            return region_plot(lambda x, y: f(x, y) < 0,
                               xrange,
                               yrange,
                               borderwidth=linewidths,
                               borderstyle=linestyles,
                               **options)
        else:
            return region_plot(f < 0,
                               xrange,
                               yrange,
                               borderwidth=linewidths,
                               borderstyle=linestyles,
                               **options)
    elif options['fill'] is False:
        return contour_plot(f,
                            xrange,
                            yrange,
                            linewidths=linewidths,
                            linestyles=linestyles,
                            **options)
    else:
        raise ValueError("fill=%s is not supported" % options['fill'])
Example #10
0
def solve_mod(eqns, modulus, solution_dict=False):
    r"""
    Return all solutions to an equation or list of equations modulo the
    given integer modulus. Each equation must involve only polynomials
    in 1 or many variables.

    By default the solutions are returned as `n`-tuples, where `n`
    is the number of variables appearing anywhere in the given
    equations. The variables are in alphabetical order.

    INPUT:


    -  ``eqns`` - equation or list of equations

    -  ``modulus`` - an integer

    -  ``solution_dict`` - bool (default: False); if True or non-zero,
       return a list of dictionaries containing the solutions. If there
       are no solutions, return an empty list (rather than a list containing
       an empty dictionary). Likewise, if there's only a single solution,
       return a list containing one dictionary with that solution.


    EXAMPLES::

        sage: var('x,y')
        (x, y)
        sage: solve_mod([x^2 + 2 == x, x^2 + y == y^2], 14)
        [(4, 2), (4, 6), (4, 9), (4, 13)]
        sage: solve_mod([x^2 == 1, 4*x  == 11], 15)
        [(14,)]

    Fermat's equation modulo 3 with exponent 5::

        sage: var('x,y,z')
        (x, y, z)
        sage: solve_mod([x^5 + y^5 == z^5], 3)
        [(0, 0, 0), (0, 1, 1), (0, 2, 2), (1, 0, 1), (1, 1, 2), (1, 2, 0), (2, 0, 2), (2, 1, 0), (2, 2, 1)]

    We can solve with respect to a bigger modulus if it consists only of small prime factors::

        sage: [d] = solve_mod([5*x + y == 3, 2*x - 3*y == 9], 3*5*7*11*19*23*29, solution_dict = True)
        sage: d[x]
        12915279
        sage: d[y]
        8610183

    For cases where there are relatively few solutions and the prime
    factors are small, this can be efficient even if the modulus itself
    is large::

        sage: sorted(solve_mod([x^2 == 41], 10^20))
        [(4538602480526452429,), (11445932736758703821,), (38554067263241296179,),
        (45461397519473547571,), (54538602480526452429,), (61445932736758703821,),
        (88554067263241296179,), (95461397519473547571,)]

    We solve a simple equation modulo 2::

        sage: x,y = var('x,y')
        sage: solve_mod([x == y], 2)
        [(0, 0), (1, 1)]

    .. warning::

       The current implementation splits the modulus into prime
       powers, then naively enumerates all possible solutions
       (starting modulo primes and then working up through prime
       powers), and finally combines the solution using the Chinese
       Remainder Theorem.  The interface is good, but the algorithm is
       very inefficient if the modulus has some larger prime factors! Sage
       *does* have the ability to do something much faster in certain
       cases at least by using Groebner basis, linear algebra
       techniques, etc. But for a lot of toy problems this function as
       is might be useful. At least it establishes an interface.


    TESTS:

    Make sure that we short-circuit in at least some cases::

        sage: solve_mod([2*x==1], 2*next_prime(10^50))
        []

    Try multi-equation cases::

        sage: x, y, z = var("x y z")
        sage: solve_mod([2*x^2 + x*y, -x*y+2*y^2+x-2*y, -2*x^2+2*x*y-y^2-x-y], 12)
        [(0, 0), (4, 4), (0, 3), (4, 7)]
        sage: eqs = [-y^2+z^2, -x^2+y^2-3*z^2-z-1, -y*z-z^2-x-y+2, -x^2-12*z^2-y+z]
        sage: solve_mod(eqs, 11)
        [(8, 5, 6)]

    Confirm that modulus 1 now behaves as it should::

        sage: x, y = var("x y")
        sage: solve_mod([x==1], 1)
        [(0,)]
        sage: solve_mod([2*x^2+x*y, -x*y+2*y^2+x-2*y, -2*x^2+2*x*y-y^2-x-y], 1)
        [(0, 0)]


    """
    from sage.rings.all import Integer, Integers, crt_basis
    from sage.symbolic.expression import is_Expression
    from sage.misc.all import cartesian_product_iterator
    from sage.modules.all import vector
    from sage.matrix.all import matrix

    if not isinstance(eqns, (list, tuple)):
        eqns = [eqns]
    eqns = [eq if is_Expression(eq) else (eq.lhs() - eq.rhs()) for eq in eqns]
    modulus = Integer(modulus)
    if modulus < 1:
        raise ValueError("the modulus must be a positive integer")
    vars = list(set(sum([list(e.variables()) for e in eqns], [])))
    vars.sort(key=repr)

    if modulus == 1:  # degenerate case
        ans = [tuple(Integers(1)(0) for v in vars)]
        return ans

    factors = modulus.factor()
    crt_basis = vector(Integers(modulus),
                       crt_basis([p**i for p, i in factors]))
    solutions = []

    has_solution = True
    for p, i in factors:
        solution = _solve_mod_prime_power(eqns, p, i, vars)
        if len(solution) > 0:
            solutions.append(solution)
        else:
            has_solution = False
            break

    ans = []
    if has_solution:
        for solution in cartesian_product_iterator(solutions):
            solution_mat = matrix(Integers(modulus), solution)
            ans.append(
                tuple(
                    c.dot_product(crt_basis) for c in solution_mat.columns()))

    # if solution_dict == True:
    # Relaxed form suggested by Mike Hansen (#8553):
    if solution_dict:
        sol_dict = [dict(zip(vars, solution)) for solution in ans]
        return sol_dict
    else:
        return ans
Example #11
0
 def __call__(self, arg):
     if isinstance(arg, self.element_class) and arg.parent() is self:
         return arg
     if is_Expression(arg):
         arg = self._subs_jet_vars(arg)
     return self.element_class(self, self._polynomial_ring(arg))
Example #12
0
def solve(f, *args, **kwds):
    r"""
    Algebraically solve an equation or system of equations (over the
    complex numbers) for given variables. Inequalities and systems 
    of inequalities are also supported.    
    
    INPUT:
    
    -  ``f`` - equation or system of equations (given by a
       list or tuple)
    
    -  ``*args`` - variables to solve for.
    
    -  ``solution_dict`` - bool (default: False); if True or non-zero, 
       return a list of dictionaries containing the solutions. If there
       are no solutions, return an empty list (rather than a list containing
       an empty dictionary). Likewise, if there's only a single solution,
       return a list containing one dictionary with that solution.
    
    EXAMPLES::
    
        sage: x, y = var('x, y')
        sage: solve([x+y==6, x-y==4], x, y)
        [[x == 5, y == 1]]
        sage: solve([x^2+y^2 == 1, y^2 == x^3 + x + 1], x, y)
        [[x == -1/2*I*sqrt(3) - 1/2, y == -sqrt(-1/2*I*sqrt(3) + 3/2)],
         [x == -1/2*I*sqrt(3) - 1/2, y == sqrt(-1/2*I*sqrt(3) + 3/2)],
         [x == 1/2*I*sqrt(3) - 1/2, y == -sqrt(1/2*I*sqrt(3) + 3/2)],
         [x == 1/2*I*sqrt(3) - 1/2, y == sqrt(1/2*I*sqrt(3) + 3/2)],
         [x == 0, y == -1],
         [x == 0, y == 1]]
        sage: solve([sqrt(x) + sqrt(y) == 5, x + y == 10], x, y)
        [[x == -5/2*I*sqrt(5) + 5, y == 5/2*I*sqrt(5) + 5], [x == 5/2*I*sqrt(5) + 5, y == -5/2*I*sqrt(5) + 5]]
        sage: solutions=solve([x^2+y^2 == 1, y^2 == x^3 + x + 1], x, y, solution_dict=True)
        sage: for solution in solutions: print solution[x].n(digits=3), ",", solution[y].n(digits=3)
        -0.500 - 0.866*I , -1.27 + 0.341*I
        -0.500 - 0.866*I , 1.27 - 0.341*I
        -0.500 + 0.866*I , -1.27 - 0.341*I
        -0.500 + 0.866*I , 1.27 + 0.341*I
        0.000 , -1.00
        0.000 , 1.00

    Whenever possible, answers will be symbolic, but with systems of 
    equations, at times approximations will be given, due to the 
    underlying algorithm in Maxima::

        sage: sols = solve([x^3==y,y^2==x],[x,y]); sols[-1], sols[0]
        ([x == 0, y == 0], [x == (0.309016994375 + 0.951056516295*I),  y == (-0.809016994375 - 0.587785252292*I)])
        sage: sols[0][0].rhs().pyobject().parent()
        Complex Double Field

    If ``f`` is only one equation or expression, we use the solve method
    for symbolic expressions, which defaults to exact answers only::

        sage: solve([y^6==y],y)
        [y == e^(2/5*I*pi), y == e^(4/5*I*pi), y == e^(-4/5*I*pi), y == e^(-2/5*I*pi), y == 1, y == 0]
        sage: solve( [y^6 == y], y)==solve( y^6 == y, y)
        True

    .. note::

        For more details about solving a single equations, see
        the documentation for its solve.

    ::

        sage: from sage.symbolic.expression import Expression
        sage: Expression.solve(x^2==1,x)
        [x == -1, x == 1]

    We must solve with respect to actual variables::

        sage: z = 5
        sage: solve([8*z + y == 3, -z +7*y == 0],y,z)
        Traceback (most recent call last):
        ...
        TypeError: 5 is not a valid variable.

    If we ask for dictionaries containing the solutions, we get them::
    
        sage: solve([x^2-1],x,solution_dict=True)
        [{x: -1}, {x: 1}]
        sage: solve([x^2-4*x+4],x,solution_dict=True)
        [{x: 2}]
        sage: res = solve([x^2 == y, y == 4],x,y,solution_dict=True)
        sage: for soln in res: print "x: %s, y: %s"%(soln[x], soln[y])
        x: 2, y: 4
        x: -2, y: 4

    If there is a parameter in the answer, that will show up as 
    a new variable.  In the following example, ``r1`` is a real free
    variable (because of the ``r``)::

        sage: solve([x+y == 3, 2*x+2*y == 6],x,y)
        [[x == -r1 + 3, y == r1]]

    Especially with trigonometric functions, the dummy variable may
    be implicitly an integer (hence the ``z``)::

        sage: solve([cos(x)*sin(x) == 1/2, x+y == 0],x,y)
        [[x == 1/4*pi + pi*z68, y == -1/4*pi - pi*z68]]

    Expressions which are not equations are assumed to be set equal
    to zero, as with `x` in the following example::

        sage: solve([x, y == 2],x,y)
        [[x == 0, y == 2]]

    If ``True`` appears in the list of equations it is
    ignored, and if ``False`` appears in the list then no
    solutions are returned. E.g., note that the first
    ``3==3`` evaluates to ``True``, not to a
    symbolic equation.
    
    ::
    
        sage: solve([3==3, 1.00000000000000*x^3 == 0], x)
        [x == 0]
        sage: solve([1.00000000000000*x^3 == 0], x)
        [x == 0]
    
    Here, the first equation evaluates to ``False``, so
    there are no solutions::
    
        sage: solve([1==3, 1.00000000000000*x^3 == 0], x)
        []
    
    Completely symbolic solutions are supported::
    
        sage: var('s,j,b,m,g')
        (s, j, b, m, g)
        sage: sys = [ m*(1-s) - b*s*j, b*s*j-g*j ];
        sage: solve(sys,s,j)
        [[s == 1, j == 0], [s == g/b, j == (b - g)*m/(b*g)]]
        sage: solve(sys,(s,j))
        [[s == 1, j == 0], [s == g/b, j == (b - g)*m/(b*g)]]
        sage: solve(sys,[s,j])
        [[s == 1, j == 0], [s == g/b, j == (b - g)*m/(b*g)]]

    Inequalities can be also solved::

        sage: solve(x^2>8,x)
        [[x < -2*sqrt(2)], [x > 2*sqrt(2)]]

    Use use_grobner if no solution is obtained from to_poly_solve::

       sage: x,y=var('x y'); c1(x,y)=(x-5)^2+y^2-16; c2(x,y)=(y-3)^2+x^2-9
       sage: solve([c1(x,y),c2(x,y)],[x,y])                               
       [[x == -9/68*sqrt(55) + 135/68, y == -15/68*sqrt(5)*sqrt(11) + 123/68], [x == 9/68*sqrt(55) + 135/68, y == 15/68*sqrt(5)*sqrt(11) + 123/68]]
        
    TESTS::

        sage: solve([sin(x)==x,y^2==x],x,y)
        [sin(x) == x, y^2 == x]
        sage: solve(0==1,x)
        Traceback (most recent call last):
        ...
        TypeError:  The first argument must be a symbolic expression or a list of symbolic expressions.

    Test if the empty list is returned, too, when (a list of)
    dictionaries (is) are requested (#8553)::

        sage: solve([0==1],x)
        []
        sage: solve([0==1],x,solution_dict=True)
        []
        sage: solve([x==1,x==-1],x)
        []
        sage: solve([x==1,x==-1],x,solution_dict=True)
        []
        sage: solve((x==1,x==-1),x,solution_dict=0)
        []

    Relaxed form, suggested by Mike Hansen (#8553)::

        sage: solve([x^2-1],x,solution_dict=-1)
        [{x: -1}, {x: 1}]
        sage: solve([x^2-1],x,solution_dict=1)
        [{x: -1}, {x: 1}]
        sage: solve((x==1,x==-1),x,solution_dict=-1)
        []
        sage: solve((x==1,x==-1),x,solution_dict=1)
        []

    This inequality holds for any real ``x`` (trac #8078)::

        sage: solve(x^4+2>0,x)
        [x < +Infinity]

    """
    from sage.symbolic.expression import is_Expression
    if is_Expression(f): # f is a single expression
        ans = f.solve(*args,**kwds)
        return ans

    if not isinstance(f, (list, tuple)):
        raise TypeError("The first argument must be a symbolic expression or a list of symbolic expressions.")

    if len(f)==1 and is_Expression(f[0]):
        # f is a list with a single expression
        return f[0].solve(*args,**kwds)

    # f is a list of such expressions or equations
    from sage.symbolic.ring import is_SymbolicVariable

    if len(args)==0:
        raise TypeError, "Please input variables to solve for."
    if is_SymbolicVariable(args[0]):
        variables = args
    else:
        variables = tuple(args[0])
    
    for v in variables:
        if not is_SymbolicVariable(v):
            raise TypeError, "%s is not a valid variable."%v

    try:
        f = [s for s in f if s is not True]
    except TypeError:
        raise ValueError, "Unable to solve %s for %s"%(f, args)

    if any(s is False for s in f):
        return []

    from sage.calculus.calculus import maxima
    m = maxima(f)

    try:
        s = m.solve(variables)
    except: # if Maxima gave an error, try its to_poly_solve
        try:
            s = m.to_poly_solve(variables)
        except TypeError, mess: # if that gives an error, raise an error.
            if "Error executing code in Maxima" in str(mess):
                raise ValueError, "Sage is unable to determine whether the system %s can be solved for %s"%(f,args)
            else:
                raise
Example #13
0
def solve(f, *args, **kwds):
    r"""
    Algebraically solve an equation or system of equations (over the
    complex numbers) for given variables. Inequalities and systems
    of inequalities are also supported.

    INPUT:

    -  ``f`` - equation or system of equations (given by a
       list or tuple)

    -  ``*args`` - variables to solve for.

    -  ``solution_dict`` - bool (default: False); if True or non-zero,
       return a list of dictionaries containing the solutions. If there
       are no solutions, return an empty list (rather than a list containing
       an empty dictionary). Likewise, if there's only a single solution,
       return a list containing one dictionary with that solution.

    There are a few optional keywords if you are trying to solve a single
    equation.  They may only be used in that context.

    -  ``multiplicities`` - bool (default: False); if True,
       return corresponding multiplicities.  This keyword is
       incompatible with ``to_poly_solve=True`` and does not make
       any sense when solving inequalities.

    -  ``explicit_solutions`` - bool (default: False); require that
       all roots be explicit rather than implicit. Not used
       when solving inequalities.

    -  ``to_poly_solve`` - bool (default: False) or string; use
       Maxima's ``to_poly_solver`` package to search for more possible
       solutions, but possibly encounter approximate solutions.
       This keyword is incompatible with ``multiplicities=True``
       and is not used when solving inequalities. Setting ``to_poly_solve``
       to 'force' (string) omits Maxima's solve command (useful when
       some solutions of trigonometric equations are lost).


    EXAMPLES::

        sage: x, y = var('x, y')
        sage: solve([x+y==6, x-y==4], x, y)
        [[x == 5, y == 1]]
        sage: solve([x^2+y^2 == 1, y^2 == x^3 + x + 1], x, y)
        [[x == -1/2*I*sqrt(3) - 1/2, y == -sqrt(-1/2*I*sqrt(3) + 3/2)],
         [x == -1/2*I*sqrt(3) - 1/2, y == sqrt(-1/2*I*sqrt(3) + 3/2)],
         [x == 1/2*I*sqrt(3) - 1/2, y == -sqrt(1/2*I*sqrt(3) + 3/2)],
         [x == 1/2*I*sqrt(3) - 1/2, y == sqrt(1/2*I*sqrt(3) + 3/2)],
         [x == 0, y == -1],
         [x == 0, y == 1]]
        sage: solve([sqrt(x) + sqrt(y) == 5, x + y == 10], x, y)
        [[x == -5/2*I*sqrt(5) + 5, y == 5/2*I*sqrt(5) + 5], [x == 5/2*I*sqrt(5) + 5, y == -5/2*I*sqrt(5) + 5]]
        sage: solutions=solve([x^2+y^2 == 1, y^2 == x^3 + x + 1], x, y, solution_dict=True)
        sage: for solution in solutions: print("{} , {}".format(solution[x].n(digits=3), solution[y].n(digits=3)))
        -0.500 - 0.866*I , -1.27 + 0.341*I
        -0.500 - 0.866*I , 1.27 - 0.341*I
        -0.500 + 0.866*I , -1.27 - 0.341*I
        -0.500 + 0.866*I , 1.27 + 0.341*I
        0.000 , -1.00
        0.000 , 1.00

    Whenever possible, answers will be symbolic, but with systems of
    equations, at times approximations will be given, due to the
    underlying algorithm in Maxima::

        sage: sols = solve([x^3==y,y^2==x],[x,y]); sols[-1], sols[0]
        ([x == 0, y == 0], [x == (0.3090169943749475 + 0.9510565162951535*I), y == (-0.8090169943749475 - 0.5877852522924731*I)])
        sage: sols[0][0].rhs().pyobject().parent()
        Complex Double Field

    If ``f`` is only one equation or expression, we use the solve method
    for symbolic expressions, which defaults to exact answers only::

        sage: solve([y^6==y],y)
        [y == 1/4*sqrt(5) + 1/4*I*sqrt(2*sqrt(5) + 10) - 1/4, y == -1/4*sqrt(5) + 1/4*I*sqrt(-2*sqrt(5) + 10) - 1/4, y == -1/4*sqrt(5) - 1/4*I*sqrt(-2*sqrt(5) + 10) - 1/4, y == 1/4*sqrt(5) - 1/4*I*sqrt(2*sqrt(5) + 10) - 1/4, y == 1, y == 0]
        sage: solve( [y^6 == y], y)==solve( y^6 == y, y)
        True

    Here we demonstrate very basic use of the optional keywords for
    a single expression to be solved::

        sage: ((x^2-1)^2).solve(x)
        [x == -1, x == 1]
        sage: ((x^2-1)^2).solve(x,multiplicities=True)
        ([x == -1, x == 1], [2, 2])
        sage: solve(sin(x)==x,x)
        [x == sin(x)]
        sage: solve(sin(x)==x,x,explicit_solutions=True)
        []
        sage: solve(abs(1-abs(1-x)) == 10, x)
        [abs(abs(x - 1) - 1) == 10]
        sage: solve(abs(1-abs(1-x)) == 10, x, to_poly_solve=True)
        [x == -10, x == 12]

    .. note::

        For more details about solving a single equation, see
        the documentation for the single-expression
        :meth:`~sage.symbolic.expression.Expression.solve`.

    ::

        sage: from sage.symbolic.expression import Expression
        sage: Expression.solve(x^2==1,x)
        [x == -1, x == 1]

    We must solve with respect to actual variables::

        sage: z = 5
        sage: solve([8*z + y == 3, -z +7*y == 0],y,z)
        Traceback (most recent call last):
        ...
        TypeError: 5 is not a valid variable.

    If we ask for dictionaries containing the solutions, we get them::

        sage: solve([x^2-1],x,solution_dict=True)
        [{x: -1}, {x: 1}]
        sage: solve([x^2-4*x+4],x,solution_dict=True)
        [{x: 2}]
        sage: res = solve([x^2 == y, y == 4],x,y,solution_dict=True)
        sage: for soln in res: print("x: %s, y: %s" % (soln[x], soln[y]))
        x: 2, y: 4
        x: -2, y: 4

    If there is a parameter in the answer, that will show up as
    a new variable.  In the following example, ``r1`` is a real free
    variable (because of the ``r``)::

        sage: solve([x+y == 3, 2*x+2*y == 6],x,y)
        [[x == -r1 + 3, y == r1]]

    Especially with trigonometric functions, the dummy variable may
    be implicitly an integer (hence the ``z``)::

        sage: solve([cos(x)*sin(x) == 1/2, x+y == 0],x,y)
        [[x == 1/4*pi + pi*z79, y == -1/4*pi - pi*z79]]

    Expressions which are not equations are assumed to be set equal
    to zero, as with `x` in the following example::

        sage: solve([x, y == 2],x,y)
        [[x == 0, y == 2]]

    If ``True`` appears in the list of equations it is
    ignored, and if ``False`` appears in the list then no
    solutions are returned. E.g., note that the first
    ``3==3`` evaluates to ``True``, not to a
    symbolic equation.

    ::

        sage: solve([3==3, 1.00000000000000*x^3 == 0], x)
        [x == 0]
        sage: solve([1.00000000000000*x^3 == 0], x)
        [x == 0]

    Here, the first equation evaluates to ``False``, so
    there are no solutions::

        sage: solve([1==3, 1.00000000000000*x^3 == 0], x)
        []

    Completely symbolic solutions are supported::

        sage: var('s,j,b,m,g')
        (s, j, b, m, g)
        sage: sys = [ m*(1-s) - b*s*j, b*s*j-g*j ];
        sage: solve(sys,s,j)
        [[s == 1, j == 0], [s == g/b, j == (b - g)*m/(b*g)]]
        sage: solve(sys,(s,j))
        [[s == 1, j == 0], [s == g/b, j == (b - g)*m/(b*g)]]
        sage: solve(sys,[s,j])
        [[s == 1, j == 0], [s == g/b, j == (b - g)*m/(b*g)]]

    Inequalities can be also solved::

        sage: solve(x^2>8,x)
        [[x < -2*sqrt(2)], [x > 2*sqrt(2)]]

    We use ``use_grobner`` in Maxima if no solution is obtained from
    Maxima's ``to_poly_solve``::

       sage: x,y=var('x y'); c1(x,y)=(x-5)^2+y^2-16; c2(x,y)=(y-3)^2+x^2-9
       sage: solve([c1(x,y),c2(x,y)],[x,y])
       [[x == -9/68*sqrt(55) + 135/68, y == -15/68*sqrt(55) + 123/68], [x == 9/68*sqrt(55) + 135/68, y == 15/68*sqrt(55) + 123/68]]

    TESTS::

        sage: solve([sin(x)==x,y^2==x],x,y)
        [sin(x) == x, y^2 == x]
        sage: solve(0==1,x)
        Traceback (most recent call last):
        ...
        TypeError:  The first argument must be a symbolic expression or a list of symbolic expressions.

    Test if the empty list is returned, too, when (a list of)
    dictionaries (is) are requested (:trac:`8553`)::

        sage: solve([SR(0)==1],x)
        []
        sage: solve([SR(0)==1],x,solution_dict=True)
        []
        sage: solve([x==1,x==-1],x)
        []
        sage: solve([x==1,x==-1],x,solution_dict=True)
        []
        sage: solve((x==1,x==-1),x,solution_dict=0)
        []

    Relaxed form, suggested by Mike Hansen (:trac:`8553`)::

        sage: solve([x^2-1],x,solution_dict=-1)
        [{x: -1}, {x: 1}]
        sage: solve([x^2-1],x,solution_dict=1)
        [{x: -1}, {x: 1}]
        sage: solve((x==1,x==-1),x,solution_dict=-1)
        []
        sage: solve((x==1,x==-1),x,solution_dict=1)
        []

    This inequality holds for any real ``x`` (:trac:`8078`)::

        sage: solve(x^4+2>0,x)
        [x < +Infinity]

    Test for user friendly input handling :trac:`13645`::

        sage: poly.<a,b> = PolynomialRing(RR)
        sage: solve([a+b+a*b == 1], a)
        Traceback (most recent call last):
        ...
        TypeError: The first argument to solve() should be a symbolic expression or a list of symbolic expressions, cannot handle <... 'bool'>
        sage: solve([a, b], (1, a))
        Traceback (most recent call last):
        ...
        TypeError: 1 is not a valid variable.
        sage: solve([x == 1], (1, a))
        Traceback (most recent call last):
        ...
        TypeError: (1, a) are not valid variables.

    Test that the original version of a system in the French Sage book
    now works (:trac:`14306`)::

        sage: var('y,z')
        (y, z)
        sage: solve([x^2 * y * z == 18, x * y^3 * z == 24, x * y * z^4 == 6], x, y, z)
        [[x == 3, y == 2, z == 1], [x == (1.337215067... - 2.685489874...*I), y == (-1.700434271... + 1.052864325...*I), z == (0.9324722294... - 0.3612416661...*I)], ...]
    """
    from sage.symbolic.expression import is_Expression
    if is_Expression(f):  # f is a single expression
        ans = f.solve(*args, **kwds)
        return ans

    if not isinstance(f, (list, tuple)):
        raise TypeError(
            "The first argument must be a symbolic expression or a list of symbolic expressions."
        )

    if len(f) == 1:
        # f is a list with a single element
        if is_Expression(f[0]):
            # if its a symbolic expression call solve method of this expression
            return f[0].solve(*args, **kwds)
        # otherwise complain
        raise TypeError("The first argument to solve() should be a symbolic "
                        "expression or a list of symbolic expressions, "
                        "cannot handle %s" % repr(type(f[0])))

    # f is a list of such expressions or equations
    from sage.symbolic.ring import is_SymbolicVariable

    if len(args) == 0:
        raise TypeError("Please input variables to solve for.")
    if is_SymbolicVariable(args[0]):
        variables = args
    else:
        variables = tuple(args[0])

    for v in variables:
        if not is_SymbolicVariable(v):
            raise TypeError("%s is not a valid variable." % repr(v))

    try:
        f = [s for s in f if s is not True]
    except TypeError:
        raise ValueError("Unable to solve %s for %s" % (f, args))

    if any(s is False for s in f):
        return []

    from sage.calculus.calculus import maxima
    m = maxima(f)

    try:
        s = m.solve(variables)
    except Exception:  # if Maxima gave an error, try its to_poly_solve
        try:
            s = m.to_poly_solve(variables)
        except TypeError as mess:  # if that gives an error, raise an error.
            if "Error executing code in Maxima" in str(mess):
                raise ValueError(
                    "Sage is unable to determine whether the system %s can be solved for %s"
                    % (f, args))
            else:
                raise

    if len(
            s
    ) == 0:  # if Maxima's solve gave no solutions, try its to_poly_solve
        try:
            s = m.to_poly_solve(variables)
        except Exception:  # if that gives an error, stick with no solutions
            s = []

    if len(s) == 0:  # if to_poly_solve gave no solutions, try use_grobner
        try:
            s = m.to_poly_solve(variables, 'use_grobner=true')
        except Exception:  # if that gives an error, stick with no solutions
            s = []

    sol_list = string_to_list_of_solutions(repr(s))

    # Relaxed form suggested by Mike Hansen (#8553):
    if kwds.get('solution_dict', False):
        if len(sol_list
               ) == 0:  # fixes IndexError on empty solution list (#8553)
            return []
        if isinstance(sol_list[0], list):
            sol_dict = [
                dict([[eq.left(), eq.right()] for eq in solution])
                for solution in sol_list
            ]
        else:
            sol_dict = [{eq.left(): eq.right()} for eq in sol_list]

        return sol_dict
    else:
        return sol_list
Example #14
0
    def _eval_(self, n, x):
        """
        The :meth:`_eval_()` method decides which evaluation suits best
        for the given input, and returns a proper value.

        EXAMPLES::

            sage: var('n,x')
            (n, x)
            sage: chebyshev_T(5,x)
            16*x^5 - 20*x^3 + 5*x
            sage: chebyshev_T(64, x)
            2*(2*(2*(2*(2*(2*x^2 - 1)^2 - 1)^2 - 1)^2 - 1)^2 - 1)^2 - 1
            sage: chebyshev_T(n,-1)
            (-1)^n
            sage: chebyshev_T(-7,x)
            64*x^7 - 112*x^5 + 56*x^3 - 7*x
            sage: chebyshev_T(3/2,x)
            chebyshev_T(3/2, x)
            sage: R.<t> = QQ[]
            sage: chebyshev_T(2,t)
            2*t^2 - 1
            sage: chebyshev_U(2,t)
            4*t^2 - 1
            sage: parent(chebyshev_T(4, RIF(5)))
            Real Interval Field with 53 bits of precision
            sage: RR2 = RealField(5)
            sage: chebyshev_T(100000,RR2(2))
            8.9e57180
            sage: chebyshev_T(5,Qp(3)(2))
            2 + 3^2 + 3^3 + 3^4 + 3^5 + O(3^20)
            sage: chebyshev_T(100001/2, 2)
            doctest:...: RuntimeWarning: mpmath failed, keeping expression unevaluated
            chebyshev_T(100001/2, 2)
            sage: chebyshev_U._eval_(1.5, Mod(8,9)) is None
            True
        """
        # n is an integer => evaluate algebraically (as polynomial)
        if n in ZZ:
            n = ZZ(n)
            # Expanded symbolic expression only for small values of n
            if is_Expression(x) and n.abs() < 32:
                return self.eval_formula(n, x)
            return self.eval_algebraic(n, x)

        if is_Expression(x) or is_Expression(n):
            # Check for known identities
            try:
                return self._eval_special_values_(n, x)
            except ValueError:
                # Don't evaluate => keep symbolic
                return None

        # n is not an integer and neither n nor x is symbolic.
        # We assume n and x are real/complex and evaluate numerically
        try:
            import sage.libs.mpmath.all as mpmath

            return self._evalf_(n, x)
        except mpmath.NoConvergence:
            warnings.warn("mpmath failed, keeping expression unevaluated", RuntimeWarning)
            return None
        except Exception:
            # Numerical evaluation failed => keep symbolic
            return None
Example #15
0
def solve(f, *args, **kwds):
    r"""
    Algebraically solve an equation or system of equations (over the
    complex numbers) for given variables. Inequalities and systems 
    of inequalities are also supported.    
    
    INPUT:
    
    -  ``f`` - equation or system of equations (given by a
       list or tuple)
    
    -  ``*args`` - variables to solve for.
    
    -  ``solution_dict`` - bool (default: False); if True or non-zero, 
       return a list of dictionaries containing the solutions. If there
       are no solutions, return an empty list (rather than a list containing
       an empty dictionary). Likewise, if there's only a single solution,
       return a list containing one dictionary with that solution.
    
    EXAMPLES::
    
        sage: x, y = var('x, y')
        sage: solve([x+y==6, x-y==4], x, y)
        [[x == 5, y == 1]]
        sage: solve([x^2+y^2 == 1, y^2 == x^3 + x + 1], x, y)
        [[x == -1/2*I*sqrt(3) - 1/2, y == -1/2*sqrt(-I*sqrt(3) + 3)*sqrt(2)],
         [x == -1/2*I*sqrt(3) - 1/2, y == 1/2*sqrt(-I*sqrt(3) + 3)*sqrt(2)],
         [x == 1/2*I*sqrt(3) - 1/2, y == -1/2*sqrt(I*sqrt(3) + 3)*sqrt(2)],
         [x == 1/2*I*sqrt(3) - 1/2, y == 1/2*sqrt(I*sqrt(3) + 3)*sqrt(2)],
         [x == 0, y == -1],
         [x == 0, y == 1]]
        sage: solve([sqrt(x) + sqrt(y) == 5, x + y == 10], x, y)
        [[x == -5/2*I*sqrt(5) + 5, y == 5/2*I*sqrt(5) + 5], [x == 5/2*I*sqrt(5) + 5, y == -5/2*I*sqrt(5) + 5]]
        sage: solutions=solve([x^2+y^2 == 1, y^2 == x^3 + x + 1], x, y, solution_dict=True)
        sage: for solution in solutions: print solution[x].n(digits=3), ",", solution[y].n(digits=3)
        -0.500 - 0.866*I , -1.27 + 0.341*I
        -0.500 - 0.866*I , 1.27 - 0.341*I
        -0.500 + 0.866*I , -1.27 - 0.341*I
        -0.500 + 0.866*I , 1.27 + 0.341*I
        0.000 , -1.00
        0.000 , 1.00

    Whenever possible, answers will be symbolic, but with systems of 
    equations, at times approximations will be given, due to the 
    underlying algorithm in Maxima::

        sage: sols = solve([x^3==y,y^2==x],[x,y]); sols[-1], sols[0]
        ([x == 0, y == 0], [x == (0.309016994375 + 0.951056516295*I),  y == (-0.809016994375 - 0.587785252292*I)])
        sage: sols[0][0].rhs().pyobject().parent()
        Complex Double Field

    If ``f`` is only one equation or expression, we use the solve method
    for symbolic expressions, which defaults to exact answers only::

        sage: solve([y^6==y],y)
        [y == e^(2/5*I*pi), y == e^(4/5*I*pi), y == e^(-4/5*I*pi), y == e^(-2/5*I*pi), y == 1, y == 0]
        sage: solve( [y^6 == y], y)==solve( y^6 == y, y)
        True

    .. note::

        For more details about solving a single equations, see
        the documentation for its solve.

    ::

        sage: from sage.symbolic.expression import Expression
        sage: Expression.solve(x^2==1,x)
        [x == -1, x == 1]

    We must solve with respect to actual variables::

        sage: z = 5
        sage: solve([8*z + y == 3, -z +7*y == 0],y,z)
        Traceback (most recent call last):
        ...
        TypeError: 5 is not a valid variable.

    If we ask for dictionaries containing the solutions, we get them::
    
        sage: solve([x^2-1],x,solution_dict=True)
        [{x: -1}, {x: 1}]
        sage: solve([x^2-4*x+4],x,solution_dict=True)
        [{x: 2}]
        sage: res = solve([x^2 == y, y == 4],x,y,solution_dict=True)
        sage: for soln in res: print "x: %s, y: %s"%(soln[x], soln[y])
        x: 2, y: 4
        x: -2, y: 4

    If there is a parameter in the answer, that will show up as 
    a new variable.  In the following example, ``r1`` is a real free
    variable (because of the ``r``)::

        sage: solve([x+y == 3, 2*x+2*y == 6],x,y)
        [[x == -r1 + 3, y == r1]]

    Especially with trigonometric functions, the dummy variable may
    be implicitly an integer (hence the ``z``)::

        sage: solve([cos(x)*sin(x) == 1/2, x+y == 0],x,y)
        [[x == 1/4*pi + pi*z68, y == -1/4*pi - pi*z68]]

    Expressions which are not equations are assumed to be set equal
    to zero, as with `x` in the following example::

        sage: solve([x, y == 2],x,y)
        [[x == 0, y == 2]]

    If ``True`` appears in the list of equations it is
    ignored, and if ``False`` appears in the list then no
    solutions are returned. E.g., note that the first
    ``3==3`` evaluates to ``True``, not to a
    symbolic equation.
    
    ::
    
        sage: solve([3==3, 1.00000000000000*x^3 == 0], x)
        [x == 0]
        sage: solve([1.00000000000000*x^3 == 0], x)
        [x == 0]
    
    Here, the first equation evaluates to ``False``, so
    there are no solutions::
    
        sage: solve([1==3, 1.00000000000000*x^3 == 0], x)
        []
    
    Completely symbolic solutions are supported::
    
        sage: var('s,j,b,m,g')
        (s, j, b, m, g)
        sage: sys = [ m*(1-s) - b*s*j, b*s*j-g*j ];
        sage: solve(sys,s,j)
        [[s == 1, j == 0], [s == g/b, j == (b - g)*m/(b*g)]]
        sage: solve(sys,(s,j))
        [[s == 1, j == 0], [s == g/b, j == (b - g)*m/(b*g)]]
        sage: solve(sys,[s,j])
        [[s == 1, j == 0], [s == g/b, j == (b - g)*m/(b*g)]]

    Inequalities can be also solved::

        sage: solve(x^2>8,x)
        [[x < -2*sqrt(2)], [x > 2*sqrt(2)]]

    Use use_grobner if no solution is obtained from to_poly_solve::

       sage: x,y=var('x y'); c1(x,y)=(x-5)^2+y^2-16; c2(x,y)=(y-3)^2+x^2-9
       sage: solve([c1(x,y),c2(x,y)],[x,y])                               
       [[x == -9/68*sqrt(55) + 135/68, y == -15/68*sqrt(5)*sqrt(11) + 123/68], [x == 9/68*sqrt(55) + 135/68, y == 15/68*sqrt(5)*sqrt(11) + 123/68]]
        
    TESTS::

        sage: solve([sin(x)==x,y^2==x],x,y)
        [sin(x) == x, y^2 == x]
        sage: solve(0==1,x)
        Traceback (most recent call last):
        ...
        TypeError:  The first argument must be a symbolic expression or a list of symbolic expressions.

    Test if the empty list is returned, too, when (a list of)
    dictionaries (is) are requested (#8553)::

        sage: solve([0==1],x)
        []
        sage: solve([0==1],x,solution_dict=True)
        []
        sage: solve([x==1,x==-1],x)
        []
        sage: solve([x==1,x==-1],x,solution_dict=True)
        []
        sage: solve((x==1,x==-1),x,solution_dict=0)
        []

    Relaxed form, suggested by Mike Hansen (#8553)::

        sage: solve([x^2-1],x,solution_dict=-1)
        [{x: -1}, {x: 1}]
        sage: solve([x^2-1],x,solution_dict=1)
        [{x: -1}, {x: 1}]
        sage: solve((x==1,x==-1),x,solution_dict=-1)
        []
        sage: solve((x==1,x==-1),x,solution_dict=1)
        []

    This inequality holds for any real ``x`` (trac #8078)::

        sage: solve(x^4+2>0,x)
        [x < +Infinity]

    """
    from sage.symbolic.expression import is_Expression
    if is_Expression(f):  # f is a single expression
        ans = f.solve(*args, **kwds)
        return ans

    if not isinstance(f, (list, tuple)):
        raise TypeError(
            "The first argument must be a symbolic expression or a list of symbolic expressions."
        )

    if len(f) == 1 and is_Expression(f[0]):
        # f is a list with a single expression
        return f[0].solve(*args, **kwds)

    # f is a list of such expressions or equations
    from sage.symbolic.ring import is_SymbolicVariable

    if len(args) == 0:
        raise TypeError, "Please input variables to solve for."
    if is_SymbolicVariable(args[0]):
        variables = args
    else:
        variables = tuple(args[0])

    for v in variables:
        if not is_SymbolicVariable(v):
            raise TypeError, "%s is not a valid variable." % v

    try:
        f = [s for s in f if s is not True]
    except TypeError:
        raise ValueError, "Unable to solve %s for %s" % (f, args)

    if any(s is False for s in f):
        return []

    from sage.calculus.calculus import maxima
    m = maxima(f)

    try:
        s = m.solve(variables)
    except:  # if Maxima gave an error, try its to_poly_solve
        try:
            s = m.to_poly_solve(variables)
        except TypeError, mess:  # if that gives an error, raise an error.
            if "Error executing code in Maxima" in str(mess):
                raise ValueError, "Sage is unable to determine whether the system %s can be solved for %s" % (
                    f, args)
            else:
                raise
Example #16
0
def implicit_plot(f, xrange, yrange, **options):
    r"""
    ``implicit_plot`` takes a function of two variables, `f(x,y)`
    and plots the curve `f(x,y) = 0` over the specified 
    ``xrange`` and ``yrange`` as demonstrated below.

    ``implicit_plot(f, (xmin, xmax), (ymin, ymax), ...)``

    ``implicit_plot(f, (x, xmin, xmax), (y, ymin, ymax), ...)``

    INPUT:

    - ``f`` -- a function of two variables or equation in two variables

    - ``(xmin, xmax)`` -- 2-tuple, the range of ``x`` values or ``(x,xmin,xmax)``

    - ``(ymin, ymax)`` -- 2-tuple, the range of ``y`` values or ``(y,ymin,ymax)``

    The following inputs must all be passed in as named parameters:

    - ``plot_points`` -- integer (default: 150); number of points to plot
      in each direction of the grid

    - ``fill`` -- boolean (default: ``False``); if ``True``, fill the region
      `f(x,y) < 0`.
        
    - ``linewidth`` -- integer (default: None), if a single integer all levels 
      will be of the width given, otherwise the levels will be plotted with the 
      widths in the order given.

    - ``linestyle`` -- string (default: None), the style of the line to be 
      plotted, one of: solid, dashed, dashdot or dotted.
      
    - ``color`` -- string (default: ``blue``), the color of the plot. Colors are
      defined in :mod:`sage.plot.colors`; try ``colors?`` to see them all.

    - ``legend_label`` -- the label for this item in the legend

    EXAMPLES:

    A simple circle with a radius of 2. Note that
    since the input function is an expression, we need to explicitly
    declare the variables in 3-tuples for the range::

        sage: var("x y")
        (x, y)
        sage: implicit_plot(x^2+y^2-2, (x,-3,3), (y,-3,3))

    I can do the same thing, but using a callable function so I don't need
    to explicitly define the variables in the ranges, and filling the inside::

        sage: f(x,y) = x^2 + y^2 - 2
        sage: implicit_plot(f, (-3, 3), (-3, 3),fill=True)

    The same circle but with a different line width::

        sage: implicit_plot(f, (-3,3), (-3,3), linewidth=6)

    And again the same circle but this time with a dashdot border::

        sage: implicit_plot(f, (-3,3), (-3,3), linestyle='dashdot')

    You can also plot an equation::

        sage: var("x y")
        (x, y)
        sage: implicit_plot(x^2+y^2 == 2, (x,-3,3), (y,-3,3))
        
    You can even change the color of the plot::
    
        sage: implicit_plot(x^2+y^2 == 2, (x,-3,3), (y,-3,3), color="red")

    Here is a beautiful (and long) example which also tests that all 
    colors work with this::

        sage: G = Graphics()
        sage: counter = 0
        sage: for col in colors.keys(): # long time
        ...       G += implicit_plot(x^2+y^2==1+counter*.1, (x,-4,4),(y,-4,4),color=col)
        ...       counter += 1
        sage: G.show(frame=False)

    We can define a level-`n` approximation of the boundary of the 
    Mandelbrot set::

        sage: def mandel(n):
        ...       c = polygen(CDF, 'c')
        ...       z = 0
        ...       for i in range(n):
        ...           z = z*z + c
        ...       def f(x, y):
        ...           val = z(CDF(x, y))
        ...           return val.norm() - 4
        ...       return f

    The first-level approximation is just a circle::

        sage: implicit_plot(mandel(1), (-3, 3), (-3, 3))
        
    A third-level approximation starts to get interesting::

        sage: implicit_plot(mandel(3), (-2, 1), (-1.5, 1.5))

    The seventh-level approximation is a degree 64 polynomial, and 
    ``implicit_plot`` does a pretty good job on this part of the curve.
    (``plot_points=200`` looks even better, but it takes over a second.)

    ::

        sage: implicit_plot(mandel(7), (-0.3, 0.05), (-1.15, -0.9),plot_points=50)

    When making a filled implicit plot using a python function rather than a
    symbolic expression the user should increase the number of plot points to
    avoid artifacts::

        sage: implicit_plot(lambda x,y: x^2+y^2-2, (x,-3,3), (y,-3,3), fill=True, plot_points=500) # long time

    TESTS::

        sage: f(x,y) = x^2 + y^2 - 2
        sage: implicit_plot(f, (-3, 3), (-3, 3),fill=5)
        Traceback (most recent call last):
        ...
        ValueError: fill=5 is not supported
    """
    from sage.symbolic.expression import is_SymbolicEquation
    if is_SymbolicEquation(f):
        if f.operator() != operator.eq:
            raise ValueError, "input to implicit plot must be function or equation"
        f = f.lhs() - f.rhs()
    linewidths = options.pop('linewidth', None)
    linestyles = options.pop('linestyle', None)

    if 'color' in options:
        options['cmap']=[options.pop('color', None)]

    if options['fill'] is True:
        options.pop('fill')
        options.pop('contours',None)
        options.pop('cmap',None)
        from sage.symbolic.expression import is_Expression
        if not is_Expression(f):
            return region_plot(lambda x,y: f(x,y)<0, xrange, yrange,
                               borderwidth=linewidths, borderstyle=linestyles,
                               **options)
        else:
            return region_plot(f<0, xrange, yrange, borderwidth=linewidths,
                               borderstyle=linestyles, **options)
    elif options['fill'] is False:
        return contour_plot(f, xrange, yrange, linewidths=linewidths,
                            linestyles=linestyles, **options)
    else:
        raise ValueError("fill=%s is not supported" % options['fill'])
Example #17
0
def region_plot(f, xrange, yrange, plot_points, incol, outcol, bordercol, borderstyle, borderwidth, alpha, **options):
    r"""
    ``region_plot`` takes a boolean function of two variables, `f(x,y)`
    and plots the region where f is True over the specified
    ``xrange`` and ``yrange`` as demonstrated below.

    ``region_plot(f, (xmin, xmax), (ymin, ymax), ...)``

    INPUT:

    - ``f`` -- a boolean function or a list of boolean functions of two variables

    - ``(xmin, xmax)`` -- 2-tuple, the range of ``x`` values OR 3-tuple
      ``(x,xmin,xmax)``

    - ``(ymin, ymax)`` -- 2-tuple, the range of ``y`` values OR 3-tuple
      ``(y,ymin,ymax)``

    - ``plot_points``  -- integer (default: 100); number of points to plot
      in each direction of the grid

    - ``incol`` -- a color (default: ``'blue'``), the color inside the region

    - ``outcol`` -- a color (default: ``None``), the color of the outside
      of the region

    If any of these options are specified, the border will be shown as indicated,
    otherwise it is only implicit (with color ``incol``) as the border of the
    inside of the region.

     - ``bordercol`` -- a color (default: ``None``), the color of the border
       (``'black'`` if ``borderwidth`` or ``borderstyle`` is specified but not ``bordercol``)

    - ``borderstyle``  -- string (default: 'solid'), one of ``'solid'``,
      ``'dashed'``, ``'dotted'``, ``'dashdot'``, respectively ``'-'``,
      ``'--'``, ``':'``, ``'-.'``.

    - ``borderwidth``  -- integer (default: None), the width of the border in pixels

    - ``alpha`` -- (default: 1) How transparent the fill is. A number between 0 and 1.

    - ``legend_label`` -- the label for this item in the legend

    - ``base`` - (default: 10) the base of the logarithm if
      a logarithmic scale is set. This must be greater than 1. The base
      can be also given as a list or tuple ``(basex, basey)``.
      ``basex`` sets the base of the logarithm along the horizontal
      axis and ``basey`` sets the base along the vertical axis.

    - ``scale`` -- (default: ``"linear"``) string. The scale of the axes.
      Possible values are ``"linear"``, ``"loglog"``, ``"semilogx"``,
      ``"semilogy"``.

      The scale can be also be given as single argument that is a list
      or tuple ``(scale, base)`` or ``(scale, basex, basey)``.

      The ``"loglog"`` scale sets both the horizontal and vertical axes to
      logarithmic scale. The ``"semilogx"`` scale sets the horizontal axis
      to logarithmic scale. The ``"semilogy"`` scale sets the vertical axis
      to logarithmic scale. The ``"linear"`` scale is the default value
      when :class:`~sage.plot.graphics.Graphics` is initialized.


    EXAMPLES:

    Here we plot a simple function of two variables::

        sage: x,y = var('x,y')
        sage: region_plot(cos(x^2+y^2) <= 0, (x, -3, 3), (y, -3, 3))
        Graphics object consisting of 1 graphics primitive

    Here we play with the colors::

        sage: region_plot(x^2+y^3 < 2, (x, -2, 2), (y, -2, 2), incol='lightblue', bordercol='gray')
        Graphics object consisting of 2 graphics primitives

    An even more complicated plot, with dashed borders::

        sage: region_plot(sin(x)*sin(y) >= 1/4, (x,-10,10), (y,-10,10), incol='yellow', bordercol='black', borderstyle='dashed', plot_points=250)
        Graphics object consisting of 2 graphics primitives

    A disk centered at the origin::

        sage: region_plot(x^2+y^2<1, (x,-1,1), (y,-1,1))
        Graphics object consisting of 1 graphics primitive

    A plot with more than one condition (all conditions must be true for the statement to be true)::

        sage: region_plot([x^2+y^2<1, x<y], (x,-2,2), (y,-2,2))
        Graphics object consisting of 1 graphics primitive

    Since it doesn't look very good, let's increase ``plot_points``::

        sage: region_plot([x^2+y^2<1, x<y], (x,-2,2), (y,-2,2), plot_points=400)
        Graphics object consisting of 1 graphics primitive

    To get plots where only one condition needs to be true, use a function.
    Using lambda functions, we definitely need the extra ``plot_points``::

        sage: region_plot(lambda x,y: x^2+y^2<1 or x<y, (x,-2,2), (y,-2,2), plot_points=400)
        Graphics object consisting of 1 graphics primitive

    The first quadrant of the unit circle::

        sage: region_plot([y>0, x>0, x^2+y^2<1], (x,-1.1, 1.1), (y,-1.1, 1.1), plot_points = 400)
        Graphics object consisting of 1 graphics primitive

    Here is another plot, with a huge border::

        sage: region_plot(x*(x-1)*(x+1)+y^2<0, (x, -3, 2), (y, -3, 3), incol='lightblue', bordercol='gray', borderwidth=10, plot_points=50)
        Graphics object consisting of 2 graphics primitives

    If we want to keep only the region where x is positive::

        sage: region_plot([x*(x-1)*(x+1)+y^2<0, x>-1], (x, -3, 2), (y, -3, 3), incol='lightblue', plot_points=50)
        Graphics object consisting of 1 graphics primitive

    Here we have a cut circle::

        sage: region_plot([x^2+y^2<4, x>-1], (x, -2, 2), (y, -2, 2), incol='lightblue', bordercol='gray', plot_points=200)
        Graphics object consisting of 2 graphics primitives

    The first variable range corresponds to the horizontal axis and
    the second variable range corresponds to the vertical axis::

        sage: s,t=var('s,t')
        sage: region_plot(s>0,(t,-2,2),(s,-2,2))
        Graphics object consisting of 1 graphics primitive

    ::

        sage: region_plot(s>0,(s,-2,2),(t,-2,2))
        Graphics object consisting of 1 graphics primitive

    An example of a region plot in 'loglog' scale::

        sage: region_plot(x^2+y^2<100, (x,1,10), (y,1,10), scale='loglog')
        Graphics object consisting of 1 graphics primitive

    TESTS:

    To check that :trac:`16907` is fixed::

        sage: x, y = var('x, y')
        sage: disc1 = region_plot(x^2+y^2 < 1, (x, -1, 1), (y, -1, 1), alpha=0.5)
        sage: disc2 = region_plot((x-0.7)^2+(y-0.7)^2 < 0.5, (x, -2, 2), (y, -2, 2), incol='red', alpha=0.5)
        sage: disc1 + disc2
        Graphics object consisting of 2 graphics primitives

    To check that :trac:`18286` is fixed::
        sage: x, y = var('x, y')
        sage: region_plot([x == 0], (x, -1, 1), (y, -1, 1))
        Graphics object consisting of 1 graphics primitive
        sage: region_plot([x^2+y^2==1, x<y], (x, -1, 1), (y, -1, 1))
        Graphics object consisting of 1 graphics primitive

    """

    from sage.plot.all import Graphics
    from sage.plot.misc import setup_for_eval_on_grid
    from sage.symbolic.expression import is_Expression
    from warnings import warn
    import numpy

    if not isinstance(f, (list, tuple)):
        f = [f]

    feqs = [equify(g) for g in f if is_Expression(g) and g.operator() is operator.eq and not equify(g).is_zero()]
    f = [equify(g) for g in f if not (is_Expression(g) and g.operator() is operator.eq)]
    neqs = len(feqs)
    if neqs > 1:
        warn("There are at least 2 equations; If the region is degenerated to points, plotting might show nothing.")
        feqs = [sum([fn**2 for fn in feqs])]
        neqs = 1
    if neqs and not bordercol:
        bordercol = incol
    if not f:
        return implicit_plot(feqs[0], xrange, yrange, plot_points=plot_points, fill=False, \
                             linewidth=borderwidth, linestyle=borderstyle, color=bordercol, **options)
    f_all, ranges = setup_for_eval_on_grid(feqs + f, [xrange, yrange], plot_points)
    xrange,yrange=[r[:2] for r in ranges]

    xy_data_arrays = numpy.asarray([[[func(x, y) for x in xsrange(*ranges[0], include_endpoint=True)]
                                     for y in xsrange(*ranges[1], include_endpoint=True)]
                                    for func in f_all[neqs::]],dtype=float)
    xy_data_array=numpy.abs(xy_data_arrays.prod(axis=0))
    # Now we need to set entries to negative iff all
    # functions were negative at that point.
    neg_indices = (xy_data_arrays<0).all(axis=0)
    xy_data_array[neg_indices]=-xy_data_array[neg_indices]

    from matplotlib.colors import ListedColormap
    incol = rgbcolor(incol)
    if outcol:
        outcol = rgbcolor(outcol)
        cmap = ListedColormap([incol, outcol])
        cmap.set_over(outcol, alpha=alpha)
    else:
        outcol = rgbcolor('white')
        cmap = ListedColormap([incol, outcol])
        cmap.set_over(outcol, alpha=0)
    cmap.set_under(incol, alpha=alpha)

    g = Graphics()

    # Reset aspect_ratio to 'automatic' in case scale is 'semilog[xy]'.
    # Otherwise matplotlib complains.
    scale = options.get('scale', None)
    if isinstance(scale, (list, tuple)):
        scale = scale[0]
    if scale == 'semilogy' or scale == 'semilogx':
        options['aspect_ratio'] = 'automatic'

    g._set_extra_kwds(Graphics._extract_kwds_for_show(options, ignore=['xmin', 'xmax']))

    if neqs == 0:
        g.add_primitive(ContourPlot(xy_data_array, xrange,yrange,
                                dict(contours=[-1e-20, 0, 1e-20], cmap=cmap, fill=True, **options)))
    else:
        mask = numpy.asarray([[elt > 0 for elt in rows] for rows in xy_data_array], dtype=bool)
        xy_data_array = numpy.asarray([[f_all[0](x, y) for x in xsrange(*ranges[0], include_endpoint=True)]
                                        for y in xsrange(*ranges[1], include_endpoint=True)], dtype=float)
        xy_data_array[mask] = None
    if bordercol or borderstyle or borderwidth:
        cmap = [rgbcolor(bordercol)] if bordercol else ['black']
        linestyles = [borderstyle] if borderstyle else None
        linewidths = [borderwidth] if borderwidth else None
        g.add_primitive(ContourPlot(xy_data_array, xrange, yrange,
                                    dict(linestyles=linestyles, linewidths=linewidths,
                                         contours=[0], cmap=[bordercol], fill=False, **options)))

    return g
Example #18
0
def solve(f, *args, **kwds):
    r"""
    Algebraically solve an equation or system of equations (over the
    complex numbers) for given variables. Inequalities and systems
    of inequalities are also supported.

    INPUT:

    -  ``f`` - equation or system of equations (given by a
       list or tuple)

    -  ``*args`` - variables to solve for.

    -  ``solution_dict`` - bool (default: False); if True or non-zero,
       return a list of dictionaries containing the solutions. If there
       are no solutions, return an empty list (rather than a list containing
       an empty dictionary). Likewise, if there's only a single solution,
       return a list containing one dictionary with that solution.

    There are a few optional keywords if you are trying to solve a single
    equation.  They may only be used in that context.

    -  ``multiplicities`` - bool (default: False); if True,
       return corresponding multiplicities.  This keyword is
       incompatible with ``to_poly_solve=True`` and does not make
       any sense when solving inequalities.

    -  ``explicit_solutions`` - bool (default: False); require that
       all roots be explicit rather than implicit. Not used
       when solving inequalities.

    -  ``to_poly_solve`` - bool (default: False) or string; use
       Maxima's ``to_poly_solver`` package to search for more possible
       solutions, but possibly encounter approximate solutions.
       This keyword is incompatible with ``multiplicities=True``
       and is not used when solving inequalities. Setting ``to_poly_solve``
       to 'force' (string) omits Maxima's solve command (useful when
       some solutions of trigonometric equations are lost).


    EXAMPLES::

        sage: x, y = var('x, y')
        sage: solve([x+y==6, x-y==4], x, y)
        [[x == 5, y == 1]]
        sage: solve([x^2+y^2 == 1, y^2 == x^3 + x + 1], x, y)
        [[x == -1/2*I*sqrt(3) - 1/2, y == -sqrt(-1/2*I*sqrt(3) + 3/2)],
         [x == -1/2*I*sqrt(3) - 1/2, y == sqrt(-1/2*I*sqrt(3) + 3/2)],
         [x == 1/2*I*sqrt(3) - 1/2, y == -sqrt(1/2*I*sqrt(3) + 3/2)],
         [x == 1/2*I*sqrt(3) - 1/2, y == sqrt(1/2*I*sqrt(3) + 3/2)],
         [x == 0, y == -1],
         [x == 0, y == 1]]
        sage: solve([sqrt(x) + sqrt(y) == 5, x + y == 10], x, y)
        [[x == -5/2*I*sqrt(5) + 5, y == 5/2*I*sqrt(5) + 5], [x == 5/2*I*sqrt(5) + 5, y == -5/2*I*sqrt(5) + 5]]
        sage: solutions=solve([x^2+y^2 == 1, y^2 == x^3 + x + 1], x, y, solution_dict=True)
        sage: for solution in solutions: print("{} , {}".format(solution[x].n(digits=3), solution[y].n(digits=3)))
        -0.500 - 0.866*I , -1.27 + 0.341*I
        -0.500 - 0.866*I , 1.27 - 0.341*I
        -0.500 + 0.866*I , -1.27 - 0.341*I
        -0.500 + 0.866*I , 1.27 + 0.341*I
        0.000 , -1.00
        0.000 , 1.00

    Whenever possible, answers will be symbolic, but with systems of
    equations, at times approximations will be given, due to the
    underlying algorithm in Maxima::

        sage: sols = solve([x^3==y,y^2==x],[x,y]); sols[-1], sols[0]
        ([x == 0, y == 0], [x == (0.3090169943749475 + 0.9510565162951535*I), y == (-0.8090169943749475 - 0.5877852522924731*I)])
        sage: sols[0][0].rhs().pyobject().parent()
        Complex Double Field

    If ``f`` is only one equation or expression, we use the solve method
    for symbolic expressions, which defaults to exact answers only::

        sage: solve([y^6==y],y)
        [y == 1/4*sqrt(5) + 1/4*I*sqrt(2*sqrt(5) + 10) - 1/4, y == -1/4*sqrt(5) + 1/4*I*sqrt(-2*sqrt(5) + 10) - 1/4, y == -1/4*sqrt(5) - 1/4*I*sqrt(-2*sqrt(5) + 10) - 1/4, y == 1/4*sqrt(5) - 1/4*I*sqrt(2*sqrt(5) + 10) - 1/4, y == 1, y == 0]
        sage: solve( [y^6 == y], y)==solve( y^6 == y, y)
        True

    Here we demonstrate very basic use of the optional keywords for
    a single expression to be solved::

        sage: ((x^2-1)^2).solve(x)
        [x == -1, x == 1]
        sage: ((x^2-1)^2).solve(x,multiplicities=True)
        ([x == -1, x == 1], [2, 2])
        sage: solve(sin(x)==x,x)
        [x == sin(x)]
        sage: solve(sin(x)==x,x,explicit_solutions=True)
        []
        sage: solve(abs(1-abs(1-x)) == 10, x)
        [abs(abs(x - 1) - 1) == 10]
        sage: solve(abs(1-abs(1-x)) == 10, x, to_poly_solve=True)
        [x == -10, x == 12]

    .. note::

        For more details about solving a single equation, see
        the documentation for the single-expression
        :meth:`~sage.symbolic.expression.Expression.solve`.

    ::

        sage: from sage.symbolic.expression import Expression
        sage: Expression.solve(x^2==1,x)
        [x == -1, x == 1]

    We must solve with respect to actual variables::

        sage: z = 5
        sage: solve([8*z + y == 3, -z +7*y == 0],y,z)
        Traceback (most recent call last):
        ...
        TypeError: 5 is not a valid variable.

    If we ask for dictionaries containing the solutions, we get them::

        sage: solve([x^2-1],x,solution_dict=True)
        [{x: -1}, {x: 1}]
        sage: solve([x^2-4*x+4],x,solution_dict=True)
        [{x: 2}]
        sage: res = solve([x^2 == y, y == 4],x,y,solution_dict=True)
        sage: for soln in res: print("x: %s, y: %s" % (soln[x], soln[y]))
        x: 2, y: 4
        x: -2, y: 4

    If there is a parameter in the answer, that will show up as
    a new variable.  In the following example, ``r1`` is a real free
    variable (because of the ``r``)::

        sage: solve([x+y == 3, 2*x+2*y == 6],x,y)
        [[x == -r1 + 3, y == r1]]

    Especially with trigonometric functions, the dummy variable may
    be implicitly an integer (hence the ``z``)::

        sage: solve([cos(x)*sin(x) == 1/2, x+y == 0],x,y)
        [[x == 1/4*pi + pi*z79, y == -1/4*pi - pi*z79]]

    Expressions which are not equations are assumed to be set equal
    to zero, as with `x` in the following example::

        sage: solve([x, y == 2],x,y)
        [[x == 0, y == 2]]

    If ``True`` appears in the list of equations it is
    ignored, and if ``False`` appears in the list then no
    solutions are returned. E.g., note that the first
    ``3==3`` evaluates to ``True``, not to a
    symbolic equation.

    ::

        sage: solve([3==3, 1.00000000000000*x^3 == 0], x)
        [x == 0]
        sage: solve([1.00000000000000*x^3 == 0], x)
        [x == 0]

    Here, the first equation evaluates to ``False``, so
    there are no solutions::

        sage: solve([1==3, 1.00000000000000*x^3 == 0], x)
        []

    Completely symbolic solutions are supported::

        sage: var('s,j,b,m,g')
        (s, j, b, m, g)
        sage: sys = [ m*(1-s) - b*s*j, b*s*j-g*j ];
        sage: solve(sys,s,j)
        [[s == 1, j == 0], [s == g/b, j == (b - g)*m/(b*g)]]
        sage: solve(sys,(s,j))
        [[s == 1, j == 0], [s == g/b, j == (b - g)*m/(b*g)]]
        sage: solve(sys,[s,j])
        [[s == 1, j == 0], [s == g/b, j == (b - g)*m/(b*g)]]

    Inequalities can be also solved::

        sage: solve(x^2>8,x)
        [[x < -2*sqrt(2)], [x > 2*sqrt(2)]]

    We use ``use_grobner`` in Maxima if no solution is obtained from
    Maxima's ``to_poly_solve``::

       sage: x,y=var('x y'); c1(x,y)=(x-5)^2+y^2-16; c2(x,y)=(y-3)^2+x^2-9
       sage: solve([c1(x,y),c2(x,y)],[x,y])
       [[x == -9/68*sqrt(55) + 135/68, y == -15/68*sqrt(11)*sqrt(5) + 123/68], [x == 9/68*sqrt(55) + 135/68, y == 15/68*sqrt(11)*sqrt(5) + 123/68]]

    TESTS::

        sage: solve([sin(x)==x,y^2==x],x,y)
        [sin(x) == x, y^2 == x]
        sage: solve(0==1,x)
        Traceback (most recent call last):
        ...
        TypeError:  The first argument must be a symbolic expression or a list of symbolic expressions.

    Test if the empty list is returned, too, when (a list of)
    dictionaries (is) are requested (#8553)::

        sage: solve([SR(0)==1],x)
        []
        sage: solve([SR(0)==1],x,solution_dict=True)
        []
        sage: solve([x==1,x==-1],x)
        []
        sage: solve([x==1,x==-1],x,solution_dict=True)
        []
        sage: solve((x==1,x==-1),x,solution_dict=0)
        []

    Relaxed form, suggested by Mike Hansen (#8553)::

        sage: solve([x^2-1],x,solution_dict=-1)
        [{x: -1}, {x: 1}]
        sage: solve([x^2-1],x,solution_dict=1)
        [{x: -1}, {x: 1}]
        sage: solve((x==1,x==-1),x,solution_dict=-1)
        []
        sage: solve((x==1,x==-1),x,solution_dict=1)
        []

    This inequality holds for any real ``x`` (:trac:`8078`)::

        sage: solve(x^4+2>0,x)
        [x < +Infinity]

    Test for user friendly input handling :trac:`13645`::

        sage: poly.<a,b> = PolynomialRing(RR)
        sage: solve([a+b+a*b == 1], a)
        Traceback (most recent call last):
        ...
        TypeError: The first argument to solve() should be a symbolic expression or a list of symbolic expressions, cannot handle <type 'bool'>
        sage: solve([a, b], (1, a))
        Traceback (most recent call last):
        ...
        TypeError: 1 is not a valid variable.
        sage: solve([x == 1], (1, a))
        Traceback (most recent call last):
        ...
        TypeError: (1, a) are not valid variables.

    Test that the original version of a system in the French Sage book
    now works (:trac:`14306`)::

        sage: var('y,z')
        (y, z)
        sage: solve([x^2 * y * z == 18, x * y^3 * z == 24, x * y * z^4 == 6], x, y, z)
        [[x == 3, y == 2, z == 1], [x == (1.337215067... - 2.685489874...*I), y == (-1.700434271... + 1.052864325...*I), z == (0.9324722294... - 0.3612416661...*I)], ...]
    """
    from sage.symbolic.expression import is_Expression
    if is_Expression(f): # f is a single expression
        ans = f.solve(*args,**kwds)
        return ans

    if not isinstance(f, (list, tuple)):
        raise TypeError("The first argument must be a symbolic expression or a list of symbolic expressions.")

    if len(f)==1:
        # f is a list with a single element
        if is_Expression(f[0]):
            # if its a symbolic expression call solve method of this expression
            return f[0].solve(*args,**kwds)
        # otherwise complain
        raise TypeError("The first argument to solve() should be a symbolic "
                        "expression or a list of symbolic expressions, "
                        "cannot handle %s"%repr(type(f[0])))

    # f is a list of such expressions or equations
    from sage.symbolic.ring import is_SymbolicVariable

    if len(args)==0:
        raise TypeError("Please input variables to solve for.")
    if is_SymbolicVariable(args[0]):
        variables = args
    else:
        variables = tuple(args[0])

    for v in variables:
        if not is_SymbolicVariable(v):
            raise TypeError("%s is not a valid variable."%repr(v))

    try:
        f = [s for s in f if s is not True]
    except TypeError:
        raise ValueError("Unable to solve %s for %s"%(f, args))

    if any(s is False for s in f):
        return []

    from sage.calculus.calculus import maxima
    m = maxima(f)

    try:
        s = m.solve(variables)
    except Exception: # if Maxima gave an error, try its to_poly_solve
        try:
            s = m.to_poly_solve(variables)
        except TypeError as mess: # if that gives an error, raise an error.
            if "Error executing code in Maxima" in str(mess):
                raise ValueError("Sage is unable to determine whether the system %s can be solved for %s"%(f,args))
            else:
                raise

    if len(s)==0: # if Maxima's solve gave no solutions, try its to_poly_solve
        try:
            s = m.to_poly_solve(variables)
        except Exception: # if that gives an error, stick with no solutions
            s = []

    if len(s)==0: # if to_poly_solve gave no solutions, try use_grobner
        try:
            s = m.to_poly_solve(variables,'use_grobner=true')
        except Exception: # if that gives an error, stick with no solutions
            s = []

    sol_list = string_to_list_of_solutions(repr(s))

    # Relaxed form suggested by Mike Hansen (#8553):
    if kwds.get('solution_dict', False):
        if len(sol_list)==0: # fixes IndexError on empty solution list (#8553)
            return []
        if isinstance(sol_list[0], list):
            sol_dict=[dict([[eq.left(),eq.right()] for eq in solution])
                    for solution in sol_list]
        else:
            sol_dict=[{eq.left():eq.right()} for eq in sol_list]

        return sol_dict
    else:
        return sol_list
Example #19
0
def solve_mod(eqns, modulus, solution_dict = False):
    r"""
    Return all solutions to an equation or list of equations modulo the
    given integer modulus. Each equation must involve only polynomials
    in 1 or many variables.

    By default the solutions are returned as `n`-tuples, where `n`
    is the number of variables appearing anywhere in the given
    equations. The variables are in alphabetical order.

    INPUT:


    -  ``eqns`` - equation or list of equations

    -  ``modulus`` - an integer

    -  ``solution_dict`` - bool (default: False); if True or non-zero,
       return a list of dictionaries containing the solutions. If there
       are no solutions, return an empty list (rather than a list containing
       an empty dictionary). Likewise, if there's only a single solution,
       return a list containing one dictionary with that solution.


    EXAMPLES::

        sage: var('x,y')
        (x, y)
        sage: solve_mod([x^2 + 2 == x, x^2 + y == y^2], 14)
        [(4, 2), (4, 6), (4, 9), (4, 13)]
        sage: solve_mod([x^2 == 1, 4*x  == 11], 15)
        [(14,)]

    Fermat's equation modulo 3 with exponent 5::

        sage: var('x,y,z')
        (x, y, z)
        sage: solve_mod([x^5 + y^5 == z^5], 3)
        [(0, 0, 0), (0, 1, 1), (0, 2, 2), (1, 0, 1), (1, 1, 2), (1, 2, 0), (2, 0, 2), (2, 1, 0), (2, 2, 1)]

    We can solve with respect to a bigger modulus if it consists only of small prime factors::

        sage: [d] = solve_mod([5*x + y == 3, 2*x - 3*y == 9], 3*5*7*11*19*23*29, solution_dict = True)
        sage: d[x]
        12915279
        sage: d[y]
        8610183

    For cases where there are relatively few solutions and the prime
    factors are small, this can be efficient even if the modulus itself
    is large::

        sage: sorted(solve_mod([x^2 == 41], 10^20))
        [(4538602480526452429,), (11445932736758703821,), (38554067263241296179,),
        (45461397519473547571,), (54538602480526452429,), (61445932736758703821,),
        (88554067263241296179,), (95461397519473547571,)]

    We solve a simple equation modulo 2::

        sage: x,y = var('x,y')
        sage: solve_mod([x == y], 2)
        [(0, 0), (1, 1)]

    .. warning::

       The current implementation splits the modulus into prime
       powers, then naively enumerates all possible solutions
       (starting modulo primes and then working up through prime
       powers), and finally combines the solution using the Chinese
       Remainder Theorem.  The interface is good, but the algorithm is
       very inefficient if the modulus has some larger prime factors! Sage
       *does* have the ability to do something much faster in certain
       cases at least by using Groebner basis, linear algebra
       techniques, etc. But for a lot of toy problems this function as
       is might be useful. At least it establishes an interface.


    TESTS:

    Make sure that we short-circuit in at least some cases::

        sage: solve_mod([2*x==1], 2*next_prime(10^50))
        []

    Try multi-equation cases::

        sage: x, y, z = var("x y z")
        sage: solve_mod([2*x^2 + x*y, -x*y+2*y^2+x-2*y, -2*x^2+2*x*y-y^2-x-y], 12)
        [(0, 0), (4, 4), (0, 3), (4, 7)]
        sage: eqs = [-y^2+z^2, -x^2+y^2-3*z^2-z-1, -y*z-z^2-x-y+2, -x^2-12*z^2-y+z]
        sage: solve_mod(eqs, 11)
        [(8, 5, 6)]

    Confirm that modulus 1 now behaves as it should::

        sage: x, y = var("x y")
        sage: solve_mod([x==1], 1)
        [(0,)]
        sage: solve_mod([2*x^2+x*y, -x*y+2*y^2+x-2*y, -2*x^2+2*x*y-y^2-x-y], 1)
        [(0, 0)]


    """
    from sage.rings.all import Integer, Integers, crt_basis
    from sage.symbolic.expression import is_Expression
    from sage.misc.all import cartesian_product_iterator
    from sage.modules.all import vector
    from sage.matrix.all import matrix

    if not isinstance(eqns, (list, tuple)):
        eqns = [eqns]
    eqns = [eq if is_Expression(eq) else (eq.lhs()-eq.rhs()) for eq in eqns]
    modulus = Integer(modulus)
    if modulus < 1:
        raise ValueError("the modulus must be a positive integer")
    vars = list(set(sum([list(e.variables()) for e in eqns], [])))
    vars.sort(key=repr)

    if modulus == 1: # degenerate case
        ans = [tuple(Integers(1)(0) for v in vars)]
        return ans

    factors = modulus.factor()
    crt_basis = vector(Integers(modulus), crt_basis([p**i for p,i in factors]))
    solutions = []

    has_solution = True
    for p,i in factors:
        solution =_solve_mod_prime_power(eqns, p, i, vars)
        if len(solution) > 0:
            solutions.append(solution)
        else:
            has_solution = False
            break


    ans = []
    if has_solution:
        for solution in cartesian_product_iterator(solutions):
            solution_mat = matrix(Integers(modulus), solution)
            ans.append(tuple(c.dot_product(crt_basis) for c in solution_mat.columns()))

    # if solution_dict == True:
    # Relaxed form suggested by Mike Hansen (#8553):
    if solution_dict:
        sol_dict = [dict(zip(vars, solution)) for solution in ans]
        return sol_dict
    else:
        return ans
Example #20
0
    def to_cartesian(self, func, params=None):
        """
        Returns a 3-tuple of functions, parameterized over ``params``, that
        represents the cartesian coordinates of the value of ``func``.

        INPUT:

         - ``func`` - A function in this coordinate space. Corresponds to the
           independent variable.

         - ``params`` - The parameters of func. Corresponds to the dependent
           variables.

        EXAMPLE::

            sage: from sage.plot.plot3d.plot3d import _ArbitraryCoordinates
            sage: x, y, z = var('x y z')
            sage: T = _ArbitraryCoordinates((x + y, x - y, z), z,[x,y])
            sage: f(x, y) = 2*x+y
            sage: T.to_cartesian(f, [x, y])
            (x + y, x - y, 2*x + y)
            sage: [h(1,2) for h in T.to_cartesian(lambda x,y: 2*x+y)]
            [3, -1, 4]
            
        We try to return a function having the same variable names as
        the function passed in::

            sage: from sage.plot.plot3d.plot3d import _ArbitraryCoordinates
            sage: x, y, z = var('x y z')
            sage: T = _ArbitraryCoordinates((x + y, x - y, z), z,[x,y])
            sage: f(a, b) = 2*a+b
            sage: T.to_cartesian(f, [a, b])
            (a + b, a - b, 2*a + b)
            sage: t1,t2,t3=T.to_cartesian(lambda a,b: 2*a+b)
            sage: import inspect
            sage: inspect.getargspec(t1)
            ArgSpec(args=['a', 'b'], varargs=None, keywords=None, defaults=None)
            sage: inspect.getargspec(t2)
            ArgSpec(args=['a', 'b'], varargs=None, keywords=None, defaults=None)
            sage: inspect.getargspec(t3)
            ArgSpec(args=['a', 'b'], varargs=None, keywords=None, defaults=None)
            sage: def g(a,b): return 2*a+b
            sage: t1,t2,t3=T.to_cartesian(g)
            sage: inspect.getargspec(t1)
            ArgSpec(args=['a', 'b'], varargs=None, keywords=None, defaults=None)
            sage: t1,t2,t3=T.to_cartesian(2*a+b)
            sage: inspect.getargspec(t1)
            ArgSpec(args=['a', 'b'], varargs=None, keywords=None, defaults=None)

        If we cannot guess the right parameter names, then the
        parameters are named `u` and `v`::

            sage: from sage.plot.plot3d.plot3d import _ArbitraryCoordinates
            sage: x, y, z = var('x y z')
            sage: T = _ArbitraryCoordinates((x + y, x - y, z), z,[x,y])
            sage: t1,t2,t3=T.to_cartesian(operator.add)
            sage: inspect.getargspec(t1)
            ArgSpec(args=['u', 'v'], varargs=None, keywords=None, defaults=None)
            sage: [h(1,2) for h in T.to_cartesian(operator.mul)]
            [3, -1, 2]
            sage: [h(u=1,v=2) for h in T.to_cartesian(operator.mul)]
            [3, -1, 2]

        """
        from sage.symbolic.expression import is_Expression
        from sage.rings.real_mpfr import is_RealNumber
        from sage.rings.integer import is_Integer
        if params is not None and (is_Expression(func) or is_RealNumber(func) or is_Integer(func)):
            return self.transform(**{
                self.dep_var: func,
                self.indep_vars[0]: params[0],
                self.indep_vars[1]: params[1]
            })
        else:
            # func might be a lambda or a Python callable; this makes it slightly
            # more complex.
            import sage.symbolic.ring
            dep_var_dummy = sage.symbolic.ring.var(self.dep_var)
            indep_var_dummies = sage.symbolic.ring.var(','.join(self.indep_vars))
            transformation = self.transform(**{
                self.dep_var: dep_var_dummy,
                self.indep_vars[0]: indep_var_dummies[0],
                self.indep_vars[1]: indep_var_dummies[1]
            })
            if params is None:
                if callable(func):
                    params = _find_arguments_for_callable(func)
                    if params is None:
                        params=['u','v']
                else:
                    raise ValueError, "function is not callable"
            def subs_func(t):
                # We use eval so that the lambda function has the same 
                # variable names as the original function
                ll="""lambda {x},{y}: t.subs({{
                    dep_var_dummy: func({x}, {y}),
                    indep_var_dummies[0]: {x},
                    indep_var_dummies[1]: {y}
                }})""".format(x=params[0], y=params[1])
                return eval(ll,dict(t=t, func=func, dep_var_dummy=dep_var_dummy, 
                                    indep_var_dummies=indep_var_dummies))
            return map(subs_func, transformation)
Example #21
0
    def to_cartesian(self, func, params=None):
        """
        Returns a 3-tuple of functions, parameterized over ``params``, that
        represents the Cartesian coordinates of the value of ``func``.

        INPUT:

         - ``func`` - A function in this coordinate space. Corresponds to the
           independent variable.

         - ``params`` - The parameters of ``func``. Corresponds to the dependent
           variables.

        EXAMPLES::

            sage: from sage.plot.plot3d.plot3d import _ArbitraryCoordinates
            sage: x, y, z = var('x y z')
            sage: T = _ArbitraryCoordinates((x + y, x - y, z), z,[x,y])
            sage: f(x, y) = 2*x+y
            sage: T.to_cartesian(f, [x, y])
            (x + y, x - y, 2*x + y)
            sage: [h(1,2) for h in T.to_cartesian(lambda x,y: 2*x+y)]
            [3.0, -1.0, 4.0]

        We try to return a function having the same variable names as
        the function passed in::

            sage: from sage.plot.plot3d.plot3d import _ArbitraryCoordinates
            sage: x, y, z = var('x y z')
            sage: T = _ArbitraryCoordinates((x + y, x - y, z), z,[x,y])
            sage: f(a, b) = 2*a+b
            sage: T.to_cartesian(f, [a, b])
            (a + b, a - b, 2*a + b)
            sage: t1,t2,t3=T.to_cartesian(lambda a,b: 2*a+b)
            sage: import inspect
            sage: inspect.getargspec(t1)
            ArgSpec(args=['a', 'b'], varargs=None, keywords=None, defaults=None)
            sage: inspect.getargspec(t2)
            ArgSpec(args=['a', 'b'], varargs=None, keywords=None, defaults=None)
            sage: inspect.getargspec(t3)
            ArgSpec(args=['a', 'b'], varargs=None, keywords=None, defaults=None)
            sage: def g(a,b): return 2*a+b
            sage: t1,t2,t3=T.to_cartesian(g)
            sage: inspect.getargspec(t1)
            ArgSpec(args=['a', 'b'], varargs=None, keywords=None, defaults=None)
            sage: t1,t2,t3=T.to_cartesian(2*a+b)
            sage: inspect.getargspec(t1)
            ArgSpec(args=['a', 'b'], varargs=None, keywords=None, defaults=None)

        If we cannot guess the right parameter names, then the
        parameters are named `u` and `v`::

            sage: from sage.plot.plot3d.plot3d import _ArbitraryCoordinates
            sage: x, y, z = var('x y z')
            sage: T = _ArbitraryCoordinates((x + y, x - y, z), z,[x,y])
            sage: t1,t2,t3=T.to_cartesian(operator.add)
            sage: inspect.getargspec(t1)
            ArgSpec(args=['u', 'v'], varargs=None, keywords=None, defaults=None)
            sage: [h(1,2) for h in T.to_cartesian(operator.mul)]
            [3.0, -1.0, 2.0]
            sage: [h(u=1,v=2) for h in T.to_cartesian(operator.mul)]
            [3.0, -1.0, 2.0]

        The output of the function ``func`` is coerced to a float when
        it is evaluated if the function is something like a lambda or
        python callable. This takes care of situations like f returning a
        singleton numpy array, for example.

            sage: from numpy import array
            sage: v_phi=array([ 0.,  1.57079637,  3.14159274, 4.71238911,  6.28318548])
            sage: v_theta=array([ 0.,  0.78539819,  1.57079637,  2.35619456,  3.14159274])
            sage: m_r=array([[ 0.16763356,  0.25683223,  0.16649297,  0.10594339, 0.55282422],
            ....: [ 0.16763356,  0.19993708,  0.31403568,  0.47359696, 0.55282422],
            ....: [ 0.16763356,  0.25683223,  0.16649297,  0.10594339, 0.55282422],
            ....: [ 0.16763356,  0.19993708,  0.31403568,  0.47359696, 0.55282422],
            ....: [ 0.16763356,  0.25683223,  0.16649297,  0.10594339, 0.55282422]])
            sage: import scipy.interpolate
            sage: f=scipy.interpolate.RectBivariateSpline(v_phi,v_theta,m_r)
            sage: spherical_plot3d(f,(0,2*pi),(0,pi))
            Graphics3d Object

        """
        from sage.symbolic.expression import is_Expression
        from sage.rings.real_mpfr import is_RealNumber
        from sage.rings.integer import is_Integer
        if params is not None and (is_Expression(func) or is_RealNumber(func)
                                   or is_Integer(func)):
            return self.transform(
                **{
                    self.dep_var: func,
                    self.indep_vars[0]: params[0],
                    self.indep_vars[1]: params[1]
                })
        else:
            # func might be a lambda or a Python callable; this makes it slightly
            # more complex.
            import sage.symbolic.ring
            dep_var_dummy = sage.symbolic.ring.var(self.dep_var)
            indep_var_dummies = sage.symbolic.ring.var(','.join(
                self.indep_vars))
            transformation = self.transform(
                **{
                    self.dep_var: dep_var_dummy,
                    self.indep_vars[0]: indep_var_dummies[0],
                    self.indep_vars[1]: indep_var_dummies[1]
                })
            if params is None:
                if callable(func):
                    params = _find_arguments_for_callable(func)
                    if params is None:
                        params = ['u', 'v']
                else:
                    raise ValueError("function is not callable")

            def subs_func(t):
                # We use eval so that the lambda function has the same
                # variable names as the original function
                ll = """lambda {x},{y}: t.subs({{
                    dep_var_dummy: float(func({x}, {y})),
                    indep_var_dummies[0]: float({x}),
                    indep_var_dummies[1]: float({y})
                }})""".format(x=params[0], y=params[1])
                return eval(
                    ll,
                    dict(t=t,
                         func=func,
                         dep_var_dummy=dep_var_dummy,
                         indep_var_dummies=indep_var_dummies))

            return [subs_func(_) for _ in transformation]
Example #22
0
    def _eval_(self, n, x):
        """
        The :meth:`_eval_()` method decides which evaluation suits best
        for the given input, and returns a proper value.

        EXAMPLES::

            sage: var('n,x')
            (n, x)
            sage: chebyshev_T(5,x)
            16*x^5 - 20*x^3 + 5*x
            sage: chebyshev_T(64, x)
            2*(2*(2*(2*(2*(2*x^2 - 1)^2 - 1)^2 - 1)^2 - 1)^2 - 1)^2 - 1
            sage: chebyshev_T(n,-1)
            (-1)^n
            sage: chebyshev_T(-7,x)
            64*x^7 - 112*x^5 + 56*x^3 - 7*x
            sage: chebyshev_T(3/2,x)
            chebyshev_T(3/2, x)
            sage: R.<t> = QQ[]
            sage: chebyshev_T(2,t)
            2*t^2 - 1
            sage: chebyshev_U(2,t)
            4*t^2 - 1
            sage: parent(chebyshev_T(4, RIF(5)))
            Real Interval Field with 53 bits of precision
            sage: RR2 = RealField(5)
            sage: chebyshev_T(100000,RR2(2))
            8.9e57180
            sage: chebyshev_T(5,Qp(3)(2))
            2 + 3^2 + 3^3 + 3^4 + 3^5 + O(3^20)
            sage: chebyshev_T(100001/2, 2)
            doctest:...: RuntimeWarning: mpmath failed, keeping expression unevaluated
            chebyshev_T(100001/2, 2)
            sage: chebyshev_U._eval_(1.5, Mod(8,9)) is None
            True
        """
        # n is an integer => evaluate algebraically (as polynomial)
        if n in ZZ:
            n = ZZ(n)
            # Expanded symbolic expression only for small values of n
            if is_Expression(x) and n.abs() < 32:
                return self.eval_formula(n, x)
            return self.eval_algebraic(n, x)

        if is_Expression(x) or is_Expression(n):
            # Check for known identities
            try:
                return self._eval_special_values_(n, x)
            except ValueError:
                # Don't evaluate => keep symbolic
                return None

        # n is not an integer and neither n nor x is symbolic.
        # We assume n and x are real/complex and evaluate numerically
        try:
            import sage.libs.mpmath.all as mpmath
            return self._evalf_(n, x)
        except mpmath.NoConvergence:
            warnings.warn("mpmath failed, keeping expression unevaluated",
                          RuntimeWarning)
            return None
        except Exception:
            # Numerical evaluation failed => keep symbolic
            return None
Example #23
0
def region_plot(f, xrange, yrange, plot_points, incol, outcol, bordercol,
                borderstyle, borderwidth, alpha, **options):
    r"""
    ``region_plot`` takes a boolean function of two variables, `f(x,y)`
    and plots the region where f is True over the specified
    ``xrange`` and ``yrange`` as demonstrated below.

    ``region_plot(f, (xmin, xmax), (ymin, ymax), ...)``

    INPUT:

    - ``f`` -- a boolean function or a list of boolean functions of two variables

    - ``(xmin, xmax)`` -- 2-tuple, the range of ``x`` values OR 3-tuple
      ``(x,xmin,xmax)``

    - ``(ymin, ymax)`` -- 2-tuple, the range of ``y`` values OR 3-tuple
      ``(y,ymin,ymax)``

    - ``plot_points``  -- integer (default: 100); number of points to plot
      in each direction of the grid

    - ``incol`` -- a color (default: ``'blue'``), the color inside the region

    - ``outcol`` -- a color (default: ``None``), the color of the outside
      of the region

    If any of these options are specified, the border will be shown as indicated,
    otherwise it is only implicit (with color ``incol``) as the border of the
    inside of the region.

     - ``bordercol`` -- a color (default: ``None``), the color of the border
       (``'black'`` if ``borderwidth`` or ``borderstyle`` is specified but not ``bordercol``)

    - ``borderstyle``  -- string (default: 'solid'), one of ``'solid'``,
      ``'dashed'``, ``'dotted'``, ``'dashdot'``, respectively ``'-'``,
      ``'--'``, ``':'``, ``'-.'``.

    - ``borderwidth``  -- integer (default: None), the width of the border in pixels

    - ``alpha`` -- (default: 1) How transparent the fill is. A number between 0 and 1.

    - ``legend_label`` -- the label for this item in the legend

    - ``base`` - (default: 10) the base of the logarithm if
      a logarithmic scale is set. This must be greater than 1. The base
      can be also given as a list or tuple ``(basex, basey)``.
      ``basex`` sets the base of the logarithm along the horizontal
      axis and ``basey`` sets the base along the vertical axis.

    - ``scale`` -- (default: ``"linear"``) string. The scale of the axes.
      Possible values are ``"linear"``, ``"loglog"``, ``"semilogx"``,
      ``"semilogy"``.

      The scale can be also be given as single argument that is a list
      or tuple ``(scale, base)`` or ``(scale, basex, basey)``.

      The ``"loglog"`` scale sets both the horizontal and vertical axes to
      logarithmic scale. The ``"semilogx"`` scale sets the horizontal axis
      to logarithmic scale. The ``"semilogy"`` scale sets the vertical axis
      to logarithmic scale. The ``"linear"`` scale is the default value
      when :class:`~sage.plot.graphics.Graphics` is initialized.


    EXAMPLES:

    Here we plot a simple function of two variables::

        sage: x,y = var('x,y')
        sage: region_plot(cos(x^2+y^2) <= 0, (x, -3, 3), (y, -3, 3))
        Graphics object consisting of 1 graphics primitive

    Here we play with the colors::

        sage: region_plot(x^2+y^3 < 2, (x, -2, 2), (y, -2, 2), incol='lightblue', bordercol='gray')
        Graphics object consisting of 2 graphics primitives

    An even more complicated plot, with dashed borders::

        sage: region_plot(sin(x)*sin(y) >= 1/4, (x,-10,10), (y,-10,10), incol='yellow', bordercol='black', borderstyle='dashed', plot_points=250)
        Graphics object consisting of 2 graphics primitives

    A disk centered at the origin::

        sage: region_plot(x^2+y^2<1, (x,-1,1), (y,-1,1))
        Graphics object consisting of 1 graphics primitive

    A plot with more than one condition (all conditions must be true for the statement to be true)::

        sage: region_plot([x^2+y^2<1, x<y], (x,-2,2), (y,-2,2))
        Graphics object consisting of 1 graphics primitive

    Since it doesn't look very good, let's increase ``plot_points``::

        sage: region_plot([x^2+y^2<1, x<y], (x,-2,2), (y,-2,2), plot_points=400)
        Graphics object consisting of 1 graphics primitive

    To get plots where only one condition needs to be true, use a function.
    Using lambda functions, we definitely need the extra ``plot_points``::

        sage: region_plot(lambda x,y: x^2+y^2<1 or x<y, (x,-2,2), (y,-2,2), plot_points=400)
        Graphics object consisting of 1 graphics primitive

    The first quadrant of the unit circle::

        sage: region_plot([y>0, x>0, x^2+y^2<1], (x,-1.1, 1.1), (y,-1.1, 1.1), plot_points = 400)
        Graphics object consisting of 1 graphics primitive

    Here is another plot, with a huge border::

        sage: region_plot(x*(x-1)*(x+1)+y^2<0, (x, -3, 2), (y, -3, 3), incol='lightblue', bordercol='gray', borderwidth=10, plot_points=50)
        Graphics object consisting of 2 graphics primitives

    If we want to keep only the region where x is positive::

        sage: region_plot([x*(x-1)*(x+1)+y^2<0, x>-1], (x, -3, 2), (y, -3, 3), incol='lightblue', plot_points=50)
        Graphics object consisting of 1 graphics primitive

    Here we have a cut circle::

        sage: region_plot([x^2+y^2<4, x>-1], (x, -2, 2), (y, -2, 2), incol='lightblue', bordercol='gray', plot_points=200)
        Graphics object consisting of 2 graphics primitives

    The first variable range corresponds to the horizontal axis and
    the second variable range corresponds to the vertical axis::

        sage: s,t=var('s,t')
        sage: region_plot(s>0,(t,-2,2),(s,-2,2))
        Graphics object consisting of 1 graphics primitive

    ::

        sage: region_plot(s>0,(s,-2,2),(t,-2,2))
        Graphics object consisting of 1 graphics primitive

    An example of a region plot in 'loglog' scale::

        sage: region_plot(x^2+y^2<100, (x,1,10), (y,1,10), scale='loglog')
        Graphics object consisting of 1 graphics primitive

    TESTS:

    To check that :trac:`16907` is fixed::

        sage: x, y = var('x, y')
        sage: disc1 = region_plot(x^2+y^2 < 1, (x, -1, 1), (y, -1, 1), alpha=0.5)
        sage: disc2 = region_plot((x-0.7)^2+(y-0.7)^2 < 0.5, (x, -2, 2), (y, -2, 2), incol='red', alpha=0.5)
        sage: disc1 + disc2
        Graphics object consisting of 2 graphics primitives

    To check that :trac:`18286` is fixed::

        sage: x, y = var('x, y')
        sage: region_plot([x == 0], (x, -1, 1), (y, -1, 1))
        Graphics object consisting of 1 graphics primitive
        sage: region_plot([x^2+y^2==1, x<y], (x, -1, 1), (y, -1, 1))
        Graphics object consisting of 1 graphics primitive
    """
    from sage.plot.all import Graphics
    from sage.plot.misc import setup_for_eval_on_grid
    from sage.symbolic.expression import is_Expression
    from warnings import warn
    import numpy

    if not isinstance(f, (list, tuple)):
        f = [f]

    feqs = [
        equify(g) for g in f if is_Expression(g)
        and g.operator() is operator.eq and not equify(g).is_zero()
    ]
    f = [
        equify(g) for g in f
        if not (is_Expression(g) and g.operator() is operator.eq)
    ]
    neqs = len(feqs)
    if neqs > 1:
        warn(
            "There are at least 2 equations; If the region is degenerated to points, plotting might show nothing."
        )
        feqs = [sum([fn**2 for fn in feqs])]
        neqs = 1
    if neqs and not bordercol:
        bordercol = incol
    if not f:
        return implicit_plot(feqs[0], xrange, yrange, plot_points=plot_points, fill=False, \
                             linewidth=borderwidth, linestyle=borderstyle, color=bordercol, **options)
    f_all, ranges = setup_for_eval_on_grid(feqs + f, [xrange, yrange],
                                           plot_points)
    xrange, yrange = [r[:2] for r in ranges]

    xy_data_arrays = numpy.asarray(
        [[[func(x, y) for x in xsrange(*ranges[0], include_endpoint=True)]
          for y in xsrange(*ranges[1], include_endpoint=True)]
         for func in f_all[neqs::]],
        dtype=float)
    xy_data_array = numpy.abs(xy_data_arrays.prod(axis=0))
    # Now we need to set entries to negative iff all
    # functions were negative at that point.
    neg_indices = (xy_data_arrays < 0).all(axis=0)
    xy_data_array[neg_indices] = -xy_data_array[neg_indices]

    from matplotlib.colors import ListedColormap
    incol = rgbcolor(incol)
    if outcol:
        outcol = rgbcolor(outcol)
        cmap = ListedColormap([incol, outcol])
        cmap.set_over(outcol, alpha=alpha)
    else:
        outcol = rgbcolor('white')
        cmap = ListedColormap([incol, outcol])
        cmap.set_over(outcol, alpha=0)
    cmap.set_under(incol, alpha=alpha)

    g = Graphics()

    # Reset aspect_ratio to 'automatic' in case scale is 'semilog[xy]'.
    # Otherwise matplotlib complains.
    scale = options.get('scale', None)
    if isinstance(scale, (list, tuple)):
        scale = scale[0]
    if scale == 'semilogy' or scale == 'semilogx':
        options['aspect_ratio'] = 'automatic'

    g._set_extra_kwds(
        Graphics._extract_kwds_for_show(options, ignore=['xmin', 'xmax']))

    if neqs == 0:
        g.add_primitive(
            ContourPlot(
                xy_data_array, xrange, yrange,
                dict(contours=[-1e-20, 0, 1e-20],
                     cmap=cmap,
                     fill=True,
                     **options)))
    else:
        mask = numpy.asarray([[elt > 0 for elt in rows]
                              for rows in xy_data_array],
                             dtype=bool)
        xy_data_array = numpy.asarray([[
            f_all[0](x, y) for x in xsrange(*ranges[0], include_endpoint=True)
        ] for y in xsrange(*ranges[1], include_endpoint=True)],
                                      dtype=float)
        xy_data_array[mask] = None
    if bordercol or borderstyle or borderwidth:
        cmap = [rgbcolor(bordercol)] if bordercol else ['black']
        linestyles = [borderstyle] if borderstyle else None
        linewidths = [borderwidth] if borderwidth else None
        g.add_primitive(
            ContourPlot(
                xy_data_array, xrange, yrange,
                dict(linestyles=linestyles,
                     linewidths=linewidths,
                     contours=[0],
                     cmap=[bordercol],
                     fill=False,
                     **options)))

    return g
Example #24
0
    def to_cartesian(self, func, params=None):
        """
        Returns a 3-tuple of functions, parameterized over ``params``, that
        represents the Cartesian coordinates of the value of ``func``.

        INPUT:

         - ``func`` - A function in this coordinate space. Corresponds to the
           independent variable.

         - ``params`` - The parameters of func. Corresponds to the dependent
           variables.

        EXAMPLE::

            sage: from sage.plot.plot3d.plot3d import _ArbitraryCoordinates
            sage: x, y, z = var('x y z')
            sage: T = _ArbitraryCoordinates((x + y, x - y, z), z,[x,y])
            sage: f(x, y) = 2*x+y
            sage: T.to_cartesian(f, [x, y])
            (x + y, x - y, 2*x + y)
            sage: [h(1,2) for h in T.to_cartesian(lambda x,y: 2*x+y)]
            [3.0, -1.0, 4.0]

        We try to return a function having the same variable names as
        the function passed in::

            sage: from sage.plot.plot3d.plot3d import _ArbitraryCoordinates
            sage: x, y, z = var('x y z')
            sage: T = _ArbitraryCoordinates((x + y, x - y, z), z,[x,y])
            sage: f(a, b) = 2*a+b
            sage: T.to_cartesian(f, [a, b])
            (a + b, a - b, 2*a + b)
            sage: t1,t2,t3=T.to_cartesian(lambda a,b: 2*a+b)
            sage: import inspect
            sage: inspect.getargspec(t1)
            ArgSpec(args=['a', 'b'], varargs=None, keywords=None, defaults=None)
            sage: inspect.getargspec(t2)
            ArgSpec(args=['a', 'b'], varargs=None, keywords=None, defaults=None)
            sage: inspect.getargspec(t3)
            ArgSpec(args=['a', 'b'], varargs=None, keywords=None, defaults=None)
            sage: def g(a,b): return 2*a+b
            sage: t1,t2,t3=T.to_cartesian(g)
            sage: inspect.getargspec(t1)
            ArgSpec(args=['a', 'b'], varargs=None, keywords=None, defaults=None)
            sage: t1,t2,t3=T.to_cartesian(2*a+b)
            sage: inspect.getargspec(t1)
            ArgSpec(args=['a', 'b'], varargs=None, keywords=None, defaults=None)

        If we cannot guess the right parameter names, then the
        parameters are named `u` and `v`::

            sage: from sage.plot.plot3d.plot3d import _ArbitraryCoordinates
            sage: x, y, z = var('x y z')
            sage: T = _ArbitraryCoordinates((x + y, x - y, z), z,[x,y])
            sage: t1,t2,t3=T.to_cartesian(operator.add)
            sage: inspect.getargspec(t1)
            ArgSpec(args=['u', 'v'], varargs=None, keywords=None, defaults=None)
            sage: [h(1,2) for h in T.to_cartesian(operator.mul)]
            [3.0, -1.0, 2.0]
            sage: [h(u=1,v=2) for h in T.to_cartesian(operator.mul)]
            [3.0, -1.0, 2.0]

        The output of the function `func` is coerced to a float when
        it is evaluated if the function is something like a lambda or
        python callable. This takes care of situations like f returning a
        singleton numpy array, for example.

            sage: from numpy import array
            sage: v_phi=array([ 0.,  1.57079637,  3.14159274, 4.71238911,  6.28318548])
            sage: v_theta=array([ 0.,  0.78539819,  1.57079637,  2.35619456,  3.14159274])
            sage: m_r=array([[ 0.16763356,  0.25683223,  0.16649297,  0.10594339, 0.55282422],
            ... [ 0.16763356,  0.19993708,  0.31403568,  0.47359696, 0.55282422],
            ... [ 0.16763356,  0.25683223,  0.16649297,  0.10594339, 0.55282422],
            ... [ 0.16763356,  0.19993708,  0.31403568,  0.47359696, 0.55282422],
            ... [ 0.16763356,  0.25683223,  0.16649297,  0.10594339, 0.55282422]])
            sage: import scipy.interpolate
            sage: f=scipy.interpolate.RectBivariateSpline(v_phi,v_theta,m_r)
            sage: spherical_plot3d(f,(0,2*pi),(0,pi))
            Graphics3d Object

        """
        from sage.symbolic.expression import is_Expression
        from sage.rings.real_mpfr import is_RealNumber
        from sage.rings.integer import is_Integer
        if params is not None and (is_Expression(func) or is_RealNumber(func) or is_Integer(func)):
            return self.transform(**{
                self.dep_var: func,
                self.indep_vars[0]: params[0],
                self.indep_vars[1]: params[1]
            })
        else:
            # func might be a lambda or a Python callable; this makes it slightly
            # more complex.
            import sage.symbolic.ring
            dep_var_dummy = sage.symbolic.ring.var(self.dep_var)
            indep_var_dummies = sage.symbolic.ring.var(','.join(self.indep_vars))
            transformation = self.transform(**{
                self.dep_var: dep_var_dummy,
                self.indep_vars[0]: indep_var_dummies[0],
                self.indep_vars[1]: indep_var_dummies[1]
            })
            if params is None:
                if callable(func):
                    params = _find_arguments_for_callable(func)
                    if params is None:
                        params=['u','v']
                else:
                    raise ValueError("function is not callable")
            def subs_func(t):
                # We use eval so that the lambda function has the same
                # variable names as the original function
                ll="""lambda {x},{y}: t.subs({{
                    dep_var_dummy: float(func({x}, {y})),
                    indep_var_dummies[0]: float({x}),
                    indep_var_dummies[1]: float({y})
                }})""".format(x=params[0], y=params[1])
                return eval(ll,dict(t=t, func=func, dep_var_dummy=dep_var_dummy,
                                    indep_var_dummies=indep_var_dummies))
            return [subs_func(_) for _ in transformation]