# Plotting complex numbers and complex function

This module exposes the `complex_plot` function which can be used to create different kinds of plot:
1. Plotting lists of complex numbers.
2. Plotting over a real range the real and imaginary part of a complex function.
3. Plotting over a real range the absolute value of a complex function colored by its argument.
4. 3D plot of the real/imaginary part, absolute value and argument of a function of two variables.
5. Domain coloring of a complex function over a complex domain.
6. 3D plot of the absolute value of a complex function colored by its argument.

In the following tutorial we are not going to explore them all. Instead, we will focus on the differences between the backends.


```python
%matplotlib widget
from sympy import *
from spb import *
from spb.backends.plotly import PB
from spb.backends.k3d import KB
from spb.backends.matplotlib import MB
var("x:z")
help(complex_plot)
```

## Real/Imaginary or Absolute/Argument for functions of 1 variable

Let's say we'd like to plot the real and imaginary parts of a complex function over a real range. All we have to do is the following:


```python
expr = cos(x) + sqrt(I * x)
expr
```


```python
complex_plot(expr, (x, -2, 2), backend=PB)
```

We set `absarg=True` in order to create a plot of the magnitude of a complex function colored by its argument:


```python
complex_plot(expr, (x, -2, 2), absarg=True, backend=PB)
```

Note that Plotly is unable to plot gradient lines, so the change in phase is represented by colored markers. By hovering the markers we get a tooltip telling us useful information.

Let's try a different backend:


```python
complex_plot(expr, (x, -2, 2), absarg=True, backend=MB)
```

If we are only interested in the absolute value without argument-coloring, we can set `use_cm=False`:


```python
complex_plot(expr, (x, -2, 2), absarg=True, use_cm=False, backend=PB)
```

Note that we can visualize every quantity by turning on the respective flag:


```python
complex_plot(expr, (x, -2, 2), real=True, imag=True, abs=True, arg=True, backend=PB)
```

The numerical data of all the above plots have been generated with Numpy. We can also choose Mpmath by setting `modules="mpmath"`: this option will be passed to `lambdify`. Note that the numerical evaluation with Mpmath is slower than Numpy, but the results are different when branch cuts are involved. Let's illustrate the differences by plotting the imaginary part of a function:


```python
p1 = complex_plot((asin(x), (x, -5, 5), "numpy"), real=False, imag=True, show=False)
p2 = complex_plot((asin(x), (x, -5, 5), "mpmath"), real=False, imag=True,
                  modules="mpmath", show=False)
(p1 + p2).show()
```

As we can see, there are regions in the plot where Numpy and Mpmath computes the same imaginary part, and other regions where the imaginary parts have opposite sign. This also leads to different arguments:


```python
p1 = complex_plot((asin(x), (x, -5, 5), "numpy"), real=False, imag=False, 
                  arg=True, show=False)
p2 = complex_plot((asin(x), (x, -5, 5), "mpmath"), real=False, imag=False,
                  arg=True, modules="mpmath", show=False)
(p1 + p2).show()
```

The above results are also valid when creating domain coloring plots (next section). Therefore, the user should carefully select the numerical library according to his/her preferences and objectives.

## Real/Imaginary part or Absolute value for functions of 2 variables

Similar to the above examples, we can also plot the real part, the imaginary part and the absolute value of a function of 2 variables over two real ranges. Again, we can control what to show by toggling `real=True, imag=True, abs=True`. For example:

By default, when no keyword arguments is passed, the real and imaginary parts
are going to be plotted:


```python
complex_plot(sqrt(x * y), (x, -5, 5), (y, -5, 5))
```

To plot only the imaginary part:


```python
complex_plot(sqrt(x * y), (x, -5, 5), (y, -5, 5), real=False, imag=True)
```

To plot the absolute value:


```python
complex_plot(sqrt(x * y), (x, -5, 5), (y, -5, 5), real=False, imag=False, abs=True)
```

## Domain Coloring

