pythonmatplotlibplotscatter-plotgenerative-art

Matplotlib Scatter Plot Returns Different Plots in Different Versions


I'm working on a generative art generator library for python called samila based on matplotlib scatter plot. It gets two functions and maps a square space into a arbitrary shape. We want the generated shape to be the same for given functions and given random seed in order to be reproducible.

Recently we were working on functions with complex values and notified that scatter plot output is not the same in different versions on matplotlib.

I wanted to know why is it like this and what's the problem with matplotlib. If this is a bug it could be horrible for matplotlib to plot different figures for a specific code in its different versions.

So if you run bellow code using matplotlib==3.4.3:

from samila import *
import math
import matplotlib.pyplot as plt

def f1(x, y):
    return math.cos(x**2 * y)**1.926 - math.floor(x - y)**1.861 - math.floor(y**2 * x)**1.688

def f2(x, y):
    return x - y**1.617 - math.ceil(y)**1.477 - abs(x**2 * y) ** 1.647 - math.cos(x * y)**1.668

GI = GenerativeImage(f1, f2)
GI.generate(seed=755398)
GI.plot(color=(0.159, 0.085, 0.191), projection=Projection.POLAR, spot_size=2)
GI.save_image('art.png')
plt.show()

You'll have bellow warning :

/home/user/.local/lib/python3.8/site-packages/numpy/core/_asarray.py:136: ComplexWarning: Casting complex values to real discards the imaginary part
  return array(a, dtype, copy=False, order=order, subok=True)

With bellow generated art: Samila Art for matplotlib==3.4.3


And if you run the code using matplotlib==3.0.3 you'll have:

Attribute Qt::AA_EnableHighDpiScaling must be set before QCoreApplication is created.
/home/user/.local/lib/python3.8/site-packages/numpy/core/_asarray.py:136: ComplexWarning: Casting complex values to real discards the imaginary part
  return array(a, dtype, copy=False, order=order, subok=True)

Samila Art for matplotlib==3.0.3


[Edit] : I added a example which uses matplotlib directly instead of using it through Samila. If you prefer you can use this script instead of previous one.

import random
import math
import matplotlib.pyplot as plt
import itertools

def f1(x,y):
    return math.cos(x**2 * y)**1.926 - math.floor(x - y)**1.861 - math.floor(y**2 * x)**1.688
def f2(x,y):
    return x - y**1.617 - math.ceil(y)**1.477 - abs(x**2 * y) ** 1.647 - math.cos(x * y)**1.668
def float_range(start, stop, step):
    while start < stop:
        yield float(start)
        start += step


data1 = []
data2 = []
range1 = list(float_range(-1*math.pi, math.pi, 0.01))
range_prod = list(itertools.product(range1, range1))
for item in range_prod:
    data1.append(f1(item[0], item[1]))
    data2.append(f2(item[0], item[1]))

color = (0.159, 0.085, 0.191)
spot_size = 0.01
projection = "polar"

fig = plt.figure()
fig.set_size_inches(10, 10)
ax = fig.add_subplot(111, projection=projection)
ax.scatter(
        data2,
        data1,
        alpha=0.1,
        edgecolors=color,
        s=spot_size)
ax.set_axis_off()
ax.patch.set_zorder(-1)
ax.add_artist(ax.patch)

plt.show()


System Details:


Solution

  • The problem was that matplotlib changes its plotting strategy from 3.0.3 which ignores points with negative radius to 3.4.3 in which they're plotted.

    I couldn't find this using compare release notes and it was so confusing for me. Hope there will be an warning for this situation avoiding future issues.

    I tested bellow code which is way simpler:

    data1 = [-2, -5,  2, 2]
    data2 = [-2, 2, -2, 2]
    
    fig = plt.figure()
    ax = fig.add_subplot(111, projection="polar")
    ax.scatter(
            data2,
            data1,
            s=30)
    
    plt.show()
    

    in matplotlib.__version__ == 3.0.3 we got:

    enter image description here

    while in matplotlib.__version__ == 3.4.3:

    enter image description here