示例#1
0
def test_common_keywords():
    # TODO: here I didn't test axis_center, autoscale, margin
    kw = dict(
        title="a",
        xlabel="x",
        ylabel="y",
        zlabel="z",
        aspect="equal",
        grid=False,
        xscale="log",
        yscale="log",
        zscale="log",
        legend=True,
        xlim=(-1, 1),
        ylim=(-2, 2),
        zlim=(-3, 3),
        size=(5, 10),
        backend=BB,
    )
    p = Plot(**kw)
    assert p.title == "a"
    assert p.xlabel == "x"
    assert p.ylabel == "y"
    assert p.zlabel == "z"
    assert p.aspect == "equal"
    assert p.grid == False
    assert p.xscale == "log"
    assert p.yscale == "log"
    assert p.zscale == "log"
    assert p.legend == True
    assert p.xlim == (-1, 1)
    assert p.ylim == (-2, 2)
    assert p.zlim == (-3, 3)
    assert p.size == (5, 10)
    assert p._kwargs == kw
示例#2
0
def plot_contour(*args, show=True, **kwargs):
    """
    Draws contour plot of a function

    Usage
    =====

    Single plot

    ``plot_contour(expr, range_x, range_y, **kwargs)``

    If the ranges are not specified, then a default range of (-10, 10) is used.

    Multiple plot with the same range.

    ``plot_contour(expr1, expr2, range_x, range_y, **kwargs)``

    If the ranges are not specified, then a default range of (-10, 10) is used.

    Multiple plots with different ranges.

    ``plot_contour((expr1, range_x, range_y), (expr2, range_x, range_y), ..., **kwargs)``

    Ranges have to be specified for every expression.

    Default range may change in the future if a more advanced default range
    detection algorithm is implemented.

    Arguments
    =========

    ``expr`` : Expression representing the function along x.

    ``range_x``: (x, 0, 5), A 3-tuple denoting the range of the x
    variable.

    ``range_y``: (y, 0, 5), A 3-tuple denoting the range of the y
     variable.

    Keyword Arguments
    =================

    Arguments for ``ContourSeries`` class:

    ``n1``: int. The x range is sampled uniformly at ``n1`` of points.

    ``n2``: int. The y range is sampled uniformly at ``n2`` of points.

    ``n``: int. The x and y ranges are sampled uniformly at ``n`` of points.
    It overrides ``n1`` and ``n2``.

    Arguments for ``Plot`` class:

    ``title`` : str. Title of the plot.
    ``size`` : (float, float), optional
        A tuple in the form (width, height) in inches to specify the size of
        the overall figure. The default value is set to ``None``, meaning
        the size will be set by the default backend.

    See Also
    ========

    Plot, ContourSeries

    """
    from spb.defaults import TWO_D_B

    args = _plot_sympify(args)
    kwargs.setdefault("backend", TWO_D_B)
    kwargs = _set_discretization_points(kwargs, ContourSeries)
    plot_expr = _check_arguments(args, 1, 2)
    series = [ContourSeries(*arg, **kwargs) for arg in plot_expr]
    xlabel = series[0].var_x.name
    ylabel = series[0].var_y.name
    kwargs.setdefault("xlabel", xlabel)
    kwargs.setdefault("ylabel", ylabel)
    plot_contours = Plot(*series, **kwargs)
    if show:
        plot_contours.show()
    return plot_contours
示例#3
0
def plot3d_parametric_surface(*args, show=True, **kwargs):
    """
    Plots a 3D parametric surface plot.

    Explanation
    ===========

    Single plot.

    ``plot3d_parametric_surface(expr_x, expr_y, expr_z, range_u, range_v, **kwargs)``

    If the ranges is not specified, then a default range of (-10, 10) is used.

    Multiple plots.

    ``plot3d_parametric_surface((expr_x, expr_y, expr_z, range_u, range_v), ..., **kwargs)``

    Ranges have to be specified for every expression.

    Default range may change in the future if a more advanced default range
    detection algorithm is implemented.

    Arguments
    =========

    ``expr_x``: Expression representing the function along ``x``.

    ``expr_y``: Expression representing the function along ``y``.

    ``expr_z``: Expression representing the function along ``z``.

    ``range_u``: ``(u, 0, 5)``,  A 3-tuple denoting the range of the ``u``
    variable.

    ``range_v``: ``(v, 0, 5)``,  A 3-tuple denoting the range of the v
    variable.

    Keyword Arguments
    =================

    Arguments for ``ParametricSurfaceSeries`` class:

    ``n1``: int. The ``u`` range is sampled uniformly at ``n1`` of points

    ``n2``: int. The ``v`` range is sampled uniformly at ``n2`` of points

    ``n``: int. The u and v ranges are sampled uniformly at ``n`` of points.
    It overrides ``n1`` and ``n2``.

    Arguments for ``Plot`` class:

    ``title`` : str. Title of the plot.
    ``size`` : (float, float), optional
    A tuple in the form (width, height) in inches to specify the size of the
    overall figure. The default value is set to ``None``, meaning the size will
    be set by the default backend.

    Examples
    ========

    .. plot::
       :context: reset
       :format: doctest
       :include-source: True

       >>> from sympy import symbols, cos, sin
       >>> from sympy.plotting import plot3d_parametric_surface
       >>> u, v = symbols('u v')

    Single plot.

    .. plot::
       :context: close-figs
       :format: doctest
       :include-source: True

       >>> plot3d_parametric_surface(cos(u + v), sin(u - v), u - v,
       ...     (u, -5, 5), (v, -5, 5))
       Plot object containing:
       [0]: parametric cartesian surface: (cos(u + v), sin(u - v), u - v) for u over (-5.0, 5.0) and v over (-5.0, 5.0)


    See Also
    ========

    Plot, ParametricSurfaceSeries

    """
    from spb.defaults import THREE_D_B

    args = _plot_sympify(args)
    kwargs.setdefault("backend", THREE_D_B)
    kwargs = _set_discretization_points(kwargs, ParametricSurfaceSeries)
    plot_expr = _check_arguments(args, 3, 2)
    kwargs.setdefault("xlabel", "x")
    kwargs.setdefault("ylabel", "y")
    kwargs.setdefault("zlabel", "z")
    series = [ParametricSurfaceSeries(*arg, **kwargs) for arg in plot_expr]
    plots = Plot(*series, **kwargs)
    if show:
        plots.show()
    return plots
示例#4
0
def plot3d(*args, show=True, **kwargs):
    """
    Plots a 3D surface plot.

    Usage
    =====

    Single plot

    ``plot3d(expr, range_x, range_y, **kwargs)``

    If the ranges are not specified, then a default range of (-10, 10) is used.

    Multiple plot with the same range.

    ``plot3d(expr1, expr2, range_x, range_y, **kwargs)``

    If the ranges are not specified, then a default range of (-10, 10) is used.

    Multiple plots with different ranges.

    ``plot3d((expr1, range_x, range_y), (expr2, range_x, range_y), ..., **kwargs)``

    Ranges have to be specified for every expression.

    Default range may change in the future if a more advanced default range
    detection algorithm is implemented.

    Arguments
    =========

    ``expr`` : Expression representing the function along x.

    ``range_x``: (x, 0, 5), A 3-tuple denoting the range of the x
    variable.

    ``range_y``: (y, 0, 5), A 3-tuple denoting the range of the y
     variable.

    Keyword Arguments
    =================

    Arguments for ``SurfaceOver2DRangeSeries`` class:

    ``n1``: int. The x range is sampled uniformly at ``n1`` of points.

    ``n2``: int. The y range is sampled uniformly at ``n2`` of points.

    ``n``: int. The x and y ranges are sampled uniformly at ``n`` of points.
    It overrides ``n1`` and ``n2``.

    Arguments for ``Plot`` class:

    ``title`` : str. Title of the plot.
    ``size`` : (float, float), optional
    A tuple in the form (width, height) in inches to specify the size of the
    overall figure. The default value is set to ``None``, meaning the size will
    be set by the default backend.

    Examples
    ========

    .. plot::
       :context: reset
       :format: doctest
       :include-source: True

       >>> from sympy import symbols
       >>> from sympy.plotting import plot3d
       >>> x, y = symbols('x y')

    Single plot

    .. plot::
       :context: close-figs
       :format: doctest
       :include-source: True

       >>> plot3d(x*y, (x, -5, 5), (y, -5, 5))
       Plot object containing:
       [0]: cartesian surface: x*y for x over (-5.0, 5.0) and y over (-5.0, 5.0)


    Multiple plots with same range

    .. plot::
       :context: close-figs
       :format: doctest
       :include-source: True

       >>> plot3d(x*y, -x*y, (x, -5, 5), (y, -5, 5))
       Plot object containing:
       [0]: cartesian surface: x*y for x over (-5.0, 5.0) and y over (-5.0, 5.0)
       [1]: cartesian surface: -x*y for x over (-5.0, 5.0) and y over (-5.0, 5.0)


    Multiple plots with different ranges.

    .. plot::
       :context: close-figs
       :format: doctest
       :include-source: True

       >>> plot3d((x**2 + y**2, (x, -5, 5), (y, -5, 5)),
       ...     (x*y, (x, -3, 3), (y, -3, 3)))
       Plot object containing:
       [0]: cartesian surface: x**2 + y**2 for x over (-5.0, 5.0) and y over (-5.0, 5.0)
       [1]: cartesian surface: x*y for x over (-3.0, 3.0) and y over (-3.0, 3.0)


    See Also
    ========

    Plot, SurfaceOver2DRangeSeries

    """
    from spb.defaults import THREE_D_B

    args = _plot_sympify(args)
    kwargs.setdefault("backend", THREE_D_B)
    kwargs = _set_discretization_points(kwargs, SurfaceOver2DRangeSeries)
    series = []
    plot_expr = _check_arguments(args, 1, 2)
    series = [SurfaceOver2DRangeSeries(*arg, **kwargs) for arg in plot_expr]
    xlabel = series[0].var_x.name
    ylabel = series[0].var_y.name
    kwargs.setdefault("xlabel", xlabel)
    kwargs.setdefault("ylabel", ylabel)
    kwargs.setdefault("zlabel", "f(%s, %s)" % (xlabel, ylabel))
    plots = Plot(*series, **kwargs)
    if show:
        plots.show()
    return plots
