Beispiel #1
0
def unify_arguments(funcs):
    """
    Return a tuple of variables of the functions, as well as the
    number of "free" variables (i.e., variables that defined in a
    callable function).

    INPUT:

    - ``funcs`` -- a list of functions; these can be symbolic
      expressions, polynomials, etc

    OUTPUT: functions, expected arguments

    - A tuple of variables in the functions

    - A tuple of variables that were "free" in the functions

    EXAMPLES::

        sage: x,y,z=var('x,y,z')
        sage: f(x,y)=x+y-z
        sage: g(x,y)=x+y
        sage: h(y)=-y
        sage: sage.plot.misc.unify_arguments((f,g,h))
        ((x, y, z), (z,))
        sage: sage.plot.misc.unify_arguments((g,h))
        ((x, y), ())
        sage: sage.plot.misc.unify_arguments((f,z))
        ((x, y, z), (z,))
        sage: sage.plot.misc.unify_arguments((h,z))
        ((y, z), (z,))
        sage: sage.plot.misc.unify_arguments((x+y,x-y))
        ((x, y), (x, y))
    """
    from sage.symbolic.callable import is_CallableSymbolicExpression

    vars = set()
    free_variables = set()
    if not isinstance(funcs, (list, tuple)):
        funcs = [funcs]

    for f in funcs:
        if is_CallableSymbolicExpression(f):
            f_args = set(f.arguments())
            vars.update(f_args)
        else:
            f_args = set()

        try:
            free_vars = set(f.variables()).difference(f_args)
            vars.update(free_vars)
            free_variables.update(free_vars)
        except AttributeError:
            # we probably have a constant
            pass
    return tuple(sorted(vars, key=lambda x: str(x))), tuple(
        sorted(free_variables, key=lambda x: str(x)))
Beispiel #2
0
def unify_arguments(funcs):
    """
    Returns a tuple of variables of the functions, as well as the
    number of "free" variables (i.e., variables that defined in a
    callable function).

    INPUT:

    - ``funcs`` -- a list of functions; these can be symbolic
            expressions, polynomials, etc

    OUTPUT: functions, expected arguments

    - A tuple of variables in the functions

    - A tuple of variables that were "free" in the functions

    EXAMPLES:

        sage: x,y,z=var('x,y,z')
        sage: f(x,y)=x+y-z
        sage: g(x,y)=x+y
        sage: h(y)=-y
        sage: sage.plot.misc.unify_arguments((f,g,h))
        ((x, y, z), (z,))
        sage: sage.plot.misc.unify_arguments((g,h))
        ((x, y), ())
        sage: sage.plot.misc.unify_arguments((f,z))
        ((x, y, z), (z,))
        sage: sage.plot.misc.unify_arguments((h,z))
        ((y, z), (z,))
        sage: sage.plot.misc.unify_arguments((x+y,x-y))
        ((x, y), (x, y))
    """
    from sage.symbolic.callable import is_CallableSymbolicExpression

    vars=set()
    free_variables=set()
    if not isinstance(funcs, (list, tuple)):
        funcs=[funcs]

    for f in funcs:
        if is_CallableSymbolicExpression(f):
            f_args=set(f.arguments())
            vars.update(f_args)
        else:
            f_args=set()

        try:
            free_vars = set(f.variables()).difference(f_args)
            vars.update(free_vars)
            free_variables.update(free_vars)
        except AttributeError:
            # we probably have a constant
            pass
    return tuple(sorted(vars, key=lambda x: str(x))), tuple(sorted(free_variables, key=lambda x: str(x)))