[Domain coloring](https://en.wikipedia.org/wiki/Domain_coloring) is a technique for visualizing complex functions by assigning a color to each point of the complex plane. As we have seen earlier by reading `complex_plot` documentation, we can chose between different coloring.

This module implements several color schemes based on Elias Wegert's book ["Visual Complex Functions"](https://www.springer.com/de/book/9783034801799). The book provides the background to better understand the images. Find out the available ``coloring`` options by reading ``complex_plot`` documentation.

Let's start with the default:


```python
complex_plot(gamma(z), (z, -4 - 2 * I, 4 + 2 * I), backend=PB)
```

More generally, we can think of the result of domain coloring as a picture. The complex domain is discretized with `n1` points in the horizontal direction and `n2` points in the vertical direction. Therefore, the picture will have `(n1 - 1) (n2 - 1)` pixels. We can increase `n1` and `n2` to refine the result, however Plotly will become slower and slower in rendering the results. In such cases, it is better to use a different backend, as we will later see.

Note that:
* By default, domain coloring plots automatically set `aspect="equal"`. 
* Plotly has a _bug_: the vertical axis is reversed, with negative values on the top and positive values on the bottom. We will get back to it later!

Let's now try a different coloring with `MatplotlibBackend`:


```python
complex_plot(gamma(z), (z, -4 - 2 * I, 4 + 2 * I), coloring="b", backend=MB)
```

Note how much faster the picture was generated: there is no javascript involved. However, we lost a lot of information: by hovering over the picture, we are only going to see the pointer coordinates.

We can also plot the absolute value of the complex function colored by its argument in 3D, by setting `threed=True`:


```python
complex_plot(gamma(z), (z, -4 - 2 * I, 4 + 2 * I), 
             backend=PB, threed=True, zlim=(0, 10), n=100)
```

There are a few things to point out:

* by default, Plotly is not keeping a fixed aspect ratio.
* by zooming in, we can see some "segmented" lines separating colors: the underlying data is absolutely correct, whereas those lines are caused by the interpolation used by Plotly. Essentially, Plotly is interpolating the argument and it is unaware that the argument is periodic. Once the periodic jump is reached, those lines will appear.
* there is even a worse [bug with Plotly](https://github.com/plotly/plotly.js/issues/5003) with 3D surfaces: when we hover a point, the tooltip will display wrong information for the argument and the phase. Hopefully this bug will be fixed upstream.

Instead of typing `threed=True`, we might use the `complex_plot3d` function, which is just a wrapper function to `complex_plot` that sets the flag for us.

Let's try a different coloring with K3D:


```python
complex_plot3d(gamma(z), (z, -4 - 2 * I, 4 + 2 * I), coloring="b",
             backend=KB, zlim=(0, 10), n=100)
```

Considering that complex functions can go to infinity, a fixed-aspect ratio plotting library (like K3D) might not be the best choice! Here we set `zlim` and `K3DBackend` added a couple of clipping planes. We can delete them by opening the menu "Controls -> Clipping Planes".

We can also plot the real, imaginary and absolute value separately by using the respective flags:


```python
complex_plot3d(gamma(z), (z, -4 - 2 * I, 4 + 2 * I), backend=KB, 
             zlim=(0, 10), real=True, imag=True, abs=True, n=100)
```

Again, we can use "Cotrols -> Objects" and chose which mesh to hide.

Finally, note that we can set a default value for the `coloring`. Refer to [tutorial 3](tutorial-3.customize-the-module.ipynb) and set `cfg["complex"]["coloring"]` to one of the values specified in `help(complex_plot)`.

## Interactive plots

We can also use `iplot` to create interactive complex plots. We must remember to set `is_complex=True`. Keep in mind that some backend might not support all functionalities listed above.


```python
from spb.interactive import iplot

iplot(
    (z * sin(x * z) * exp(2 * pi * I / (y * z)), (z, -5, 5)),
    params = {
        x: (1, 0, 3),
        y: (1, -5, 5),
    },
    is_complex = True,
    backend = PB,
    absarg = True,
    n1 = 2000
)
```

The user can change `absarg=False` and rerun the plot.

Let's now try to plot the real part of a function of 2 variables:


```python
iplot(
    (sqrt(x**z * y), (x, -5, 5), (y, -5, 5)),
    params = {
        z: (1, 0, 2)
    },
    backend = KB,
    is_complex = True,
    real = True,
    threed = True
)
```

Let's now try with a domain coloring plot:


```python
iplot(
    ((z**2 + 1) / (x * (z**2 - 1)), (z, -4 - 2 * I, 4 + 2 * I)),
    params = {
        x: (1, -2, 2)
    },
    backend = MB,
    is_complex = True,
    coloring = "b"
)
```

Finally, let's try with a 3D. Keep in mind that the update might be slow:


```python
iplot(
    ((z**2 + 1) / (x * (z**2 - 1)), (z, -4 - 2 * I, 4 + 2 * I)),
    params = {
        x: (1, -2, 2)
    },
    backend = KB,
    threed = True,
    is_complex = True,
    coloring = "b",
    zlim = (0, 6)
)
```


```python

```

830f5076db34def6fa6e3078853ad4c0c1f1ba86
ipynb
Jupyter Notebook
tutorials/tutorial-9.complex-plots.ipynb
Davide-sd/sympy-plot-backends
f8ad8a5d7aef3c55ddee8e77997e06e0b4f2e14b
BSD-3-Clause
2021-05-04T17:57:32.000Z
2022-02-24T16:14:45.000Z
tutorials/tutorial-9.complex-plots.ipynb
Davide-sd/sympy-plot-backends
f8ad8a5d7aef3c55ddee8e77997e06e0b4f2e14b
BSD-3-Clause
2021-05-04T20:02:20.000Z
2022-03-25T22:08:49.000Z
tutorials/tutorial-9.complex-plots.ipynb
Davide-sd/sympy-plot-backends
f8ad8a5d7aef3c55ddee8e77997e06e0b4f2e14b
BSD-3-Clause
2021-07-23T20:38:14.000Z
2022-02-10T09:20:47.000Z
Qwen/Qwen-72B
1. YES
2. YES