示例#5
0
def plot3d_parametric_line(*args, show=True, **kwargs):
    """
    Plots a 3D parametric line plot.

    Usage
    =====

    Single plot:

    ``plot3d_parametric_line(expr_x, expr_y, expr_z, range, label, **kwargs)``

    If the range is not specified, then a default range of (-10, 10) is used.

    Multiple plots.

    ``plot3d_parametric_line((expr_x, expr_y, expr_z, range, label), ..., **kwargs)``

    Ranges have to be specified for every expression.

    Default range may change in the future if a more advanced default range
    detection algorithm is implemented.

    Arguments
    =========

    ``expr_x`` : Expression representing the function along x.

    ``expr_y`` : Expression representing the function along y.

    ``expr_z`` : Expression representing the function along z.

    ``range``: ``(u, 0, 5)``, A 3-tuple denoting the range of the parameter
    variable.

    ``label`` : An optional string denoting the label of the expression
        to be visualized on the legend. If not provided, the label will be the
        string representation of the expression.

    Keyword Arguments
    =================

    Arguments for ``Parametric3DLineSeries`` class.

    ``n``: The range is uniformly sampled at ``n`` number of points.

    Arguments for ``Plot`` class.

    ``title`` : str. Title of the plot.

    ``size`` : (float, float), optional
        A tuple in the form (width, height) in inches to specify the size of
        the overall figure. The default value is set to ``None``, meaning
        the size will be set by the default backend.

    Examples
    ========

    .. plot::
       :context: reset
       :format: doctest
       :include-source: True

       >>> from sympy import symbols, cos, sin
       >>> from sympy.plotting import plot3d_parametric_line
       >>> u = symbols('u')

    Single plot.

    .. plot::
       :context: close-figs
       :format: doctest
       :include-source: True

       >>> plot3d_parametric_line(cos(u), sin(u), u, (u, -5, 5))
       Plot object containing:
       [0]: 3D parametric cartesian line: (cos(u), sin(u), u) for u over (-5.0, 5.0)


    Multiple plots with different ranges and custom labels.

    .. plot::
       :context: close-figs
       :format: doctest
       :include-source: True

       >>> plot3d_parametric_line((cos(u), sin(u), u, (u, -5, 5), "a"),
       ...     (sin(u), u**2, u, (u, -3, 3), "b"), legend=True)
       Plot object containing:
       [0]: 3D parametric cartesian line: (cos(u), sin(u), u) for u over (-5.0, 5.0)
       [1]: 3D parametric cartesian line: (sin(u), u**2, u) for u over (-3.0, 3.0)


    See Also
    ========

    Plot, Parametric3DLineSeries

    """
    from spb.defaults import THREE_D_B

    args = _plot_sympify(args)
    kwargs.setdefault("backend", THREE_D_B)
    kwargs = _set_discretization_points(kwargs, Parametric3DLineSeries)
    series = []
    plot_expr = _check_arguments(args, 3, 1)
    series = [Parametric3DLineSeries(*arg, **kwargs) for arg in plot_expr]
    kwargs.setdefault("xlabel", "x")
    kwargs.setdefault("ylabel", "y")
    kwargs.setdefault("zlabel", "z")
    plots = Plot(*series, **kwargs)
    if show:
        plots.show()
    return plots
示例#6
0
def plot_parametric(*args, show=True, **kwargs):
    """
    Plots a 2D parametric curve.

    Parameters
    ==========

    args
        Common specifications are:

        - Plotting a single parametric curve with a range
            ``plot_parametric(expr_x, expr_y, range)``
        - Plotting multiple parametric curves with the same range
            ``plot_parametric((expr_x, expr_y), ..., range)``
        - Plotting multiple parametric curves with different ranges
            ``plot_parametric((expr_x, expr_y, range), ...)``
        - Plotting multiple parametric curves with different ranges and
            custom labels
            ``plot_parametric((expr_x, expr_y, range, label), ...)``

        ``expr_x`` is the expression representing $x$ component of the
        parametric function.

        ``expr_y`` is the expression representing $y$ component of the
        parametric function.

        ``range`` is a 3-tuple denoting the parameter symbol, start and
        stop. For example, ``(u, 0, 5)``. If the range is not specified, then
        a default range of (-10, 10) is used.

        However, if the arguments are specified as
        ``(expr_x, expr_y, range), ...``, you must specify the ranges
        for each expressions manually.

        Default range may change in the future if a more advanced
        algorithm is implemented.

        ``label`` : An optional string denoting the label of the expression
        to be visualized on the legend. If not provided, the label will be the
        string representation of the expression.

    adaptive : bool, optional
        Specifies whether to use the adaptive sampling or not.

        The default value is set to ``True``. Set adaptive to ``False``
        and specify ``n`` if uniform sampling is required.

    depth :  int, optional
        The recursion depth of the adaptive algorithm. A depth of
        value $n$ samples a maximum of $2^n$ points.

    n : int, optional
        Used when the ``adaptive`` flag is set to ``False``.

        Specifies the number of the points used for the uniform
        sampling.

    xlabel : str, optional
        Label for the x-axis.

    ylabel : str, optional
        Label for the y-axis.

    xscale : 'linear' or 'log', optional
        Sets the scaling of the x-axis.

    yscale : 'linear' or 'log', optional
        Sets the scaling of the y-axis.

    axis_center : (float, float), optional
        Tuple of two floats denoting the coordinates of the center or
        {'center', 'auto'}

    xlim : (float, float), optional
        Denotes the x-axis limits, ``(min, max)```.

    ylim : (float, float), optional
        Denotes the y-axis limits, ``(min, max)```.

    size : (float, float), optional
        A tuple in the form (width, height) in inches to specify the size of
        the overall figure. The default value is set to ``None``, meaning
        the size will be set by the default backend.

    Examples
    ========

    .. plot::
       :context: reset
       :format: doctest
       :include-source: True

       >>> from sympy import symbols, cos, sin
       >>> from sympy.plotting import plot_parametric
       >>> u = symbols('u')

    A parametric plot with a single expression:

    .. plot::
       :context: close-figs
       :format: doctest
       :include-source: True

       >>> plot_parametric((cos(u), sin(u)), (u, -5, 5))
       Plot object containing:
       [0]: parametric cartesian line: (cos(u), sin(u)) for u over (-5.0, 5.0)

    A parametric plot with multiple expressions with the same range:

    .. plot::
       :context: close-figs
       :format: doctest
       :include-source: True

       >>> plot_parametric((cos(u), sin(u)), (u, cos(u)), (u, -10, 10))
       Plot object containing:
       [0]: parametric cartesian line: (cos(u), sin(u)) for u over (-10.0, 10.0)
       [1]: parametric cartesian line: (u, cos(u)) for u over (-10.0, 10.0)

    A parametric plot with multiple expressions with different ranges and
    custom labels for each curve:

    .. plot::
       :context: close-figs
       :format: doctest
       :include-source: True

       >>> plot_parametric((cos(u), sin(u), (u, -5, 5), "a"),
       ...     (cos(u), u, "b"))
       Plot object containing:
       [0]: parametric cartesian line: (cos(u), sin(u)) for u over (-5.0, 5.0)
       [1]: parametric cartesian line: (cos(u), u) for u over (-10.0, 10.0)

    Notes
    =====

    The plotting uses an adaptive algorithm which samples recursively to
    accurately plot the curve. The adaptive algorithm uses a random point
    near the midpoint of two points that has to be further sampled.
    Hence, repeating the same plot command can give slightly different
    results because of the random sampling.

    See Also
    ========

    Plot, Parametric2DLineSeries
    """
    from spb.defaults import TWO_D_B

    args = _plot_sympify(args)
    series = []
    kwargs.setdefault("backend", TWO_D_B)
    kwargs = _set_discretization_points(kwargs, Parametric2DLineSeries)
    plot_expr = _check_arguments(args, 2, 1)
    series = [Parametric2DLineSeries(*arg, **kwargs) for arg in plot_expr]
    plots = Plot(*series, **kwargs)
    if show:
        plots.show()
    return plots