Beispiel #3
0
def adapt_to_callable(f, nargs=None):
    """
    Tries to make every function in f into a (fast) callable
    function, returning a new list of functions and the expected
    arguments.
    
    INPUT:
    
    - ``f`` -- a list of functions; these can be symbolic expressions,
            polynomials, etc
            
    -  ``nargs`` -- number of input args to have in the output functions
    
    OUTPUT: functions, expected arguments
    """
    from sage.misc.misc import deprecation
    deprecation(
        "adapt_to_callable is a deprecated function.  Please use functions from sage.misc.plot instead."
    )

    try:
        from sage.symbolic.callable import is_CallableSymbolicExpression
        if sum([is_CallableSymbolicExpression(z) for z in f]):
            # Sum to get common universe; this works since f is
            # callable, and summing gets the arguments in the right
            # order.
            vars = sum(f).args()
        else:
            # Otherwise any free variable names in any order
            try:
                vars = tuple(sorted(set(sum([z.variables() for z in f], ()))))
                if len(vars) > 1:
                    from sage.misc.misc import deprecation
                    deprecation(
                        "Substitution using function-call syntax and unnamed arguments is deprecated and will be removed from a future release of Sage; you can use named arguments instead, like EXPR(x=..., y=...)"
                    )
            except AttributeError:
                vars = ()
                f = [fast_float_constant(x) for x in f]
    except TypeError:
        vars = ()
        f = [fast_float_constant(x) for x in f]

    if nargs is not None and len(vars) != nargs:
        vars = (vars + ('_', ) * nargs)[:nargs]

    return fast_float(f, *vars), vars
Beispiel #4
0
def adapt_to_callable(f, nargs=None):
    """
    Tries to make every function in f into a (fast) callable
    function, returning a new list of functions and the expected
    arguments.
    
    INPUT:
    
    - ``f`` -- a list of functions; these can be symbolic expressions,
            polynomials, etc
            
    -  ``nargs`` -- number of input args to have in the output functions
    
    OUTPUT: functions, expected arguments
    """
    from sage.misc.misc import deprecation
    deprecation("adapt_to_callable is a deprecated function.  Please use functions from sage.misc.plot instead.")
    
    try:
        from sage.symbolic.callable import is_CallableSymbolicExpression
        if sum([is_CallableSymbolicExpression(z) for z in f]):
            # Sum to get common universe; this works since f is
            # callable, and summing gets the arguments in the right
            # order.
            vars = sum(f).args()
        else:
            # Otherwise any free variable names in any order
            try:
                vars = tuple(sorted(set(sum( [z.variables() for z in f], ()) )))
                if len(vars) > 1:
                    from sage.misc.misc import deprecation
                    deprecation("Substitution using function-call syntax and unnamed arguments is deprecated and will be removed from a future release of Sage; you can use named arguments instead, like EXPR(x=..., y=...)")
            except AttributeError:
                vars = ()
                f = [fast_float_constant(x) for x in f]
    except TypeError:
        vars = ()
        f = [fast_float_constant(x) for x in f]
    
    if nargs is not None and len(vars) != nargs:
        vars = (vars + ('_',)*nargs)[:nargs]
        
    return fast_float(f, *vars), vars