示例#7
0
def plot(*args, show=True, **kwargs):
    """Plots a function of a single variable as a curve.

    Parameters
    ==========

    args :
        The first argument is the expression representing the function
        of single variable to be plotted.

        The next argument is a 3-tuple denoting the range of the free
        variable. e.g. ``(x, 0, 5)``

        The last optional argument is a string denoting the label of the
        expression to be visualized on the legend. If not provided, the label
        will be the string representation of the expression.

        Typical usage examples are in the followings:

        - Plotting a single expression with a single range.
            ``plot(expr, range, **kwargs)``
        - Plotting a single expression with the default range (-10, 10).
            ``plot(expr, **kwargs)``
        - Plotting multiple expressions with a single range.
            ``plot(expr1, expr2, ..., range, **kwargs)``
        - Plotting multiple expressions with multiple ranges.
            ``plot((expr1, range1), (expr2, range2), ..., **kwargs)``
        - Plotting multiple expressions with multiple ranges and custom labels.
            ``plot((expr1, range1, label1), (expr2, range2, label2), ..., legend=True, **kwargs)``

        It is best practice to specify range explicitly because default
        range may change in the future if a more advanced default range
        detection algorithm is implemented.

    adaptive : bool, optional
        The default value is set to ``True``. Set adaptive to ``False``
        and specify ``n`` if uniform sampling is required.

        The plotting uses an adaptive algorithm which samples
        recursively to accurately plot. The adaptive algorithm uses a
        random point near the midpoint of two points that has to be
        further sampled. Hence the same plots can appear slightly
        different.

    axis_center : (float, float), optional
        Tuple of two floats denoting the coordinates of the center or
        {'center', 'auto'}. Only available with MatplotlibBackend.

    depth : int, optional
        Recursion depth of the adaptive algorithm. A depth of value
        ``n`` samples a maximum of `2^{n}` points.

        If the ``adaptive`` flag is set to ``False``, this will be
        ignored.

    detect_poles : boolean
            Chose whether to detect and correctly plot poles. 
            Defaulto to False. This improve detection, increase the number of 
            discretization points and/or change the value of `eps`.

    eps : float
        An arbitrary small value used by the `detect_poles` algorithm.
        Default value to 0.1. Before changing this value, it is better to
        increase the number of discretization points.

    n : int, optional
        Used when the ``adaptive`` is set to ``False``. The function
        is uniformly sampled at ``n`` number of points.

        If the ``adaptive`` flag is set to ``True``, this will be
        ignored.

    only_integers : boolean, optional
        Default to False. If True, discretize the domain with integer numbers.
        This can be useful to plot sums.

    polar : boolean
        Default to False. If True, generate a polar plot of a curve with radius
        `expr` as a function of the range

    show : bool, optional
        The default value is set to ``True``. Set show to ``False`` and
        the function will not display the plot. The returned instance of
        the ``Plot`` class can then be used to save or display the plot
        by calling the ``save()`` and ``show()`` methods respectively.

    size : (float, float), optional
        A tuple in the form (width, height) in inches to specify the size of
        the overall figure. The default value is set to ``None``, meaning
        the size will be set by the default backend.

    steps : boolean, optional
        Default to False. If True, connects consecutive points with steps 
        rather than straight segments.

    title : str, optional
        Title of the plot. It is set to the latex representation of
        the expression, if the plot has only one expression.

    xlabel : str, optional
        Label for the x-axis.

    ylabel : str, optional
        Label for the y-axis.

    xscale : 'linear' or 'log', optional
        Sets the scaling of the x-axis.

    yscale : 'linear' or 'log', optional
        Sets the scaling of the y-axis.

    xlim : (float, float), optional
        Denotes the x-axis limits, ``(min, max)```.

    ylim : (float, float), optional
        Denotes the y-axis limits, ``(min, max)```.


    Examples
    ========

    .. plot::
       :context: close-figs
       :format: doctest
       :include-source: True

       >>> from sympy import symbols
       >>> from sympy.plotting import plot
       >>> x = symbols('x')

    Single Plot

    .. plot::
       :context: close-figs
       :format: doctest
       :include-source: True

       >>> plot(x**2, (x, -5, 5))
       Plot object containing:
       [0]: cartesian line: x**2 for x over (-5.0, 5.0)

    Multiple plots with single range.

    .. plot::
       :context: close-figs
       :format: doctest
       :include-source: True

       >>> plot(x, x**2, x**3, (x, -5, 5))
       Plot object containing:
       [0]: cartesian line: x for x over (-5.0, 5.0)
       [1]: cartesian line: x**2 for x over (-5.0, 5.0)
       [2]: cartesian line: x**3 for x over (-5.0, 5.0)

    Multiple plots with different ranges and custom labels.

    .. plot::
       :context: close-figs
       :format: doctest
       :include-source: True

       >>> plot((x**2, (x, -6, 6), "$f_{1}$"), (x, (x, -5, 5), "f2"), legend=True)
       Plot object containing:
       [0]: cartesian line: x**2 for x over (-6.0, 6.0)
       [1]: cartesian line: x for x over (-5.0, 5.0)

    No adaptive sampling.

    .. plot::
       :context: close-figs
       :format: doctest
       :include-source: True

       >>> plot(x**2, adaptive=False, n=400)
       Plot object containing:
       [0]: cartesian line: x**2 for x over (-10.0, 10.0)

    Polar plot:

    .. code-block:: python
        plot(1 + sin(10 * x) / 10, (x, 0, 2 * pi), polar=True, aspect="equal")

    See Also
    ========

    Plot, LineOver1DRangeSeries

    """
    from spb.defaults import TWO_D_B

    args = _plot_sympify(args)
    free = set()
    for a in args:
        if isinstance(a, Expr):
            free |= a.free_symbols
            if len(free) > 1:
                raise ValueError("The same variable should be used in all "
                                 "univariate expressions being plotted.")
    x = free.pop() if free else Symbol("x")
    kwargs.setdefault("backend", TWO_D_B)
    kwargs.setdefault("xlabel", x.name)
    kwargs.setdefault("ylabel", "f(%s)" % x.name)
    kwargs = _set_discretization_points(kwargs, LineOver1DRangeSeries)
    series = []
    plot_expr = _check_arguments(args, 1, 1)
    series = _build_line_series(*plot_expr, **kwargs)

    plots = Plot(*series, **kwargs)
    if show:
        plots.show()
    return plots
示例#8
0
def plotgrid(*args, **kwargs):
    """Combine multiple plots into a grid-like layout.
    This function has two modes of operation, depending on the input arguments.
    Make sure to read the examples to fully understand them.

    Parameters
    ==========
        args : sequence
            A sequence of aldready created plots. This, in combination with
            `nr` and `nc` represents the first mode of operation, where a basic
            grid with (nc * nr) subplots will be created.

    Keyword Arguments
    =================
        nr, nc : int
            Number of rows and columns.
            By default, `nc = 1` and `nr = -1`: this will create as many rows as
            necessary, and a single column.
            If we set `nc = 1` and `nc = -1`, it will create as many column as
            necessary, and a single row.

        gs : dict
            A dictionary mapping Matplotlib's GridSpect objects to the plots.
            The keys represent the cells of the layout. Each cell will host the
            associated plot.
            This represents the second mode of operation, as it allows to create
            more complicated layouts.
            NOTE: all plots must be instances of MatplotlibBackend!

    Returns
    =======
        Depending on the types of plots, this function returns either:
        * None: if all plots are instances of MatplotlibBackend.
        * an instance of holoviz's panel GridSpec, which will be rendered on
        Jupyter Notebook when mixed types of plots are received or when all the
        plots are not instances of MatplotlibBackend. Read the following
        documentation page to get more information:
        https://panel.holoviz.org/reference/layouts/GridSpec.html


    Examples
    ========

    First mode of operation with MatplotlibBackends:

    .. code-block:: python
        from sympy import *
        from spb.backends.matplotlib import MB
        from spb import *

        x, y, z = symbols("x, y, z")
        p1 = plot(sin(x), backend=MB, show=False)
        p2 = plot(tan(x), backend=MB, detect_poles=True, show=False)
        p3 = plot(exp(-x), backend=MB, show=False)
        plotgrid(p1, p2, p3)

    First mode of operation with different backends. Try this on a Jupyter
    Notebook. Note that Matplotlib as been integrated as a picture, thus it
    loses its interactivity.

    .. code-block:: python
        p1 = plot(sin(x), backend=MB, show=False)
        p2 = plot(tan(x), backend=MB, detect_poles=True, show=False)
        p3 = plot(exp(-x), backend=MB, show=False)
        plotgrid(p1, p2, p3, nr=1, nc=3)

    Second mode of operation: using Matplotlib GridSpec and all plots are
    instances of MatplotlibBackend:

    .. code-block:: python
        from matplotlib.gridspec import GridSpec

        p1 = plot(sin(x), cos(x), show=False, backend=MB)
        p2 = plot_contour(cos(x**2 + y**2), (x, -3, 3), (y, -3, 3), show=False, backend=BB)
        p3 = complex_plot(sqrt(x), show=False, backend=PB)
        p4 = vector_plot(Matrix([-y, x]), (x, -5, 5), (y, -5, 5), show=False, backend=MB)
        p5 = complex_plot(gamma(z), (z, -3-3*I, 3+3*I), show=False, backend=MB)

        gs = GridSpec(3, 3)
        mapping = {
            gs[0, :1]: p1,
            gs[1, :1]: p2,
            gs[2:, :1]: p3,
            gs[2:, 1:]: p4,
            gs[0:2, 1:]: p5,
        }
        plotgrid(gs=mapping)

    """
    gs = kwargs.get("gs", None)

    if gs is None:
        # default layout: 1 columns, as many rows as needed
        nr = kwargs.get("nr", -1)
        nc = kwargs.get("nc", 1)
        nr, nc = _nrows_ncols(nr, nc, len(args))

        if all(isinstance(a, MB) for a in args):
            fig, ax = plt.subplots(nr, nc)
            ax = ax.flatten()

            c = 0
            for i in range(nr):
                for j in range(nc):
                    if c < len(args):
                        kw = args[c]._kwargs
                        kw["backend"] = MB
                        kw["fig"] = fig
                        kw["ax"] = ax[c]
                        p = Plot(*args[c].series, **kw)
                        p.process_series()
                        c += 1
        else:
            fig = pn.GridSpec(sizing_mode="stretch_width")
            c = 0
            for i in range(nr):
                for j in range(nc):
                    if c < len(args):
                        p = args[c]
                        c += 1
                        if isinstance(p, MB) and (not hasattr(p, "ax")):
                            # if the MatplotlibBackend was created without
                            # showing it
                            p.process_series()
                        fig[i, j] = p.fig if not isinstance(p, MB) else p.fig[0]

    else:
        fig = plt.figure()
        axes = dict()
        for gs, p in gs.items():
            axes[fig.add_subplot(gs)] = p

        for ax, p in axes.items():
            kw = p._kwargs
            kw["backend"] = MB
            kw["fig"] = fig
            kw["ax"] = ax
            newplot = Plot(*p.series, **kw)
            newplot.process_series()

    if isinstance(fig, plt.Figure):
        fig.tight_layout()
        fig.show()
    else:
        return fig
示例#9
0
def test_K3DBackend():
    from k3d.objects import Mesh, Line, Vectors
    from matplotlib.colors import ListedColormap

    assert hasattr(KB, "colorloop")
    assert isinstance(KB.colorloop, (list, tuple, ListedColormap))
    assert hasattr(KB, "colormaps")
    assert isinstance(KB.colormaps, (list, tuple))

    series = [UnsupportedSeries()]
    raises(NotImplementedError, lambda: Plot(*series, backend=KBchild2))

    ### Setting custom color loop

    assert len(KBchild1.colorloop.colors) != len(KBchild2.colorloop)
    _p1 = p13(KBchild1)
    _p2 = p13(KBchild2)
    assert len(_p1.series) == len(_p2.series)
    f1 = _p1.fig
    f2 = _p2.fig
    assert all([isinstance(t, Mesh) for t in f1.objects])
    assert all([isinstance(t, Mesh) for t in f2.objects])
    # there are 6 unique colors in _p1 and 3 unique colors in _p2
    assert len(set([o.color for o in f1.objects])) == 6
    assert len(set([o.color for o in f2.objects])) == 3

    # ### test for line_kw, surface_kw, quiver_kw, stream_kw: they should override
    # ### defualt settings.

    # K3D doesn't support 2D plots
    raises(NotImplementedError, lambda: p2(KBchild1, line_kw=dict(line_color="red")))
    raises(NotImplementedError, lambda: p3(KBchild1, line_kw=dict(line_color="red")))

    p = p4(KBchild1, line_kw=dict(color=16711680))
    assert len(p.series) == 1
    f = p.fig
    assert len(f.objects) == 1
    assert isinstance(f.objects[0], Line)
    assert f.objects[0].color == 16711680
    assert f.objects[0].name is None

    # use_cm=False will force to apply a default solid color to the mesh.
    # Here, I override that solid color with a custom color.
    p = p5(KBchild1, surface_kw=dict(color=16711680))
    assert len(p.series) == 1
    f = p.fig
    assert len(f.objects) == 1
    assert isinstance(f.objects[0], Mesh)
    assert f.objects[0].color == 16711680
    assert f.objects[0].name is None

    # K3D doesn't support 2D plots
    raises(NotImplementedError, lambda: p6(KBchild1, contour_kw=dict()))
    raises(
        NotImplementedError, lambda: p7(KBchild1, quiver_kw=dict(), contour_kw=dict())
    )
    raises(
        NotImplementedError, lambda: p8(KBchild1, stream_kw=dict(), contour_kw=dict())
    )

    p = p9(KBchild1, quiver_kw=dict(scale=0.5, color=16711680))
    assert len(p.series) == 1
    f = p.fig
    assert len(f.objects) == 1
    assert isinstance(f.objects[0], Vectors)
    assert all([c == 16711680 for c in f.objects[0].colors])

    p = p10(KBchild1, stream_kw=dict(color=16711680))
    assert len(p.series) == 1
    f = p.fig
    assert len(f.objects) == 1
    assert isinstance(f.objects[0], Line)
    assert f.objects[0].color == 16711680

    # K3D doesn't support 2D plots
    raises(NotImplementedError, lambda: p11(KBchild1, contour_kw=dict()))
    raises(NotImplementedError, lambda: p12(KBchild1, contour_kw=dict()))
    raises(NotImplementedError, lambda: p14(KBchild1, line_kw=dict()))
    raises(NotImplementedError, lambda: p15(KBchild1, line_kw=dict()))
    raises(NotImplementedError, lambda: p16(KBchild1, contour_kw=dict()))

    p = p17(KBchild1, surface_kw=dict())
    assert len(p.series) == 1
    f = p.fig
    assert len(f.objects) == 1
    assert isinstance(f.objects[0], Mesh)
    assert f.objects[0].name is None
示例#10
0
def complex_plot(*args, show=True, **kwargs):
    """Plot complex numbers or complex functions. By default, the aspect ratio
    of the plot is set to ``aspect="equal"``.

    Depending on the provided expression, this function will produce different
    types of plots:
    * list of complex numbers: creates a scatter plot.
    * function of 1 variable over a real range:
        1. line plot separating the real and imaginary parts.
        2. line plot of the modulus of the complex function colored by its
            argument, if `absarg=True`.
        3. line plot of the modulus and the argument, if `abs=True, arg=True`.
    * function of 2 variables over 2 real ranges:
        1. By default, a surface plot of the real part is created.
        2. By toggling `real=True, imag=True, abs=True` we can create surface
            plots of the real, imaginary part or the absolute value.
    * complex function over a complex range:
        1. domain coloring plot.
        2. 3D plot of the modulus colored by the argument, if `threed=True`.
        3. 3D plot of the real and imaginary part.

    Explore the example below to better understand how to use it.

    Arguments
    =========
        expr : Expr
            Represent the complex number or complex function to be plotted.

        range : 3-element tuple
            Denotes the range of the variables. For example:
            * (z, -5, 5): plot a line from complex point (-5 + 0*I) to (5 + 0*I)
            * (z, -5 + 2*I, 5 + 2*I): plot a line from complex point (-5 + 2*I)
                to (5 + 2 * I). Note the same imaginary part for the start/end
                point.
            * (z, -5 - 3*I, 5 + 3*I): domain coloring plot of the complex
                function over the specified domain.

        label : str
            The name of the complex function to be eventually shown on the
            legend. If none is provided, the string representation of the
            function will be used.

        To specify multiple complex functions, wrap them into a tuple.
        Refer to the examples to learn more.

    Keyword Arguments
    =================

        absarg : boolean
            If True, plot the modulus of the complex function colored by its
            argument. If False, separately plot the real and imaginary parts.
            Default to False.

        abs : boolean
            If True, and if the provided range is a real segment, plot the
            modulus of the complex function. Default to False.

        adaptive : boolean
            Attempt to create line plots by using an adaptive algorithm.
            Default to True. If `absarg=True`, the function will automatically
            switch to `adaptive=False`, using a uniformly-spaced grid.

        arg : boolean
            If True, and if the provided range is a real segment, plot the
            argument of the complex function. Default to False.

        depth : int
            Controls the smootheness of the overall evaluation. The higher
            the number, the smoother the function, the more memory will be
            used by the recursive procedure. Default value is 9.

        detect_poles : boolean
            Chose whether to detect and correctly plot poles. Defaulto to False.
            This improve detection, increase the number of discretization points
            and/or change the value of `eps`.

        eps : float
            An arbitrary small value used by the `detect_poles` algorithm.
            Default value to 0.1. Before changing this value, it is better to
            increase the number of discretization points.

        n1, n2 : int
            Number of discretization points in the real/imaginary-directions,
            respectively. For domain coloring plots (2D and 3D), default to 300.
            For line plots default to 1000.

        n : int
            Set the same number of discretization points in all directions.
            For domain coloring plots (2D and 3D), default to 300. For line
            plots default to 1000.

        real : boolean
            If True, and if the provided range is a real segment, plot the
            real part of the complex function.
            If a complex range is given and `threed=True`, plot a 3D
            representation of the real part. Default to False.

        imag : boolean
            If True, and if the provided range is a real segment, plot the
            imaginary part of the complex function.
            If a complex range is given and `threed=True`, plot a 3D
            representation of the imaginary part. Default to False.

        show : boolean
            Default to True, in which case the plot will be shown on the screen.

        threed : boolean
            Default to False. When True, it will plot a 3D representation of the
            absolute value of the complex function colored by its argument.

        use_cm : boolean
            If `absarg=True` and `use_cm=True` then plot the modulus of the
            complex function colored by its argument. If `use_cm=False`, plot
            the modulus of the complex function with a solid color.
            Default to True.

    Domain Coloring Arguments
    =========================

        coloring : str
            Default to "a". Chose between different coloring options:
            "a": standard domain coloring using HSV.
            "b": enhanced domain coloring using HSV, showing iso-modulus and
                is-phase lines.
            "c": enhanced domain coloring using HSV, showing iso-modulus lines.
            "d": enhanced domain coloring using HSV, showing iso-phase lines.
            "e": HSV color grading. Read the following article to understand it:
                https://www.codeproject.com/Articles/80641/Visualizing-Complex-Functions
            "f": domain coloring implemented by cplot:
                https://github.com/nschloe/cplot
                Use the following keywords to further customize the appearance:
                    `abs_scaling`: str
                        Default to "h-1". It can be used to adjust the use of
                        colors. h with a value less than 1.0 adds more color
                        which can help isolating the roots and poles (which are
                        still black and white, respectively). "h-0.0" ignores
                        the magnitude of f(z) completely. "arctan" is another
                        possible scaling.
                    `colorspace` : str
                        Default to "cam16". Can be set to "hsl" to get the
                        common fully saturated, vibrant colors.
                    `abs` and/or `args` : boolean
                        Set them to True to show contour lines for absolute
                        value and argument.
                    `levels` : (n_abs, n_arg)
                        Number of contour levels for the absolute value and the
                        argument.
                WARNING: if `abs=True` and/or `arg=True`, only MatplotlibBackend
                will be able to render the plot! Moreover, `iplot` won't be
                able to update these contour lines.
            "g": alternating black and white stripes corresponding to modulus.
            "h": alternating black and white stripes corresponding to phase.
            "i": alternating black and white stripes corresponding to real part.
            "j": alternating black and white stripes corresponding to imaginary
                part.
            "k": cartesian chessboard on the complex points space. The result
                will hide zeros.
            "l": polar Chessboard on the complex points space. The result will
                show conformality.

        alpha : float
            This parameter works when `coloring="f"`.
            Default to 1. Can be `0 <= alpha <= 1`. It adjust the use of colors.
            A value less than 1 adds more color which can help isolating the
            roots and poles (which are still black and white, respectively).
            alpha=0 ignores the magnitude of f(z) completely.

        colorspace : str
            This parameter works when `coloring="f"`.
            Default to `"cam16"`. Other options are `"cielab", "oklab", "hsl"`.
            It can be set to `"hsl"` to get the common fully saturated, vibrant
            colors. This is usually a bad idea since it creates artifacts which
            are not related with the underlying data.

        phaseres : int
            This parameter works when `coloring` is different from `"f"`.
            Default value to 20. It controls the number of iso-phase or
            iso-modulus lines.

    Examples
    ========

    Plot individual complex points:

    .. code-block:: python
        complex_plot(3 + 2 * I, 4 * I, 2, aspect="equal", legend=True)

    Plot two lists of complex points:

    .. code-block:: python
        z = symbols("z")
        expr1 = z * exp(2 * pi * I * z)
        expr2 = 2 * expr1
        l1 = [expr1.subs(z, t / 20) for t in range(20)]
        l2 = [expr2.subs(z, t / 20) for t in range(20)]
        complex_plot((l1, "f1"), (l2, "f2"), aspect="equal", legend=True)

    Plot the real and imaginary part of a function:

    .. code-block:: python
        z = symbols("z")
        complex_plot(sqrt(z), (z, -3, 3), legend=True)

    .. code-block:: python
        z = symbols("z")
        complex_plot((cos(z) + sin(I * z), "f"), (z, -2, 2), legend=True)

    Plot the modulus of a complex function colored by its magnitude:

    .. code-block:: python
        z = symbols("z")
        complex_plot((cos(z) + sin(I * z), "f"), (z, -2, 2), legend=True,
            absarg=True)

    Plot the modulus and the argument of a complex function:

    .. code-block:: python
        z = symbols("z")
        complex_plot((cos(z) + sin(I * z), "f"), (z, -2, 2), legend=True,
            abs=True, arg=True, real=False, imag=False)

    Plot the real and imaginary part of a function of two variables over two
    real ranges:

    .. code-block:: python
        x, y = symbols("x, y")
        complex_plot(sqrt(x*y), (x, -5, 5), (y, -5, 5), real=True, imag=True)

    Domain coloring plot. Note that it might be necessary to increase the number
    of discretization points in order to get a smoother plot:

    .. code-block:: python
        z = symbols("z")
        complex_plot(gamma(z), (z, -3 - 3*I, 3 + 3*I), coloring="b", n=500)

    3D plot of the absolute value of a complex function colored by its argument:

    .. code-block:: python
        z = symbols("z")
        complex_plot(gamma(z), (z, -3 - 3*I, 3 + 3*I), threed=True,
            legend=True, zlim=(-1, 6))

    3D plot of the real part a complex function:

    .. code-block:: python
        z = symbols("z")
        complex_plot(gamma(z), (z, -3 - 3*I, 3 + 3*I), threed=True,
            real=True)

    """
    args = _plot_sympify(args)
    kwargs = _set_discretization_points(kwargs, ComplexSeries)

    series = _build_series(*args, **kwargs)

    if "backend" not in kwargs:
        kwargs["backend"] = TWO_D_B
        if any(s.is_3Dsurface for s in series):
            kwargs["backend"] = THREE_D_B

    if all(
            isinstance(s, (SurfaceOver2DRangeSeries, InteractiveSeries))
            for s in series):
        # function of 2 variables
        if kwargs.get("xlabel", None) is None:
            kwargs["xlabel"] = str(series[0].var_x)
        if kwargs.get("ylabel", None) is None:
            kwargs["ylabel"] = str(series[0].var_y)
        # do not set anything for zlabel since it could be f(x,y) or
        # abs(f(x, y)) or something else
    elif all(not s.is_parametric for s in series):
        # when plotting real/imaginary or domain coloring/3D plots, the
        # horizontal axis is the real, the vertical axis is the imaginary
        if kwargs.get("xlabel", None) is None:
            kwargs["xlabel"] = "Re"
        if kwargs.get("ylabel", None) is None:
            kwargs["ylabel"] = "Im"
        if kwargs.get("zlabel", None) is None:
            kwargs["zlabel"] = "Abs"
    else:
        if kwargs.get("xlabel", None) is None:
            kwargs["xlabel"] = "Real"
        if kwargs.get("ylabel", None) is None:
            kwargs["ylabel"] = "Abs"

    if (kwargs.get("aspect", None) is None) and any(
            s.is_complex and s.is_domain_coloring for s in series):
        kwargs["aspect"] = "equal"

    p = Plot(*series, **kwargs)
    if show:
        p.show()
    return p