Beispiel #5
0
def plot3d(f, urange, vrange, adaptive=False, transformation=None, **kwds):
    """
    Plots a function in 3d.

    INPUT:


    -  ``f`` - a symbolic expression or function of 2
       variables

    -  ``urange`` - a 2-tuple (u_min, u_max) or a 3-tuple
       (u, u_min, u_max)

    -  ``vrange`` - a 2-tuple (v_min, v_max) or a 3-tuple
       (v, v_min, v_max)

    -  ``adaptive`` - (default: False) whether to use
       adaptive refinement to draw the plot (slower, but may look better).
       This option does NOT work in conjunction with a transformation
       (see below).

    -  ``mesh`` - bool (default: False) whether to display
       mesh grid lines

    -  ``dots`` - bool (default: False) whether to display
       dots at mesh grid points

    -  ``plot_points`` - (default: "automatic") initial number of sample
       points in each direction; an integer or a pair of integers


    - ``transformation`` - (default: None) a transformation to
      apply. May be a 3 or 4-tuple (x_func, y_func, z_func,
      independent_vars) where the first 3 items indicate a
      transformation to Cartesian coordinates (from your coordinate
      system) in terms of u, v, and the function variable fvar (for
      which the value of f will be substituted). If a 3-tuple is
      specified, the independent variables are chosen from the range
      variables.  If a 4-tuple is specified, the 4th element is a list
      of independent variables.  ``transformation`` may also be a
      predefined coordinate system transformation like Spherical or
      Cylindrical.

    .. note::

       ``mesh`` and ``dots`` are not supported when using the Tachyon
       raytracer renderer.

    EXAMPLES: We plot a 3d function defined as a Python function::

        sage: plot3d(lambda x, y: x^2 + y^2, (-2,2), (-2,2))
        Graphics3d Object

    .. PLOT::
        
        sphinx_plot(plot3d(lambda x, y: x**2 + y**2, (-2,2), (-2,2)))

    We plot the same 3d function but using adaptive refinement::

        sage: plot3d(lambda x, y: x^2 + y^2, (-2,2), (-2,2), adaptive=True)
        Graphics3d Object

    .. PLOT::
        
        sphinx_plot(plot3d(lambda x, y: x**2 + y**2, (-2,2), (-2,2), adaptive=True))

    Adaptive refinement but with more points::

        sage: plot3d(lambda x, y: x^2 + y^2, (-2,2), (-2,2), adaptive=True, initial_depth=5)
        Graphics3d Object

    .. PLOT::
        
        sphinx_plot(plot3d(lambda x, y: x**2 + y**2, (-2,2), (-2,2), adaptive=True, initial_depth=5))

    We plot some 3d symbolic functions::

        sage: var('x,y')
        (x, y)
        sage: plot3d(x^2 + y^2, (x,-2,2), (y,-2,2))
        Graphics3d Object

    .. PLOT::
        
        var('x y')
        sphinx_plot(plot3d(x**2 + y**2, (x,-2,2), (y,-2,2)))

    ::

        sage: plot3d(sin(x*y), (x, -pi, pi), (y, -pi, pi))
        Graphics3d Object

    .. PLOT::
        
        var('x y')
        sphinx_plot(plot3d(sin(x*y), (x, -pi, pi), (y, -pi, pi)))

    We give a plot with extra sample points::

        sage: var('x,y')
        (x, y)
        sage: plot3d(sin(x^2+y^2),(x,-5,5),(y,-5,5), plot_points=200)
        Graphics3d Object

    .. PLOT::
        
        var('x y')
        sphinx_plot(plot3d(sin(x**2+y**2),(x,-5,5),(y,-5,5), plot_points=200))

    ::

        sage: plot3d(sin(x^2+y^2),(x,-5,5),(y,-5,5), plot_points=[10,100])
        Graphics3d Object

    .. PLOT::
        
        var('x y')
        sphinx_plot(plot3d(sin(x**2+y**2),(x,-5,5),(y,-5,5), plot_points=[10,100]))

    A 3d plot with a mesh::

        sage: var('x,y')
        (x, y)
        sage: plot3d(sin(x-y)*y*cos(x),(x,-3,3),(y,-3,3), mesh=True)
        Graphics3d Object

    .. PLOT::
        
        var('x y')
        sphinx_plot(plot3d(sin(x-y)*y*cos(x),(x,-3,3),(y,-3,3), mesh=True))

    Two wobby translucent planes::

        sage: x,y = var('x,y')
        sage: P = plot3d(x+y+sin(x*y), (x,-10,10),(y,-10,10), opacity=0.87, color='blue')
        sage: Q = plot3d(x-2*y-cos(x*y),(x,-10,10),(y,-10,10),opacity=0.3,color='red')
        sage: P + Q
        Graphics3d Object

    .. PLOT::
        
        x,y=var('x y')
        P = plot3d(x+y+sin(x*y), (x,-10,10),(y,-10,10), opacity=0.87, color='blue')
        Q = plot3d(x-2*y-cos(x*y),(x,-10,10),(y,-10,10),opacity=0.3,color='red')
        sphinx_plot(P+Q)

    We draw two parametric surfaces and a transparent plane::

        sage: L = plot3d(lambda x,y: 0, (-5,5), (-5,5), color="lightblue", opacity=0.8)
        sage: P = plot3d(lambda x,y: 4 - x^3 - y^2, (-2,2), (-2,2), color='green')
        sage: Q = plot3d(lambda x,y: x^3 + y^2 - 4, (-2,2), (-2,2), color='orange')
        sage: L + P + Q
        Graphics3d Object

    .. PLOT::
        
        L = plot3d(lambda x,y: 0, (-5,5), (-5,5), color="lightblue", opacity=0.8)
        P = plot3d(lambda x,y: 4 - x**3 - y**2, (-2,2), (-2,2), color='green')
        Q = plot3d(lambda x,y: x**3 + y**2 - 4, (-2,2), (-2,2), color='orange')
        sphinx_plot(L+P+Q)

    We draw the "Sinus" function (water ripple-like surface)::

        sage: x, y = var('x y')
        sage: plot3d(sin(pi*(x^2+y^2))/2,(x,-1,1),(y,-1,1))
        Graphics3d Object

    .. PLOT::
        
        x, y = var('x y')
        sphinx_plot(plot3d(sin(pi*(x**2+y**2))/2,(x,-1,1),(y,-1,1)))

    Hill and valley (flat surface with a bump and a dent)::

        sage: x, y = var('x y')
        sage: plot3d( 4*x*exp(-x^2-y^2), (x,-2,2), (y,-2,2))
        Graphics3d Object

    .. PLOT::
        
        x, y = var('x y')
        sphinx_plot(plot3d( 4*x*exp(-x**2-y**2), (x,-2,2), (y,-2,2)))

    An example of a transformation::

        sage: r, phi, z = var('r phi z')
        sage: trans=(r*cos(phi),r*sin(phi),z)
        sage: plot3d(cos(r),(r,0,17*pi/2),(phi,0,2*pi),transformation=trans,opacity=0.87).show(aspect_ratio=(1,1,2),frame=False)

    .. PLOT::
        
        r, phi, z = var('r phi z')
        trans = (r*cos(phi),r*sin(phi),z)
        P = plot3d(cos(r),(r,0,17*pi/2),(phi,0,2*pi),transformation=trans,opacity=0.87)
        P.aspect_ratio([1,1,2])
        sphinx_plot(P)

    An example of a transformation with symbolic vector::

        sage: cylindrical(r,theta,z)=[r*cos(theta),r*sin(theta),z]
        sage: plot3d(3,(theta,0,pi/2),(z,0,pi/2),transformation=cylindrical)
        Graphics3d Object

    .. PLOT::
        
        r, theta, z = var('r theta z')
        cylindrical=(r*cos(theta),r*sin(theta),z)
        P = plot3d(z-z+3,(theta,0,pi/2),(z,0,pi/2),transformation=cylindrical)
        sphinx_plot(P)

    Many more examples of transformations::

        sage: u, v, w = var('u v w')
        sage: rectangular=(u,v,w)
        sage: spherical=(w*cos(u)*sin(v),w*sin(u)*sin(v),w*cos(v))
        sage: cylindric_radial=(w*cos(u),w*sin(u),v)
        sage: cylindric_axial=(v*cos(u),v*sin(u),w)
        sage: parabolic_cylindrical=(w*v,(v^2-w^2)/2,u)

    Plot a constant function of each of these to get an idea of what it does::

        sage: A = plot3d(2,(u,-pi,pi),(v,0,pi),transformation=rectangular,plot_points=[100,100])
        sage: B = plot3d(2,(u,-pi,pi),(v,0,pi),transformation=spherical,plot_points=[100,100])
        sage: C = plot3d(2,(u,-pi,pi),(v,0,pi),transformation=cylindric_radial,plot_points=[100,100])
        sage: D = plot3d(2,(u,-pi,pi),(v,0,pi),transformation=cylindric_axial,plot_points=[100,100])
        sage: E = plot3d(2,(u,-pi,pi),(v,-pi,pi),transformation=parabolic_cylindrical,plot_points=[100,100])
        sage: @interact
        ....: def _(which_plot=[A,B,C,D,E]):
        ....:     show(which_plot)
        <html>...

    Now plot a function::

        sage: g=3+sin(4*u)/2+cos(4*v)/2
        sage: F = plot3d(g,(u,-pi,pi),(v,0,pi),transformation=rectangular,plot_points=[100,100])
        sage: G = plot3d(g,(u,-pi,pi),(v,0,pi),transformation=spherical,plot_points=[100,100])
        sage: H = plot3d(g,(u,-pi,pi),(v,0,pi),transformation=cylindric_radial,plot_points=[100,100])
        sage: I = plot3d(g,(u,-pi,pi),(v,0,pi),transformation=cylindric_axial,plot_points=[100,100])
        sage: J = plot3d(g,(u,-pi,pi),(v,0,pi),transformation=parabolic_cylindrical,plot_points=[100,100])
        sage: @interact
        ....: def _(which_plot=[F, G, H, I, J]):
        ....:     show(which_plot)
        <html>...

    TESTS:

    Make sure the transformation plots work::

        sage: show(A + B + C + D + E)
        sage: show(F + G + H + I + J)

    Listing the same plot variable twice gives an error::

        sage: x, y = var('x y')
        sage: plot3d( 4*x*exp(-x^2-y^2), (x,-2,2), (x,-2,2))
        Traceback (most recent call last):
        ...
        ValueError: range variables should be distinct, but there are duplicates
    """
    if transformation is not None:
        params = None
        from sage.symbolic.callable import is_CallableSymbolicExpression
        # First, determine the parameters for f (from the first item of urange
        # and vrange, preferably).
        if len(urange) == 3 and len(vrange) == 3:
            params = (urange[0], vrange[0])
        elif is_CallableSymbolicExpression(f):
            params = f.variables()

        from sage.modules.vector_callable_symbolic_dense import Vector_callable_symbolic_dense
        if isinstance(transformation,
                      (tuple, list, Vector_callable_symbolic_dense)):
            if len(transformation) == 3:
                if params is None:
                    raise ValueError(
                        "must specify independent variable names in the ranges when using generic transformation"
                    )
                indep_vars = params
            elif len(transformation) == 4:
                indep_vars = transformation[3]
                transformation = transformation[0:3]
            else:
                raise ValueError("unknown transformation type")
            # find out which variable is the function variable by
            # eliminating the parameter variables.
            all_vars = set(
                sum([list(s.variables()) for s in transformation], []))
            dep_var = all_vars - set(indep_vars)
            if len(dep_var) == 1:
                dep_var = dep_var.pop()
                transformation = _ArbitraryCoordinates(transformation, dep_var,
                                                       indep_vars)
            else:
                raise ValueError(
                    "unable to determine the function variable in the transform"
                )

        if isinstance(transformation, _Coordinates):
            R = transformation.to_cartesian(f, params)
            return parametric_plot3d.parametric_plot3d(R, urange, vrange,
                                                       **kwds)
        else:
            raise ValueError('unknown transformation type')
    elif adaptive:
        P = plot3d_adaptive(f, urange, vrange, **kwds)
    else:
        u = fast_float_arg(0)
        v = fast_float_arg(1)
        P = parametric_plot3d.parametric_plot3d((u, v, f), urange, vrange,
                                                **kwds)
    P.frame_aspect_ratio([1.0, 1.0, 0.5])
    return P