示例#11
0
def test_BokehBackend():
    from bokeh.models.glyphs import Line, MultiLine, Image, Segment, ImageRGBA
    from bokeh.plotting.figure import Figure

    assert hasattr(BB, "colorloop")
    assert isinstance(BB.colorloop, (list, tuple))
    assert hasattr(BB, "colormaps")
    assert isinstance(BB.colormaps, (list, tuple))

    series = [UnsupportedSeries()]
    raises(NotImplementedError, lambda: Plot(*series, backend=PB))

    ### Setting custom color loop

    assert len(BBchild.colorloop) != len(BB.colorloop)
    _p1 = p1(BB)
    _p2 = p1(BBchild)
    assert len(_p1.series) == len(_p2.series)
    f1 = _p1.fig
    f2 = _p2.fig
    assert all([isinstance(t.glyph, Line) for t in f1.renderers])
    assert all([isinstance(t.glyph, Line) for t in f2.renderers])
    # there are 6 unique colors in _p1 and 3 unique colors in _p2
    assert len(set([r.glyph.line_color for r in f1.renderers])) == 6
    assert len(set([r.glyph.line_color for r in f2.renderers])) == 3

    ### test for line_kw, surface_kw, quiver_kw, stream_kw: they should override
    ### defualt settings.

    p = p2(BB, line_kw=dict(line_color="red"))
    assert len(p.series) == 2
    f = p.fig
    assert isinstance(f, Figure)
    assert len(f.renderers) == 2
    assert isinstance(f.renderers[0].glyph, Line)
    assert f.legend[0].items[0].label["value"] == "sin(x)"
    assert f.renderers[0].glyph.line_color == "red"
    assert isinstance(f.renderers[1].glyph, Line)
    assert f.legend[0].items[1].label["value"] == "cos(x)"
    assert f.renderers[1].glyph.line_color == "red"
    assert f.legend[0].visible == True

    p = p3(BB, line_kw=dict(line_color="red"))
    assert len(p.series) == 1
    f = p.fig
    assert len(f.renderers) == 1
    assert isinstance(f.renderers[0].glyph, MultiLine)
    assert f.renderers[0].glyph.line_color == "red"

    # Bokeh doesn't support 3D plots
    raises(NotImplementedError, lambda: p4(BB, line_kw=dict(line_color="red")))
    raises(
        NotImplementedError,
        lambda: p5(BB, surface_kw=dict(colorscale=[[0, "cyan"], [1, "cyan"]])),
    )

    # Bokeh doesn't use contour_kw dictionary. Nothing to customize yet.
    p = p6(BB, contour_kw=dict())
    assert len(p.series) == 1
    f = p.fig
    assert len(f.renderers) == 1
    assert isinstance(f.renderers[0].glyph, Image)
    assert f.right[0].title == str(cos(x ** 2 + y ** 2))

    p = p7(BB, contour_kw=dict(), quiver_kw=dict(line_color="red"))
    assert len(p.series) == 2
    f = p.fig
    assert len(f.renderers) == 2
    assert isinstance(f.renderers[0].glyph, Image)
    assert isinstance(f.renderers[1].glyph, Segment)
    assert f.right[0].title == "Magnitude"
    assert f.renderers[1].glyph.line_color == "red"

    p = p8(BB, stream_kw=dict(line_color="red"), contour_kw=dict())
    assert len(p.series) == 2
    f = p.fig
    assert len(f.renderers) == 2
    assert isinstance(f.renderers[0].glyph, Image)
    assert isinstance(f.renderers[1].glyph, MultiLine)
    assert f.right[0].title == "x + y"
    assert f.renderers[1].glyph.line_color == "red"

    # Bokeh doesn't support 3D plots
    raises(NotImplementedError, lambda: p9(BB, quiver_kw=dict(sizeref=5)))
    raises(
        NotImplementedError,
        lambda: p10(BB, stream_kw=dict(colorscale=[[0, "red"], [1, "red"]])),
    )

    p = p11(BB, contour_kw=dict())
    assert len(p.series) == 1
    f = p.fig
    assert len(f.renderers) == 1
    assert isinstance(f.renderers[0].glyph, Image)

    p = p12(BB, contour_kw=dict())
    assert len(p.series) == 1
    f = p.fig
    assert len(f.renderers) == 1
    assert isinstance(f.renderers[0].glyph, Image)

    p = p14(BB, line_kw=dict(line_color="red"))
    assert len(p.series) == 2
    f = p.fig
    assert isinstance(f, Figure)
    assert len(f.renderers) == 2
    assert isinstance(f.renderers[0].glyph, Line)
    assert f.legend[0].items[0].label["value"] == "re(sqrt(x))"
    assert f.renderers[0].glyph.line_color == "red"
    assert isinstance(f.renderers[1].glyph, Line)
    assert f.legend[0].items[1].label["value"] == "im(sqrt(x))"
    assert f.renderers[1].glyph.line_color == "red"
    assert f.legend[0].visible == True

    p = p15(BB, line_kw=dict(line_color="red"))
    assert len(p.series) == 1
    f = p.fig
    assert len(f.renderers) == 1
    assert isinstance(f.renderers[0].glyph, MultiLine)
    assert f.renderers[0].glyph.line_color == "red"

    p = p16(BB, contour_kw=dict())
    assert len(p.series) == 1
    f = p.fig
    assert len(f.renderers) == 1
    assert isinstance(f.renderers[0].glyph, ImageRGBA)
示例#12
0
def test_PlotlyBackend():
    assert hasattr(PB, "colorloop")
    assert isinstance(PB.colorloop, (list, tuple))
    assert hasattr(PB, "colormaps")
    assert isinstance(PB.colormaps, (list, tuple))
    assert hasattr(PB, "wireframe_colors")
    assert isinstance(PB.wireframe_colors, (list, tuple))
    assert hasattr(PB, "quivers_colors")
    assert isinstance(PB.quivers_colors, (list, tuple))

    series = [UnsupportedSeries()]
    raises(NotImplementedError, lambda: Plot(*series, backend=PB))

    ### Setting custom color loop

    assert len(PBchild.colorloop) != len(PB.colorloop)
    _p1 = p1(PB)
    _p2 = p1(PBchild)
    assert len(_p1.series) == len(_p2.series)
    f1 = _p1.fig
    f2 = _p2.fig
    assert all([isinstance(t, go.Scatter) for t in f1.data])
    assert all([isinstance(t, go.Scatter) for t in f2.data])
    # there are 6 unique colors in _p1 and 3 unique colors in _p2
    assert len(set([d["line"]["color"] for d in f1.data])) == 6
    assert len(set([d["line"]["color"] for d in f2.data])) == 3

    ### test for line_kw, surface_kw, quiver_kw, stream_kw: they should override
    ### defualt settings.

    p = p2(PB, line_kw=dict(line_color="red"))
    assert len(p.series) == 2
    f = p.fig
    assert isinstance(f, go.Figure)
    assert len(f.data) == 2
    assert isinstance(f.data[0], go.Scatter)
    assert f.data[0]["name"] == "sin(x)"
    assert f.data[0]["line"]["color"] == "red"
    assert isinstance(f.data[1], go.Scatter)
    assert f.data[1]["name"] == "cos(x)"
    assert f.data[1]["line"]["color"] == "red"
    assert f.layout["showlegend"] == True

    p = p3(PB, line_kw=dict(line_color="red"))
    assert len(p.series) == 1
    f = p.fig
    assert len(f.data) == 1
    assert isinstance(f.data[0], go.Scatter)
    assert f.data[0]["name"] == "(cos(x), sin(x))"
    assert f.data[0]["line"]["color"] == "red"

    p = p4(PB, line_kw=dict(line_color="red"))
    assert len(p.series) == 1
    f = p.fig
    assert len(f.data) == 1
    assert isinstance(f.data[0], go.Scatter3d)
    assert f.data[0]["line"]["color"] == "red"
    assert f.data[0]["name"] == "(cos(x), sin(x), x)"
    assert f.data[0]["line"]["colorbar"]["title"]["text"] == "(cos(x), sin(x), x)"

    # use_cm=False will force to apply a default solid color to the mesh.
    # Here, I override that solid color with a custom color.
    p = p5(PB, surface_kw=dict(colorscale=[[0, "cyan"], [1, "cyan"]]))
    assert len(p.series) == 1
    f = p.fig
    assert len(f.data) == 1
    assert isinstance(f.data[0], go.Surface)
    assert f.data[0]["name"] == "cos(x**2 + y**2)"
    assert f.data[0]["showscale"] == False
    assert f.data[0]["colorscale"] == ((0, "cyan"), (1, "cyan"))
    assert f.layout["showlegend"] == False

    p = p6(PB, contour_kw=dict(contours=dict(coloring="lines")))
    assert len(p.series) == 1
    f = p.fig
    assert len(f.data) == 1
    assert isinstance(f.data[0], go.Contour)
    assert f.data[0]["contours"]["coloring"] == "lines"
    assert f.data[0]["colorbar"]["title"]["text"] == str(cos(x ** 2 + y ** 2))

    p = p7(
        PB,
        quiver_kw=dict(line_color="red"),
        contour_kw=dict(contours=dict(coloring="lines")),
    )
    assert len(p.series) == 2
    f = p.fig
    assert len(f.data) == 2
    assert isinstance(f.data[0], go.Contour)
    assert isinstance(f.data[1], go.Scatter)
    assert f.data[0]["contours"]["coloring"] == "lines"
    assert f.data[0]["colorbar"]["title"]["text"] == "Magnitude"
    assert f.data[1]["line"]["color"] == "red"

    p = p8(
        PB,
        stream_kw=dict(line_color="red"),
        contour_kw=dict(contours=dict(coloring="lines")),
    )
    assert len(p.series) == 2
    f = p.fig
    assert len(f.data) == 2
    assert isinstance(f.data[0], go.Contour)
    assert isinstance(f.data[1], go.Scatter)
    assert f.data[0]["contours"]["coloring"] == "lines"
    assert f.data[0]["colorbar"]["title"]["text"] == "x + y"
    assert f.data[1]["line"]["color"] == "red"

    p = p9(PB, quiver_kw=dict(sizeref=5))
    assert len(p.series) == 1
    f = p.fig
    assert len(f.data) == 1
    assert isinstance(f.data[0], go.Cone)
    assert f.data[0]["sizeref"] == 5
    assert f.data[0]["colorbar"]["title"]["text"] == str(Matrix([z, y, x]))

    p = p10(PB, stream_kw=dict(colorscale=[[0, "red"], [1, "red"]]))
    assert len(p.series) == 1
    f = p.fig
    assert len(f.data) == 1
    assert isinstance(f.data[0], go.Streamtube)
    assert f.data[0]["colorscale"] == ((0, "red"), (1, "red"))
    assert f.data[0]["colorbar"]["title"]["text"] == str(Matrix([z, y, x]))

    p = p11(PB, contour_kw=dict(colorscale=[[0, "rgba(0,0,0,0)"], [1, "red"]]))
    assert len(p.series) == 1
    f = p.fig
    assert len(f.data) == 1
    assert isinstance(f.data[0], go.Contour)
    assert f.data[0]["colorscale"] == ((0, "rgba(0,0,0,0)"), (1, "red"))

    p = p12(PB, contour_kw=dict(fillcolor="red"))
    assert len(p.series) == 1
    f = p.fig
    assert len(f.data) == 1
    assert isinstance(f.data[0], go.Contour)
    assert f.data[0]["fillcolor"] == "red"

    p = p14(PB, line_kw=dict(line_color="red"))
    assert len(p.series) == 2
    f = p.fig
    assert isinstance(f, go.Figure)
    assert len(f.data) == 2
    assert isinstance(f.data[0], go.Scatter)
    assert f.data[0]["name"] == "re(sqrt(x))"
    assert f.data[0]["line"]["color"] == "red"
    assert isinstance(f.data[1], go.Scatter)
    assert f.data[1]["name"] == "im(sqrt(x))"
    assert f.data[1]["line"]["color"] == "red"
    assert f.layout["showlegend"] == True

    p = p15(PB, line_kw=dict(line_color="red"))
    assert len(p.series) == 1
    f = p.fig
    assert len(f.data) == 1
    assert isinstance(f.data[0], go.Scatter)
    assert f.data[0]["name"] == "Abs(sqrt(x))"
    assert f.data[0]["line"]["color"] == "red"

    p = p16(PB, contour_kw=dict())
    assert len(p.series) == 1
    f = p.fig
    assert len(f.data) == 2
    assert isinstance(f.data[0], go.Image)
    assert f.data[0]["name"] == "sqrt(x)"
    assert isinstance(f.data[1], go.Scatter)
    assert f.data[1]["marker"]["colorbar"]["title"]["text"] == "Argument"

    p = p17(PB, surface_kw=dict())
    assert len(p.series) == 1
    f = p.fig
    assert len(f.data) == 1
    assert isinstance(f.data[0], go.Surface)
    assert f.data[0]["name"] == "sqrt(x)"
    assert f.data[0]["showscale"] == False
    assert f.layout["showlegend"] == False
示例#13
0
def test_MatplotlibBackend():
    assert hasattr(MB, "colorloop")
    assert isinstance(MB.colorloop, (ListedColormap, list, tuple))
    assert hasattr(MB, "colormaps")
    assert isinstance(MB.colormaps, (list, tuple))

    series = [UnsupportedSeries()]
    raises(NotImplementedError, lambda: Plot(*series, backend=MB).process_series())

    ### test for line_kw, surface_kw, quiver_kw, stream_kw: they should override
    ### defualt settings.

    p = p2(MB, line_kw=dict(color="red"))
    assert len(p.series) == 2
    # MatplotlibBackend only add data to the plot when the following method
    # is internally called. But show=False, hence it is not called.
    p.process_series()
    f, ax = p.fig
    assert isinstance(f, plt.Figure)
    assert isinstance(ax, Axes)
    assert len(ax.get_lines()) == 2
    assert ax.get_lines()[0].get_label() == "sin(x)"
    assert ax.get_lines()[0].get_color() == "red"
    assert ax.get_lines()[1].get_label() == "cos(x)"
    assert ax.get_lines()[1].get_color() == "red"

    p = p3(MB, line_kw=dict(color="red"))
    assert len(p.series) == 1
    # parametric plot. The label is shown on the colorbar, which is only visible
    # when legend=True.
    p.legend = True
    p.process_series()
    f, ax = p.fig
    # parametric plot with use_cm=True -> LineCollection
    assert len(ax.collections) == 1
    assert isinstance(ax.collections[0], LineCollection)
    assert f.axes[1].get_ylabel() == "(cos(x), sin(x))"
    assert all(*(ax.collections[0].get_color() - np.array([1.0, 0.0, 0.0, 1.0])) == 0)

    p = p4(MB, line_kw=dict(color="red"))
    assert len(p.series) == 1
    p.legend = True
    p.process_series()
    f, ax = p.fig
    assert len(ax.collections) == 1
    assert isinstance(ax.collections[0], Line3DCollection)
    assert f.axes[1].get_ylabel() == "(cos(x), sin(x), x)"
    assert all(*(ax.collections[0].get_color() - np.array([1.0, 0.0, 0.0, 1.0])) == 0)

    # use_cm=False will force to apply a default solid color to the mesh.
    # Here, I override that solid color with a custom color.
    p = p5(MB, surface_kw=dict(color="red"))
    assert len(p.series) == 1
    p.process_series()
    f, ax = p.fig
    assert len(ax.collections) == 1
    assert isinstance(ax.collections[0], Poly3DCollection)
    # TODO: apparently, without showing the plot, the colors are not applied
    # to a Poly3DCollection... -.-'
    #     # matplotlib renders shadows, hence there are different red colors. Here
    #     # we check that the G, B components are zero, hence the color is Red.
    #     colors = ax.collections[0].get_facecolors()
    #     assert all(c[1] == 0 and c[2] == 0 for c in colors)
    # casso

    p = p6(MB, contour_kw=dict(cmap="jet"))
    assert len(p.series) == 1
    p.process_series()
    f, ax = p.fig
    # TODO: isn't there an exact number of collections associated to contour plots?
    assert len(ax.collections) > 0
    assert f.axes[1].get_ylabel() == str(cos(x ** 2 + y ** 2))
    # TODO: how to retrieve the colormap from a contour series?????
    #     assert ax.collections[0].cmap.name == "jet"

    p = p7(MB, quiver_kw=dict(color="red"), contour_kw=dict(cmap="jet"))
    assert len(p.series) == 2
    p.process_series()
    f, ax = p.fig
    assert len(ax.collections) > 0
    assert isinstance(ax.collections[-1], Quiver)
    assert f.axes[1].get_ylabel() == "Magnitude"
    # TODO: how to retrieve the colormap from a contour series?????
    #     assert ax.collections[0].cmap.name == "jet"

    p = p8(MB, stream_kw=dict(color="red"), contour_kw=dict(cmap="jet"))
    assert len(p.series) == 2
    p.process_series()
    f, ax = p.fig
    assert len(ax.collections) > 0
    assert isinstance(ax.collections[-1], LineCollection)
    assert f.axes[1].get_ylabel() == "x + y"
    assert all(*(ax.collections[-1].get_color() - np.array([1.0, 0.0, 0.0, 1.0])) == 0)

    p = p9(MB, quiver_kw=dict(cmap="jet"))
    assert len(p.series) == 1
    p.process_series()
    f, ax = p.fig
    assert len(ax.collections) == 1
    assert isinstance(ax.collections[0], Line3DCollection)
    assert ax.collections[0].cmap.name == "jet"

    p = p10(MB, stream_kw=dict())
    raises(NotImplementedError, lambda: p.process_series())

    p = p12(MB, contour_kw=dict(cmap="jet"))
    assert len(p.series) == 1
    p.process_series()
    f, ax = p.fig
    assert len(ax.collections) > 0
    # TODO: how to retrieve the colormap from a contour series?????
    #     assert ax.collections[0].cmap.name == "jet"

    p = p14(MB, line_kw=dict(color="red"))
    assert len(p.series) == 2
    p.process_series()
    f, ax = p.fig
    assert len(ax.get_lines()) == 2
    assert ax.get_lines()[0].get_label() == "re(sqrt(x))"
    assert ax.get_lines()[0].get_color() == "red"
    assert ax.get_lines()[1].get_label() == "im(sqrt(x))"
    assert ax.get_lines()[1].get_color() == "red"

    p = p15(MB, line_kw=dict(color="red"))
    assert len(p.series) == 1
    p.process_series()
    f, ax = p.fig
    assert len(ax.collections) == 1
    assert isinstance(ax.collections[0], LineCollection)
    assert f.axes[1].get_ylabel() == "Abs(sqrt(x))"
    assert all(*(ax.collections[0].get_color() - np.array([1.0, 0.0, 0.0, 1.0])) == 0)

    p = p16(MB, contour_kw=dict())
    assert len(p.series) == 1
    p.process_series()
    f, ax = p.fig
    assert len(ax.images) == 1
    assert f.axes[1].get_ylabel() == "Argument"

    p = p17(MB, surface_kw=dict(color="red"))
    assert len(p.series) == 1
    p.process_series()
    f, ax = p.fig
    assert len(ax.collections) == 1
    assert isinstance(ax.collections[0], Poly3DCollection)