Beispiel #6
0
def plot3d(f, urange, vrange, adaptive=False, transformation=None, **kwds):
    """
    INPUT:


    -  ``f`` - a symbolic expression or function of 2
       variables

    -  ``urange`` - a 2-tuple (u_min, u_max) or a 3-tuple
       (u, u_min, u_max)

    -  ``vrange`` - a 2-tuple (v_min, v_max) or a 3-tuple
       (v, v_min, v_max)

    -  ``adaptive`` - (default: False) whether to use
       adaptive refinement to draw the plot (slower, but may look better).
       This option does NOT work in conjuction with a transformation
       (see below).

    -  ``mesh`` - bool (default: False) whether to display
       mesh grid lines

    -  ``dots`` - bool (default: False) whether to display
       dots at mesh grid points

    -  ``plot_points`` - (default: "automatic") initial number of sample
       points in each direction; an integer or a pair of integers


    - ``transformation`` - (default: None) a transformation to
      apply. May be a 3 or 4-tuple (x_func, y_func, z_func,
      independent_vars) where the first 3 items indicate a
      transformation to Cartesian coordinates (from your coordinate
      system) in terms of u, v, and the function variable fvar (for
      which the value of f will be substituted). If a 3-tuple is
      specified, the independent variables are chosen from the range
      variables.  If a 4-tuple is specified, the 4th element is a list
      of independent variables.  ``transformation`` may also be a
      predefined coordinate system transformation like Spherical or
      Cylindrical.

    .. note::

       ``mesh`` and ``dots`` are not supported when using the Tachyon
       raytracer renderer.

    EXAMPLES: We plot a 3d function defined as a Python function::

        sage: plot3d(lambda x, y: x^2 + y^2, (-2,2), (-2,2))
        Graphics3d Object

    .. PLOT::
        
        sphinx_plot(plot3d(lambda x, y: x**2 + y**2, (-2,2), (-2,2)))

    We plot the same 3d function but using adaptive refinement::

        sage: plot3d(lambda x, y: x^2 + y^2, (-2,2), (-2,2), adaptive=True)
        Graphics3d Object

    .. PLOT::
        
        sphinx_plot(plot3d(lambda x, y: x**2 + y**2, (-2,2), (-2,2), adaptive=True))

    Adaptive refinement but with more points::

        sage: plot3d(lambda x, y: x^2 + y^2, (-2,2), (-2,2), adaptive=True, initial_depth=5)
        Graphics3d Object

    .. PLOT::
        
        sphinx_plot(plot3d(lambda x, y: x**2 + y**2, (-2,2), (-2,2), adaptive=True, initial_depth=5))

    We plot some 3d symbolic functions::

        sage: var('x,y')
        (x, y)
        sage: plot3d(x^2 + y^2, (x,-2,2), (y,-2,2))
        Graphics3d Object

    .. PLOT::
        
        var('x y')
        sphinx_plot(plot3d(x**2 + y**2, (x,-2,2), (y,-2,2)))

    ::

        sage: plot3d(sin(x*y), (x, -pi, pi), (y, -pi, pi))
        Graphics3d Object

    .. PLOT::
        
        var('x y')
        sphinx_plot(plot3d(sin(x*y), (x, -pi, pi), (y, -pi, pi)))

    We give a plot with extra sample points::

        sage: var('x,y')
        (x, y)
        sage: plot3d(sin(x^2+y^2),(x,-5,5),(y,-5,5), plot_points=200)
        Graphics3d Object

    .. PLOT::
        
        var('x y')
        sphinx_plot(plot3d(sin(x**2+y**2),(x,-5,5),(y,-5,5), plot_points=200))

    ::

        sage: plot3d(sin(x^2+y^2),(x,-5,5),(y,-5,5), plot_points=[10,100])
        Graphics3d Object

    .. PLOT::
        
        var('x y')
        sphinx_plot(plot3d(sin(x**2+y**2),(x,-5,5),(y,-5,5), plot_points=[10,100]))

    A 3d plot with a mesh::

        sage: var('x,y')
        (x, y)
        sage: plot3d(sin(x-y)*y*cos(x),(x,-3,3),(y,-3,3), mesh=True)
        Graphics3d Object

    .. PLOT::
        
        var('x y')
        sphinx_plot(plot3d(sin(x-y)*y*cos(x),(x,-3,3),(y,-3,3), mesh=True))

    Two wobby translucent planes::

        sage: x,y = var('x,y')
        sage: P = plot3d(x+y+sin(x*y), (x,-10,10),(y,-10,10), opacity=0.87, color='blue')
        sage: Q = plot3d(x-2*y-cos(x*y),(x,-10,10),(y,-10,10),opacity=0.3,color='red')
        sage: P + Q
        Graphics3d Object

    .. PLOT::
        
        x,y=var('x y')
        P = plot3d(x+y+sin(x*y), (x,-10,10),(y,-10,10), opacity=0.87, color='blue')
        Q = plot3d(x-2*y-cos(x*y),(x,-10,10),(y,-10,10),opacity=0.3,color='red')
        sphinx_plot(P+Q)

    We draw two parametric surfaces and a transparent plane::

        sage: L = plot3d(lambda x,y: 0, (-5,5), (-5,5), color="lightblue", opacity=0.8)
        sage: P = plot3d(lambda x,y: 4 - x^3 - y^2, (-2,2), (-2,2), color='green')
        sage: Q = plot3d(lambda x,y: x^3 + y^2 - 4, (-2,2), (-2,2), color='orange')
        sage: L + P + Q
        Graphics3d Object

    .. PLOT::
        
        L = plot3d(lambda x,y: 0, (-5,5), (-5,5), color="lightblue", opacity=0.8)
        P = plot3d(lambda x,y: 4 - x**3 - y**2, (-2,2), (-2,2), color='green')
        Q = plot3d(lambda x,y: x**3 + y**2 - 4, (-2,2), (-2,2), color='orange')
        sphinx_plot(L+P+Q)

    We draw the "Sinus" function (water ripple-like surface)::

        sage: x, y = var('x y')
        sage: plot3d(sin(pi*(x^2+y^2))/2,(x,-1,1),(y,-1,1))
        Graphics3d Object

    .. PLOT::
        
        x, y = var('x y')
        sphinx_plot(plot3d(sin(pi*(x**2+y**2))/2,(x,-1,1),(y,-1,1)))

    Hill and valley (flat surface with a bump and a dent)::

        sage: x, y = var('x y')
        sage: plot3d( 4*x*exp(-x^2-y^2), (x,-2,2), (y,-2,2))
        Graphics3d Object

    .. PLOT::
        
        x, y = var('x y')
        sphinx_plot(plot3d( 4*x*exp(-x**2-y**2), (x,-2,2), (y,-2,2)))

    An example of a transformation::

        sage: r, phi, z = var('r phi z')
        sage: trans=(r*cos(phi),r*sin(phi),z)
        sage: plot3d(cos(r),(r,0,17*pi/2),(phi,0,2*pi),transformation=trans,opacity=0.87).show(aspect_ratio=(1,1,2),frame=False)

    .. PLOT::
        
        r, phi, z = var('r phi z')
        trans = (r*cos(phi),r*sin(phi),z)
        P = plot3d(cos(r),(r,0,17*pi/2),(phi,0,2*pi),transformation=trans,opacity=0.87)
        P.aspect_ratio([1,1,2])
        sphinx_plot(P)

    An example of a transformation with symbolic vector::

        sage: cylindrical(r,theta,z)=[r*cos(theta),r*sin(theta),z]
        sage: plot3d(3,(theta,0,pi/2),(z,0,pi/2),transformation=cylindrical)
        Graphics3d Object

    .. PLOT::
        
        r, theta, z = var('r theta z')
        cylindrical=(r*cos(theta),r*sin(theta),z)
        P = plot3d(z-z+3,(theta,0,pi/2),(z,0,pi/2),transformation=cylindrical)
        sphinx_plot(P)

    Many more examples of transformations::

        sage: u, v, w = var('u v w')
        sage: rectangular=(u,v,w)
        sage: spherical=(w*cos(u)*sin(v),w*sin(u)*sin(v),w*cos(v))
        sage: cylindric_radial=(w*cos(u),w*sin(u),v)
        sage: cylindric_axial=(v*cos(u),v*sin(u),w)
        sage: parabolic_cylindrical=(w*v,(v^2-w^2)/2,u)

    Plot a constant function of each of these to get an idea of what it does::

        sage: A = plot3d(2,(u,-pi,pi),(v,0,pi),transformation=rectangular,plot_points=[100,100])
        sage: B = plot3d(2,(u,-pi,pi),(v,0,pi),transformation=spherical,plot_points=[100,100])
        sage: C = plot3d(2,(u,-pi,pi),(v,0,pi),transformation=cylindric_radial,plot_points=[100,100])
        sage: D = plot3d(2,(u,-pi,pi),(v,0,pi),transformation=cylindric_axial,plot_points=[100,100])
        sage: E = plot3d(2,(u,-pi,pi),(v,-pi,pi),transformation=parabolic_cylindrical,plot_points=[100,100])
        sage: @interact
        ... def _(which_plot=[A,B,C,D,E]):
        ...       show(which_plot)
        <html>...

    Now plot a function::

        sage: g=3+sin(4*u)/2+cos(4*v)/2
        sage: F = plot3d(g,(u,-pi,pi),(v,0,pi),transformation=rectangular,plot_points=[100,100])
        sage: G = plot3d(g,(u,-pi,pi),(v,0,pi),transformation=spherical,plot_points=[100,100])
        sage: H = plot3d(g,(u,-pi,pi),(v,0,pi),transformation=cylindric_radial,plot_points=[100,100])
        sage: I = plot3d(g,(u,-pi,pi),(v,0,pi),transformation=cylindric_axial,plot_points=[100,100])
        sage: J = plot3d(g,(u,-pi,pi),(v,0,pi),transformation=parabolic_cylindrical,plot_points=[100,100])
        sage: @interact
        ... def _(which_plot=[F, G, H, I, J]):
        ...       show(which_plot)
        <html>...

    TESTS:

    Make sure the transformation plots work::

        sage: show(A + B + C + D + E)
        sage: show(F + G + H + I + J)

    Listing the same plot variable twice gives an error::

        sage: x, y = var('x y')
        sage: plot3d( 4*x*exp(-x^2-y^2), (x,-2,2), (x,-2,2))
        Traceback (most recent call last):
        ...
        ValueError: range variables should be distinct, but there are duplicates
    """
    if transformation is not None:
        params=None
        from sage.symbolic.callable import is_CallableSymbolicExpression
        # First, determine the parameters for f (from the first item of urange
        # and vrange, preferably).
        if len(urange) == 3 and len(vrange) == 3:
            params = (urange[0], vrange[0])
        elif is_CallableSymbolicExpression(f):
            params = f.variables()

        from sage.modules.vector_callable_symbolic_dense import Vector_callable_symbolic_dense
        if isinstance(transformation, (tuple, list,Vector_callable_symbolic_dense)):
            if len(transformation)==3:
                if params is None:
                    raise ValueError("must specify independent variable names in the ranges when using generic transformation")
                indep_vars = params
            elif len(transformation)==4:
                indep_vars = transformation[3]
                transformation = transformation[0:3]
            else:
                raise ValueError("unknown transformation type")
            # find out which variable is the function variable by
            # eliminating the parameter variables.
            all_vars = set(sum([list(s.variables()) for s in transformation],[]))
            dep_var=all_vars - set(indep_vars)
            if len(dep_var)==1:
                dep_var = dep_var.pop()
                transformation = _ArbitraryCoordinates(transformation, dep_var, indep_vars)
            else:
                raise ValueError("unable to determine the function variable in the transform")

        if isinstance(transformation, _Coordinates):
            R = transformation.to_cartesian(f, params)
            return parametric_plot3d.parametric_plot3d(R, urange, vrange, **kwds)
        else:
            raise ValueError('unknown transformation type')
    elif adaptive:
        P = plot3d_adaptive(f, urange, vrange, **kwds)
    else:
        u=fast_float_arg(0)
        v=fast_float_arg(1)
        P=parametric_plot3d.parametric_plot3d((u,v,f), urange, vrange, **kwds)
    P.frame_aspect_ratio([1.0,1.0,0.5])
    return P