示例#14
0
def plot_implicit(*args, show=True, **kwargs):
    """Plot implicit equations / inequalities.

    plot_implicit, by default, generates a contour using a mesh grid of fixed
    number of points. The greater the number of points, the greater the memory
    used. By setting `adaptive=True` interval arithmetic will be used to plot
    functions. If the expression cannot be plotted using interval arithmetic,
    it defaults to generating a contour using a mesh grid. With interval
    arithmetic, the line width can become very small; in those cases, it is
    better to use the mesh grid approach.

    Arguments
    =========
        expr : Expr, Relational, BooleanFunction
            The equation / inequality that is to be plotted.

        ranges : tuples
            Two tuple denoting the discretization domain, for example:
            `(x, -10, 10), (y, -10, 10)`
            If no range is given, then the free symbols in the expression will
            be assigned in the order they are sorted.

        label : str
            The name of the expression to be eventually shown on the legend.
            If none is provided, the string representation will be used.

    Keyword Arguments
    =================

        adaptive : Boolean
            The default value is set to False, meaning that the internal
            algorithm uses a mesh grid approach. In such case,
            Boolean combinations of expressions cannot be plotted.
            If set to True, the internal algorithm uses interval arithmetic.
            It switches to a fall back algorithm (meshgrid approach) if the
            expression cannot be plotted using interval arithmetic.

        depth : integer
            The depth of recursion for adaptive mesh grid. Default value is 0.
            Takes value in the range (0, 4).
            Think of the resulting plot as a picture composed by pixels. By
            increasing `depth` we are increasing the number of pixels, thus
            obtaining a more accurate plot.

        n1, n2 : int
            Number of discretization points in the horizontal and vertical
            directions when `adaptive=False`. Default to 1000.

        n : integer
            Set the number of discretization points when `adaptive=False` in
            both direction simultaneously. Default value is 1000.
            The greater the value the more accurate the plot, but the more
            memory will be used.

        show : Boolean
            Default value is True. If set to False, the plot will not be shown.
            See ``Plot`` for further information.

        title : string
            The title for the plot.

        xlabel : string
            The label for the x-axis

        ylabel : string
            The label for the y-axis

    Examples
    ========

    Plot expressions:

    .. plot::
        :context: reset
        :format: doctest
        :include-source: True

        >>> from sympy import plot_implicit, symbols, Eq, And
        >>> x, y = symbols('x y')

    Without any ranges for the symbols in the expression:

    .. plot::
        :context: close-figs
        :format: doctest
        :include-source: True

        >>> p1 = plot_implicit(Eq(x**2 + y**2, 5))

    With the range for the symbols:

    .. plot::
        :context: close-figs
        :format: doctest
        :include-source: True

        >>> p2 = plot_implicit(
        ...     Eq(x**2 + y**2, 3), (x, -3, 3), (y, -3, 3))

    Using mesh grid without adaptive meshing with number of points
    specified:

    .. plot::
        :context: close-figs
        :format: doctest
        :include-source: True

        >>> p3 = plot_implicit(
        ...     (x**2 + y**2 - 1)**3 - x**2 * y**3,
        ...     (x, -1.5, 1.5), (y, -1.5, 1.5),
        ...     n = 1000)

    Using adaptive meshing and Boolean expressions:

    .. plot::
        :context: close-figs
        :format: doctest
        :include-source: True

        >>> p4 = plot_implicit(
        ...     Eq(y, sin(x)) & (y > 0),
        ...     Eq(y, sin(x)) & (y < 0),
        ...     (x, -2 * pi, 2 * pi), (y, -4, 4),
        ...     adaptive=True)

    Using adaptive meshing with depth of recursion as argument:

    .. plot::
        :context: close-figs
        :format: doctest
        :include-source: True

        >>> p5 = plot_implicit(
        ...     Eq(x**2 + y**2, 5), (x, -4, 4), (y, -4, 4),
        ...     adaptive=True, depth = 2)

    Plotting regions:

    .. plot::
        :context: close-figs
        :format: doctest
        :include-source: True

        >>> p6 = plot_implicit(y > x**2)

    """
    from spb.defaults import TWO_D_B

    args = _plot_sympify(args)
    args = _check_arguments(args, 1, 2)

    kwargs = _set_discretization_points(kwargs, ImplicitSeries)

    series_kw = dict()
    series_kw["n1"] = kwargs.pop("n1", 1000)
    series_kw["n2"] = kwargs.pop("n2", 1000)
    series_kw["depth"] = kwargs.pop("depth", 0)
    series_kw["adaptive"] = kwargs.pop("adaptive", False)

    series = []
    xmin, xmax, ymin, ymax = oo, -oo, oo, -oo
    for a in args:
        s = ImplicitSeries(*a, **series_kw)
        if s.start_x < xmin:
            xmin = s.start_x
        if s.end_x > xmax:
            xmax = s.end_x
        if s.start_y < ymin:
            ymin = s.start_y
        if s.end_y > ymax:
            ymax = s.end_y
        series.append(s)

    kwargs.setdefault("backend", TWO_D_B)
    kwargs.setdefault("xlim", (xmin, xmax))
    kwargs.setdefault("ylim", (ymin, ymax))
    kwargs.setdefault("xlabel", series[-1].var_x.name)
    kwargs.setdefault("ylabel", series[-1].var_y.name)
    p = Plot(*series, **kwargs)
    if show:
        p.show()
    return p
示例#15
0
def geometry_plot(*args, show=True, **kwargs):
    """Plot entities from the sympy.geometry module.

    Arguments
    =========
        geom : GeometryEntity
            Represent the geometric entity to be plotted.

        label : str
            The name of the complex function to be eventually shown on the
            legend. If none is provided, the string representation of the
            function will be used.

        To specify multiple complex functions, wrap them into a tuple.
        Refer to the examples to learn more.

    Keyword Arguments
    =================

        fill : boolean
            Default to True. Fill the polygon/circle/ellipse.

        params : dict
            Substitution dictionary to properly evaluate symbolic geometric
            entities. The keys contains symbols, the values the numeric number
            associated to the symbol.

    Examples
    ========

    Plot several numeric geometric entitiesy. By default, circles, ellipses and
    polygons are going to be filled. Plotting Curve objects is the same as
    `plot_parametric`.

    .. code-block:: python
        geometry_plot(
            Circle(Point(0, 0), 5),
            Ellipse(Point(-3, 2), hradius=3, eccentricity=Rational(4, 5)),
            Polygon((4, 0), 4, n=5),
            Curve((cos(x), sin(x)), (x, 0, 2 * pi)),
            Segment((-4, -6), (6, 6)),
            Point2D(0, 0))

    Plot several numeric geometric entities defined by numbers only, turn off
    fill. Every entity is represented as a line.

    .. code-block:: python
        geometry_plot(
            Circle(Point(0, 0), 5),
            Ellipse(Point(-3, 2), hradius=3, eccentricity=Rational(4, 5)),
            Polygon((4, 0), 4, n=5),
            Curve((cos(x), sin(x)), (x, 0, 2 * pi)),
            Segment((-4, -6), (6, 6)),
            Point2D(0, 0), fill=False)

    Plot several symbolic geometric entities. We need to pass in the `params`
    dictionary, which will be used to substitute symbols before numerical
    evaluation. Note: here we also set custom labels:

    .. code-block:: python
        a, b, c, d = symbols("a, b, c, d")
        geometry_plot(
            (Polygon((a, b), c, n=d), "triangle"),
            (Polygon((a + 2, b + 3), c, n=d + 1), "square"),
            params = {a: 0, b: 1, c: 2, d: 3}
        )

    Plot 3D geometric entities. Note: when plotting a Plane, we must always
    provide the x/y/z ranges:

    .. code-block:: python
        geometry_plot(
            (Point3D(5, 5, 5), "center"),
            (Line3D(Point3D(-2, -3, -4), Point3D(2, 3, 4)), "line"),
            (Plane((0, 0, 0), (1, 1, 1)), (x, -5, 5), (y, -4, 4), (z, -10, 10))
        )

    """
    from spb.defaults import TWO_D_B, THREE_D_B

    args = _plot_sympify(args)

    series = []
    if not all([isinstance(a, (list, tuple, Tuple)) for a in args]):
        args = [args]

    for a in args:
        exprs, ranges, label = _unpack_args(*a)
        r = ranges if len(ranges) > 0 else [None]
        if len(exprs) == 1:
            series.append(GeometrySeries(exprs[0], *r, label, **kwargs))
        else:
            # this is the case where the user provided: v1, v2, ..., range
            # we use the same ranges for each expression
            for e in exprs:
                series.append(GeometrySeries(e, *r, str(e), **kwargs))

    any_3D = any(s.is_3D for s in series)
    if ("aspect" not in kwargs) and (not any_3D):
        kwargs["aspect"] = "equal"

    if any_3D:
        kwargs.setdefault("backend", THREE_D_B)
    else:
        kwargs.setdefault("backend", TWO_D_B)

    p = Plot(*series, **kwargs)
    if show:
        p.show()
    return p
示例#16
0
def smart_plot(*args, show=True, **kwargs):
    """Smart plot interface. Using the same interface of the other plot
    functions, namely (expr, range, label), it unifies the plotting experience.
    If a symbolic expression can be plotted with any of the plotting functions
    exposed by spb.functions or spb.vectors, then this function will be able
    to plot it as well.

    Keyword Arguments
    =================
        The usual keyword arguments available on every other plotting functions
        are available (`xlabel`, ..., `adaptive`, `n`, ...). On top of that
        we can set:

        pt : str
            Specify which kind of plot we are intereseted. Default value
            is None, indicating the function will use automatic detection.
            Possible values are:
                "p": to specify a line plot.
                "pp": to specify a 2d parametric line plot.
                "p3dl": to specify a 3d parametric line plot.
                "p3d": to specify a 3d plot.
                "p3ds": to specify a 3d parametric surface plot.
                "pi": to specify an implificit plot.
                "pinter": to specify an interactive plot. In such a case, you
                        will also have to provide a `param` dictionary mapping
                        theparameters to their values.
                        To specify a complex-interactive plot, set
                        `is_complex=True`.
                "v2d": to specify a 2D vector plot.
                "v3d": to specify a 3D vector plot.
                "c": to specify a complex plot.
                "g": to specify a geometric entity plot.

    Examples
    ========

    Plotting different types of expressions with automatic detection:

    .. code-block:: python
        from sympy import symbols, sin, cos, Matrix
        from spb.backends.plotly import PB
        x, y = symbols("x, y")
        smart_plot(
            (Matrix([-sin(y), cos(x)]), (x, -5, 5), (y, -3, 3), "vector"),
            (sin(x), (x, -5, 5)),
            aspect="equal", n=20, legend=True,
            quiver_kw=dict(scale=0.25), line_kw=dict(line_color="cyan"),
            backend=PB
        )

    Specify the kind of plot we are interested in:

    .. code-block:: python
        from sympy import symbols, cos
        x, y = symbols("x, y")
        plot(cos(x**2 + y**2), (x, -3, 3), (y, -3, 3),
            xlabel="x", ylabel="y", pt="pc")

    See also
    ========

    plot, plot_parametric, plot3d, plot3d_parametric_line,
    plot3d_parametric_surface, plot_contour, plot_implicit,
    vector_plot, complex_plot
    """

    args = _plot_sympify(args)
    if not all([isinstance(a, (list, tuple, Tuple)) for a in args]):
        args = [args]
    series = []

    for arg in args:
        series.append(_build_series(*arg, **kwargs))

    if "backend" not in kwargs.keys():
        from spb.defaults import TWO_D_B, THREE_D_B

        is_3D = any([s.is_3D for s in series])
        kwargs["backend"] = THREE_D_B if is_3D else TWO_D_B
    plots = Plot(*series, **kwargs)
    if show:
        plots.show()
    return plots
示例#17
0
def vector_plot(*args, show=True, **kwargs):
    """Plot a 2D or 3D vector field. By default, the aspect ratio of the plot
    is set to ``aspect="equal"``.

    Arguments
    =========
        expr : Vector, or Matrix with 2 or 3 elements, or list/tuple  with 2 or
                3 elements)
            Represent the vector to be plotted.
            Note: if a 3D vector is given with a list/tuple, it might happens
            that the internal algorithm could think of it as a range. Therefore,
            3D vectors should be given as a Matrix or as a Vector: this reduces
            ambiguities.

        ranges : 3-element tuples
            Denotes the range of the variables. For example (x, -5, 5). For 2D
            vector plots, 2 ranges should be provided. For 3D vector plots, 3
            ranges are needed.

        label : str
            The name of the vector field to be eventually shown on the legend.
            If none is provided, the string representation of the vector will
            be used.

        To specify multiple vector fields, wrap them into a tuple. Refer to the
        examples to learn more.

    Keyword Arguments
    =================

        contours_kw : dict
            A dictionary of keywords/values which is passed to the plotting
            library contour function to customize the appearance. Refer to the
            plotting library (backend) manual for more informations.

        n1, n2, n3 : int
            Number of discretization points in the x/y/z-direction respectively
            for the quivers or streamlines. Default to 25.

        n : int
            Set the same number of discretization points in all directions for
            the quivers or streamlines. It overrides n1, n2, n3. Default to 25.

        nc : int
            Number of discretization points for the scalar contour plot.
            Default to 100.

        quiver_kw : dict
            A dictionary of keywords/values which is passed to the plotting
            library quivers function to customize the appearance. Refer to the
            plotting library (backend) manual for more informations.

        scalar : boolean, Expr, None or list/tuple of 2 elements
            Represents the scalar field to be plotted in the background. Can be:
                True: plot the magnitude of the vector field.
                False/None: do not plot any scalar field.
                Expr: a symbolic expression representing the scalar field.
                List/Tuple: [scalar_expr, label], where the label will be shown
                    on the colorbar.
            Default to True.

        show : boolean
            Default to True, in which case the plot will be shown on the screen.

        slice : Plane, list, Expr
            Plot the 3D vector field over the provided slice. It can be:
                Plane: a Plane object from sympy.geometry module.
                list: a list of planes.
                Expr: a symbolic expression representing a surface.
            The number of discretization points will be `n1`, `n2`, `n3`.
            Note that:
                1. only quivers plots are supported with slices.
                2. `n3` will be used only with planes parallel to xz or yz.

        streamlines : boolean
            Whether to plot the vector field using streamlines (True) or quivers
            (False). Default to False.

        stream_kw : dict
            A dictionary of keywords/values which is passed to the backend
            streamlines-plotting function to customize the appearance. Refer to
            the backend's manual for more informations.

    Examples
    ========

    Quivers plot of a 2D vector field with a contour plot in background
    representing the vector's magnitude (a scalar field):

    .. code-block:: python
        x, y = symbols("x, y")
        vector_plot([-sin(y), cos(x)], (x, -3, 3), (y, -3, 3))


    Streamlines plot of a 2D vector field with no background scalar field:

    .. code-block:: python
        x, y = symbols("x, y")
        vector_plot([-sin(y), cos(x)], (x, -3, 3), (y, -3, 3),
                streamlines=True, scalar=None)


    Plot of two 2D vectors with background the contour plot of magnitude of the
    first vector:

    .. code-block:: python
        x, y = symbols("x, y")
        vector_plot([-sin(y), cos(x)], [y, x], n=20,
            scalar=sqrt((-sin(y))**2 + cos(x)**2), legend=True)


    3D vector field:

    .. code-block:: python
        x, y, z = symbols("x, y, z")
        vector_plot([x, y, z], (x, -10, 10), (y, -10, 10), (z, -10, 10),
                n=8)


    3D vector field with 3 orthogonal slice planes:

    .. code-block:: python
        x, y, z = symbols("x, y, z")
        vector_plot([z, y, x], (x, -10, 10), (y, -10, 10), (z, -10, 10), n=8,
            slice=[
                Plane((-10, 0, 0), (1, 0, 0)),
                Plane((0, -10, 0), (0, 2, 0)),
                Plane((0, 0, -10), (0, 0, 1)),
            ])
    """
    args = _plot_sympify(args)
    args = _preprocess(*args)

    kwargs = _set_discretization_points(kwargs, Vector3DSeries)
    # TODO: do I need these?
    if "n1" not in kwargs:
        kwargs["n1"] = 25
    if "n2" not in kwargs:
        kwargs["n2"] = 25
    if "aspect" not in kwargs.keys():
        kwargs["aspect"] = "equal"

    series = _build_series(*args, **kwargs)
    if all([isinstance(s, (Vector2DSeries, ContourSeries)) for s in series]):
        from spb.defaults import TWO_D_B

        backend = kwargs.pop("backend", TWO_D_B)
    elif all([isinstance(s, Vector3DSeries) for s in series]):
        from spb.defaults import THREE_D_B

        backend = kwargs.pop("backend", THREE_D_B)
    else:
        raise ValueError("Mixing 2D vectors with 3D vectors is not allowed.")

    p = Plot(*series, backend=backend, **kwargs)
    if show:
        p.show()
    return p