diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/3d_surface_wireframe.py b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/3d_surface_wireframe.py new file mode 100644 index 0000000000..96ac2bdb65 --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/3d_surface_wireframe.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 +# Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# The Universal Permissive License (UPL), Version 1.0 +# +# Subject to the condition set forth below, permission is hereby granted to any +# person obtaining a copy of this software, associated documentation and/or +# data (collectively the "Software"), free of charge and under any and all +# copyright rights in the Software, and any and all patent rights owned or +# freely licensable by each licensor hereunder covering either (i) the +# unmodified Software as contributed to or provided by such licensor, or (ii) +# the Larger Works (as defined below), to deal in both +# +# (a) the Software, and +# +# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +# one is included with the Software each a "Larger Work" to which the Software +# is contributed by such licensors), +# +# without restriction, including without limitation the rights to copy, create +# derivative works of, display, perform, and distribute the Software and make, +# use, sell, offer for sale, import, export, have made, and have sold the +# Software and the Larger Work(s), and to sublicense the foregoing rights on +# either these or other terms. +# +# This license is subject to the following condition: +# +# The above copyright notice and either this complete permission notice or at a +# minimum a reference to the UPL must be included in all copies or substantial +# portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +3D plotting example: surface, wireframe, and contour projections. +Saves to PDF. +""" + + +def run(): + # Execute all imports inside the `run` method so they're measured + from pathlib import Path + # Use a non-interactive backend to work in headless environments + import matplotlib + matplotlib.use("Agg") + import matplotlib.pyplot as plt + import numpy as np + + # Ensure we have version info in the logs + print(f"Using matplotlib version '{matplotlib.__version__}'") + print(f"Using numpy version '{np.__version__}'") + + out_path = Path(__file__).parent / "surface_3d.pdf" + + # Domain and function + x = np.linspace(-4, 4, 200) + y = np.linspace(-4, 4, 200) + X, Y = np.meshgrid(x, y) + + R = np.sqrt(X**2 + Y**2) + 1e-12 + Z = np.sin(R) / R + 0.15 * np.cos(3*X) * np.sin(3*Y) / (1 + 0.5 * (X**2 + Y**2)) + + fig = plt.figure(figsize=(7.5, 5.8), dpi=150) + ax = fig.add_subplot(111, projection="3d") + + # Surface with colormap + surf = ax.plot_surface(X, Y, Z, cmap="viridis", linewidth=0, antialiased=True, alpha=0.95) + + # Wireframe overlay (sparser grid to avoid clutter) + step = 10 + ax.plot_wireframe(X[::step, ::step], Y[::step, ::step], Z[::step, ::step], + rstride=1, cstride=1, color="k", linewidth=0.3, alpha=0.5) + + # Contour projections on Z, X, and Y planes + z_offset = Z.min() - 0.4 + ax.contour(X, Y, Z, zdir="z", offset=z_offset, cmap="viridis", levels=18, linewidths=0.8) + + x_offset = x.min() - 0.6 + ax.contour(X, Y, Z, zdir="x", offset=x_offset, cmap="magma", levels=14, linewidths=0.7) + + y_offset = y.max() + 0.6 + ax.contour(X, Y, Z, zdir="y", offset=y_offset, cmap="plasma", levels=14, linewidths=0.7) + + # Axes labels and limits + ax.set_xlabel("X") + ax.set_ylabel("Y") + ax.set_zlabel("Z") + + ax.set_xlim(x_offset, x.max()) + ax.set_ylim(y.min(), y_offset) + ax.set_zlim(z_offset, Z.max()) + + # Colorbar + cb = fig.colorbar(surf, ax=ax, shrink=0.6, aspect=12, pad=0.08) + cb.set_label("Z value") + + # View angle + ax.view_init(elev=25, azim=-55) + + ax.set_title("3D Surface + Wireframe + Contour Projections") + fig.tight_layout() + + fig.savefig(out_path, format="pdf") + plt.close(fig) + + +def warmupIterations(): + return 0 + + +def iterations(): + return 1 + + +def summary(): + return { + "name": "OutlierRemovalAverageSummary", + "lower-threshold": 0.0, + "upper-threshold": 1.0, + } diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/OWNERS.toml b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/OWNERS.toml new file mode 100644 index 0000000000..34cb11f338 --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/OWNERS.toml @@ -0,0 +1,6 @@ +[[rule]] +files = "*" +any = [ + "francois.farquet@oracle.com", + "andrija.kolic@oracle.com", +] diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/categorical_bar_and_box.py b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/categorical_bar_and_box.py new file mode 100644 index 0000000000..6fa26ce37a --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/categorical_bar_and_box.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 +# Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# The Universal Permissive License (UPL), Version 1.0 +# +# Subject to the condition set forth below, permission is hereby granted to any +# person obtaining a copy of this software, associated documentation and/or +# data (collectively the "Software"), free of charge and under any and all +# copyright rights in the Software, and any and all patent rights owned or +# freely licensable by each licensor hereunder covering either (i) the +# unmodified Software as contributed to or provided by such licensor, or (ii) +# the Larger Works (as defined below), to deal in both +# +# (a) the Software, and +# +# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +# one is included with the Software each a "Larger Work" to which the Software +# is contributed by such licensors), +# +# without restriction, including without limitation the rights to copy, create +# derivative works of, display, perform, and distribute the Software and make, +# use, sell, offer for sale, import, export, have made, and have sold the +# Software and the Larger Work(s), and to sublicense the foregoing rights on +# either these or other terms. +# +# This license is subject to the following condition: +# +# The above copyright notice and either this complete permission notice or at a +# minimum a reference to the UPL must be included in all copies or substantial +# portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +Categorical plots: grouped bar chart with error bars and a boxplot. +Saves a multi-page PDF using PdfPages. +""" + + +def _colorize_boxplot(bp, facecolor="#1f77b4", edgecolor="black", alpha=0.6): + for box in bp["boxes"]: + box.set(facecolor=facecolor, edgecolor=edgecolor, alpha=alpha) + for median in bp["medians"]: + median.set(color="black", linewidth=1.2) + for whisker in bp["whiskers"]: + whisker.set(color=edgecolor, linewidth=1.0) + for cap in bp["caps"]: + cap.set(color=edgecolor, linewidth=1.0) + for flier in bp["fliers"]: + flier.set(marker="o", markersize=3, markerfacecolor="white", markeredgecolor=edgecolor, alpha=0.7) + + +def run(): + # Execute all imports inside the `run` method so they're measured + from pathlib import Path + # Use a non-interactive backend to work in headless environments + import matplotlib + matplotlib.use("Agg") + import matplotlib.pyplot as plt + import numpy as np + from matplotlib.backends.backend_pdf import PdfPages + + # Ensure we have version info in the logs + print(f"Using matplotlib version '{matplotlib.__version__}'") + print(f"Using numpy version '{np.__version__}'") + + out_path = Path(__file__).parent / "categorical_plots.pdf" + + rng = np.random.default_rng(2024) + categories = ["A", "B", "C", "D"] + n = len(categories) + + with PdfPages(out_path) as pdf: + # Page 1: Grouped bar chart with error bars + x = np.arange(n) + bar_w = 0.35 + + means1 = rng.normal(3.0, 0.4, n) + errs1 = rng.uniform(0.1, 0.4, n) + + means2 = rng.normal(2.2, 0.5, n) + errs2 = rng.uniform(0.1, 0.4, n) + + fig1, ax1 = plt.subplots(figsize=(7, 4), dpi=150) + b1 = ax1.bar(x - bar_w / 2, means1, yerr=errs1, width=bar_w, capsize=3, + label="Series 1", color="#1f77b4", edgecolor="black", alpha=0.85) + b2 = ax1.bar(x + bar_w / 2, means2, yerr=errs2, width=bar_w, capsize=3, + label="Series 2", color="#ff7f0e", edgecolor="black", alpha=0.85) + + ax1.set_xticks(x, categories) + ax1.set_ylabel("Value") + ax1.set_title("Grouped Bar Chart with Error Bars") + ax1.grid(axis="y", linestyle="--", alpha=0.35) + ax1.legend(loc="best") + + # Annotate bars with heights + for bars in (b1, b2): + for rect in bars: + h = rect.get_height() + ax1.text(rect.get_x() + rect.get_width() / 2.0, h + 0.05, + f"{h:.2f}", ha="center", va="bottom", fontsize=8, rotation=0) + + fig1.tight_layout() + pdf.savefig(fig1) + plt.close(fig1) + + # Page 2: Boxplot across categories + # Generate some synthetic distributions with varying mean/variance + mus = [2.8, 3.2, 2.5, 3.5] + sigmas = [0.50, 0.60, 0.45, 0.55] + data = [rng.normal(loc=m, scale=s, size=400) for m, s in zip(mus, sigmas)] + + fig2, ax2 = plt.subplots(figsize=(7, 4), dpi=150) + bp = ax2.boxplot( + data, + labels=categories, + widths=0.6, + patch_artist=True, + showfliers=True, + whis=(5, 95), + ) + # Colorize boxes with a palette + palette = ["#1f77b4", "#ff7f0e", "#2ca02c", "#9467bd"] + for box, color in zip(bp["boxes"], palette): + box.set(facecolor=color, edgecolor="black", alpha=0.6) + + # Style the rest + for median in bp["medians"]: + median.set(color="black", linewidth=1.4) + for whisker in bp["whiskers"]: + whisker.set(color="black", linewidth=1.0) + for cap in bp["caps"]: + cap.set(color="black", linewidth=1.0) + for flier in bp["fliers"]: + flier.set(marker="o", markersize=3, markerfacecolor="white", markeredgecolor="black", alpha=0.7) + + ax2.set_title("Boxplot by Category") + ax2.set_ylabel("Distribution") + ax2.grid(axis="y", linestyle="--", alpha=0.35) + fig2.tight_layout() + + pdf.savefig(fig2) + plt.close(fig2) + + +def warmupIterations(): + return 0 + + +def iterations(): + return 1 + + +def summary(): + return { + "name": "OutlierRemovalAverageSummary", + "lower-threshold": 0.0, + "upper-threshold": 1.0, + } diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/distributions_hist_2d.py b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/distributions_hist_2d.py new file mode 100644 index 0000000000..092d2aa746 --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/distributions_hist_2d.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python3 +# Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# The Universal Permissive License (UPL), Version 1.0 +# +# Subject to the condition set forth below, permission is hereby granted to any +# person obtaining a copy of this software, associated documentation and/or +# data (collectively the "Software"), free of charge and under any and all +# copyright rights in the Software, and any and all patent rights owned or +# freely licensable by each licensor hereunder covering either (i) the +# unmodified Software as contributed to or provided by such licensor, or (ii) +# the Larger Works (as defined below), to deal in both +# +# (a) the Software, and +# +# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +# one is included with the Software each a "Larger Work" to which the Software +# is contributed by such licensors), +# +# without restriction, including without limitation the rights to copy, create +# derivative works of, display, perform, and distribute the Software and make, +# use, sell, offer for sale, import, export, have made, and have sold the +# Software and the Larger Work(s), and to sublicense the foregoing rights on +# either these or other terms. +# +# This license is subject to the following condition: +# +# The above copyright notice and either this complete permission notice or at a +# minimum a reference to the UPL must be included in all copies or substantial +# portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +Distribution plots: 1D histogram, 2D histogram, and hexbin with colorbars. +""" + + +def run(): + # Execute all imports inside the `run` method so they're measured + from pathlib import Path + # Use a non-interactive backend to work in headless environments + import matplotlib + matplotlib.use("Agg") + import matplotlib.pyplot as plt + import numpy as np + + # Ensure we have version info in the logs + print(f"Using matplotlib version '{matplotlib.__version__}'") + print(f"Using numpy version '{np.__version__}'") + + out_path = Path(__file__).parent / "distributions_2d.pdf" + + rng = np.random.default_rng(7) + n = 6000 + + # Create two correlated variables + x = rng.normal(0.0, 1.0, n) + y = 0.65 * x + rng.normal(0.0, 0.8, n) + + fig, axs = plt.subplots(1, 3, figsize=(12, 4), dpi=150) + + # 1) 1D histograms overlayed + ax = axs[0] + ax.hist(x, bins=40, alpha=0.8, label="x", color="#1f77b4", edgecolor="white") + ax.hist(y, bins=40, alpha=0.6, label="y", color="#ff7f0e", edgecolor="white") + ax.set_title("1D Histograms") + ax.set_xlabel("Value") + ax.set_ylabel("Frequency") + ax.grid(True, linestyle="--", alpha=0.3) + ax.legend(loc="best") + + # 2) 2D histogram via pcolormesh + ax = axs[1] + H, xedges, yedges = np.histogram2d(x, y, bins=60) + X, Y = np.meshgrid(xedges, yedges) + pcm = ax.pcolormesh(X, Y, H.T, cmap="viridis", shading="auto") + ax.set_title("2D Histogram") + ax.set_xlabel("x") + ax.set_ylabel("y") + cb = fig.colorbar(pcm, ax=ax) + cb.set_label("Count") + ax.grid(False) + + # 3) Hexbin with log color scale + ax = axs[2] + hb = ax.hexbin(x, y, gridsize=45, cmap="plasma", mincnt=1, bins="log") + ax.set_title("Hexbin (log density)") + ax.set_xlabel("x") + ax.set_ylabel("y") + cb = fig.colorbar(hb, ax=ax) + cb.set_label("log10(count)") + ax.grid(False) + + fig.suptitle("Distributions: 1D and 2D Density", fontsize=14) + fig.tight_layout(rect=[0, 0.03, 1, 0.95]) + + fig.savefig(out_path, format="pdf") + plt.close(fig) + + +def warmupIterations(): + return 0 + + +def iterations(): + return 1 + + +def summary(): + return { + "name": "OutlierRemovalAverageSummary", + "lower-threshold": 0.0, + "upper-threshold": 1.0, + } diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/polar_quiver_stream.py b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/polar_quiver_stream.py new file mode 100644 index 0000000000..192d984f63 --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/polar_quiver_stream.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +# Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# The Universal Permissive License (UPL), Version 1.0 +# +# Subject to the condition set forth below, permission is hereby granted to any +# person obtaining a copy of this software, associated documentation and/or +# data (collectively the "Software"), free of charge and under any and all +# copyright rights in the Software, and any and all patent rights owned or +# freely licensable by each licensor hereunder covering either (i) the +# unmodified Software as contributed to or provided by such licensor, or (ii) +# the Larger Works (as defined below), to deal in both +# +# (a) the Software, and +# +# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +# one is included with the Software each a "Larger Work" to which the Software +# is contributed by such licensors), +# +# without restriction, including without limitation the rights to copy, create +# derivative works of, display, perform, and distribute the Software and make, +# use, sell, offer for sale, import, export, have made, and have sold the +# Software and the Larger Work(s), and to sublicense the foregoing rights on +# either these or other terms. +# +# This license is subject to the following condition: +# +# The above copyright notice and either this complete permission notice or at a +# minimum a reference to the UPL must be included in all copies or substantial +# portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +Vector and polar plots: polar plot, quiver, and streamplot on separate pages. +Saves a multi-page PDF using PdfPages. +""" + + +def page_polar(ax): + # Polar demo with multiple radii and an area fill + import numpy as np + theta = np.linspace(0, 2*np.pi, 512) + r1 = 1.0 + 0.3*np.sin(5*theta) + r2 = 0.7 + 0.2*np.cos(3*theta + 0.5) + + ax.plot(theta, r1, color="#1f77b4", linewidth=2.0, label="r1(θ) = 1 + 0.3 sin(5θ)") + ax.plot(theta, r2, color="#ff7f0e", linewidth=2.0, linestyle="--", label="r2(θ) = 0.7 + 0.2 cos(3θ+0.5)") + ax.fill_between(theta, r2, color="#ff7f0e", alpha=0.25, step="mid") + ax.set_theta_zero_location("N") + ax.set_theta_direction(-1) + ax.set_title("Polar Plot", va="bottom") + ax.grid(True, alpha=0.4) + ax.legend(loc="upper right", bbox_to_anchor=(1.25, 1.15), frameon=False) + + +def page_quiver(ax): + # Quiver plot for a simple rotational vector field + import numpy as np + n = 25 + x = np.linspace(-2.0, 2.0, n) + y = np.linspace(-2.0, 2.0, n) + X, Y = np.meshgrid(x, y) + + # Vector field: rotation around origin + U = -Y + V = X + speed = np.hypot(U, V) + + q = ax.quiver(X, Y, U, V, speed, cmap="viridis", pivot="mid", angles="xy", scale=35, width=0.006) + ax.set_aspect("equal", adjustable="box") + ax.set_title("Quiver: Rotational Field") + ax.set_xlabel("x") + ax.set_ylabel("y") + cb = ax.figure.colorbar(q, ax=ax, pad=0.01) + cb.set_label("|v|") + ax.grid(True, linestyle="--", alpha=0.3) + + +def page_streamplot(ax): + # Streamplot with linewidth and color mapped to speed + import numpy as np + x = np.linspace(-3.0, 3.0, 200) + y = np.linspace(-3.0, 3.0, 200) + X, Y = np.meshgrid(x, y) + + # Double-vortex-like field + U = 1 - (X**2) + (Y**2) + V = -2*X*Y + speed = np.sqrt(U**2 + V**2) + + lw = 1.5 * speed / (speed.max() + 1e-12) + strm = ax.streamplot(X, Y, U, V, color=speed, linewidth=lw, cmap="plasma", density=1.4, arrowsize=1.2) + ax.set_aspect("equal", adjustable="box") + ax.set_title("Streamplot: Speed-coded") + ax.set_xlabel("x") + ax.set_ylabel("y") + cb = ax.figure.colorbar(strm.lines, ax=ax, pad=0.01) + cb.set_label("|v|") + ax.grid(True, linestyle="--", alpha=0.25) + + +def run(): + # Execute all imports inside the `run` method so they're measured + from pathlib import Path + # Use a non-interactive backend to work in headless environments + import matplotlib + matplotlib.use("Agg") + import matplotlib.pyplot as plt + import numpy as np + from matplotlib.backends.backend_pdf import PdfPages + + # Ensure we have version info in the logs + print(f"Using matplotlib version '{matplotlib.__version__}'") + print(f"Using numpy version '{np.__version__}'") + + out_path = Path(__file__).parent / "vector_and_polar.pdf" + + with PdfPages(out_path) as pdf: + # Page 1: Polar + fig1 = plt.figure(figsize=(6.2, 5.5), dpi=150) + ax1 = fig1.add_subplot(111, projection="polar") + page_polar(ax1) + fig1.tight_layout() + pdf.savefig(fig1) + plt.close(fig1) + + # Page 2: Quiver + fig2, ax2 = plt.subplots(figsize=(6.2, 5.0), dpi=150) + page_quiver(ax2) + fig2.tight_layout() + pdf.savefig(fig2) + plt.close(fig2) + + # Page 3: Streamplot + fig3, ax3 = plt.subplots(figsize=(6.6, 5.2), dpi=150) + page_streamplot(ax3) + fig3.tight_layout() + pdf.savefig(fig3) + plt.close(fig3) + + +def warmupIterations(): + return 0 + + +def iterations(): + return 1 + + +def summary(): + return { + "name": "OutlierRemovalAverageSummary", + "lower-threshold": 0.0, + "upper-threshold": 1.0, + } diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/simple_line_plot.py b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/simple_line_plot.py new file mode 100644 index 0000000000..8333b80f53 --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/simple_line_plot.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +# Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# The Universal Permissive License (UPL), Version 1.0 +# +# Subject to the condition set forth below, permission is hereby granted to any +# person obtaining a copy of this software, associated documentation and/or +# data (collectively the "Software"), free of charge and under any and all +# copyright rights in the Software, and any and all patent rights owned or +# freely licensable by each licensor hereunder covering either (i) the +# unmodified Software as contributed to or provided by such licensor, or (ii) +# the Larger Works (as defined below), to deal in both +# +# (a) the Software, and +# +# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +# one is included with the Software each a "Larger Work" to which the Software +# is contributed by such licensors), +# +# without restriction, including without limitation the rights to copy, create +# derivative works of, display, perform, and distribute the Software and make, +# use, sell, offer for sale, import, export, have made, and have sold the +# Software and the Larger Work(s), and to sublicense the foregoing rights on +# either these or other terms. +# +# This license is subject to the following condition: +# +# The above copyright notice and either this complete permission notice or at a +# minimum a reference to the UPL must be included in all copies or substantial +# portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +Simple line plot example that saves to PDF. +""" + + +def run(): + # Execute all imports inside the `run` method so they're measured + from pathlib import Path + # Use a non-interactive backend to work in headless environments + import matplotlib + matplotlib.use("Agg") + import matplotlib.pyplot as plt + import numpy as np + + # Ensure we have version info in the logs + print(f"Using matplotlib version '{matplotlib.__version__}'") + print(f"Using numpy version '{np.__version__}'") + + out_path = Path(__file__).parent / "simple_line_plot.pdf" + + # Reproducible data + rng = np.random.default_rng(42) + x = np.linspace(0.0, 10.0, 200) + y = np.sin(x) + 0.15 * rng.standard_normal(x.size) + + plt.figure(figsize=(6, 4), dpi=150) + plt.plot(x, np.sin(x), label="sin(x)", color="#1f77b4", linewidth=2.0) + plt.scatter(x[::8], y[::8], label="samples", color="#ff7f0e", s=15, alpha=0.85) + plt.title("Simple Line + Sampled Points") + plt.xlabel("x") + plt.ylabel("y") + plt.grid(True, linestyle="--", alpha=0.4) + plt.legend(loc="best") + plt.tight_layout() + + plt.savefig(out_path, format="pdf") + plt.close() + + +def warmupIterations(): + return 0 + + +def iterations(): + return 1 + + +def summary(): + return { + "name": "OutlierRemovalAverageSummary", + "lower-threshold": 0.0, + "upper-threshold": 1.0, + } diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/subplots_and_styles.py b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/subplots_and_styles.py new file mode 100644 index 0000000000..011a2b612b --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/subplots_and_styles.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 +# Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# The Universal Permissive License (UPL), Version 1.0 +# +# Subject to the condition set forth below, permission is hereby granted to any +# person obtaining a copy of this software, associated documentation and/or +# data (collectively the "Software"), free of charge and under any and all +# copyright rights in the Software, and any and all patent rights owned or +# freely licensable by each licensor hereunder covering either (i) the +# unmodified Software as contributed to or provided by such licensor, or (ii) +# the Larger Works (as defined below), to deal in both +# +# (a) the Software, and +# +# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +# one is included with the Software each a "Larger Work" to which the Software +# is contributed by such licensors), +# +# without restriction, including without limitation the rights to copy, create +# derivative works of, display, perform, and distribute the Software and make, +# use, sell, offer for sale, import, export, have made, and have sold the +# Software and the Larger Work(s), and to sublicense the foregoing rights on +# either these or other terms. +# +# This license is subject to the following condition: +# +# The above copyright notice and either this complete permission notice or at a +# minimum a reference to the UPL must be included in all copies or substantial +# portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +Subplots and style variations; saves a multi-panel PDF. +""" + + +def run(): + # Execute all imports inside the `run` method so they're measured + from pathlib import Path + # Use a non-interactive backend to work in headless environments + import matplotlib + matplotlib.use("Agg") + import matplotlib.pyplot as plt + import numpy as np + + # Ensure we have version info in the logs + print(f"Using matplotlib version '{matplotlib.__version__}'") + print(f"Using numpy version '{np.__version__}'") + + out_path = Path(__file__).parent / "subplots_and_styles.pdf" + + rng = np.random.default_rng(123) + x = np.linspace(0, 2*np.pi, 200) + y1 = np.sin(x) + y2 = np.cos(x) + y3 = np.sin(2*x) * np.exp(-0.3*x) + y4 = rng.normal(loc=0.0, scale=1.0, size=200) + + fig, axs = plt.subplots(2, 2, figsize=(8, 6), dpi=150) + + # 1) Basic line styles + ax = axs[0, 0] + ax.plot(x, y1, label="sin(x)", color="#1f77b4", linewidth=2.0) + ax.plot(x, y2, label="cos(x)", color="#ff7f0e", linestyle="--", linewidth=2.0) + ax.set_title("Line Styles") + ax.set_xlabel("x") + ax.set_ylabel("y") + ax.grid(True, linestyle=":", alpha=0.5) + ax.legend(loc="best") + + # 2) Markers and transparency + ax = axs[0, 1] + ax.plot(x, y3, color="#2ca02c", linewidth=1.5) + ax.scatter(x[::10], y3[::10], color="#d62728", s=20, alpha=0.8, label="samples") + ax.set_title("Markers and Decay") + ax.annotate("decay", xy=(2.0, y3[np.searchsorted(x, 2.0)]), xytext=(3.5, 0.8), + arrowprops=dict(arrowstyle="->", color="gray"), color="gray") + ax.grid(True, alpha=0.4) + ax.legend(loc="best") + + # 3) Simple bar chart + ax = axs[1, 0] + categories = ["A", "B", "C", "D", "E"] + values = np.abs(rng.normal(3.0, 1.0, size=len(categories))) + bars = ax.bar(categories, values, color="#9467bd", edgecolor="black", alpha=0.85) + for b in bars: + ax.text(b.get_x() + b.get_width()/2, b.get_height() + 0.05, + f"{b.get_height():.1f}", ha="center", va="bottom", fontsize=8) + ax.set_title("Bar Chart") + ax.set_ylabel("Value") + ax.set_ylim(0, max(values) * 1.2) + ax.grid(axis="y", linestyle="--", alpha=0.3) + + # 4) Histogram with style + ax = axs[1, 1] + ax.hist(y4, bins=20, color="#8c564b", edgecolor="white", alpha=0.9) + ax.set_title("Histogram") + ax.set_xlabel("Value") + ax.set_ylabel("Frequency") + ax.grid(True, linestyle="--", alpha=0.3) + + fig.suptitle("Subplots and Styles", fontsize=14) + fig.tight_layout(rect=[0, 0.03, 1, 0.95]) + + fig.savefig(out_path, format="pdf") + plt.close(fig) + + +def warmupIterations(): + return 0 + + +def iterations(): + return 1 + + +def summary(): + return { + "name": "OutlierRemovalAverageSummary", + "lower-threshold": 0.0, + "upper-threshold": 1.0, + } diff --git a/mx.graalpython/OWNERS.toml b/mx.graalpython/OWNERS.toml new file mode 100644 index 0000000000..9e9526a0fe --- /dev/null +++ b/mx.graalpython/OWNERS.toml @@ -0,0 +1,6 @@ +[[rule]] +files = "polybench-stable-run-config.json" +any = [ + "francois.farquet@oracle.com", + "andrija.kolic@oracle.com", +] diff --git a/mx.graalpython/polybench-stable-run-config.json b/mx.graalpython/polybench-stable-run-config.json index 3341e29995..f7a1ab9c5b 100644 --- a/mx.graalpython/polybench-stable-run-config.json +++ b/mx.graalpython/polybench-stable-run-config.json @@ -726,5 +726,35 @@ "policy": "outlier-elimination-all-builds", "forks": "1x1", "focus": "0.0-1.0" + }, + "warmup/matplotlib/3d_surface_wireframe.py": { + "policy": "outlier-elimination-all-builds", + "forks": "5x3", + "focus": "0.3-0.7" + }, + "warmup/matplotlib/categorical_bar_and_box.py": { + "policy": "outlier-elimination-all-builds", + "forks": "5x3", + "focus": "0.4-0.6" + }, + "warmup/matplotlib/distributions_hist_2d.py": { + "policy": "outlier-elimination-all-builds", + "forks": "3x5", + "focus": "0.4-0.9" + }, + "warmup/matplotlib/polar_quiver_stream.py": { + "policy": "outlier-elimination-all-builds", + "forks": "0x0", + "focus": "0.5-0.85" + }, + "warmup/matplotlib/simple_line_plot.py": { + "policy": "outlier-elimination-all-builds", + "forks": "4x3", + "focus": "0.0-0.4" + }, + "warmup/matplotlib/subplots_and_styles.py": { + "policy": "outlier-elimination-all-builds", + "forks": "5x5", + "focus": "0.3-0.7" } } diff --git a/mx.graalpython/suite.py b/mx.graalpython/suite.py index a67f5946f3..07d4f2b413 100644 --- a/mx.graalpython/suite.py +++ b/mx.graalpython/suite.py @@ -1652,6 +1652,9 @@ "file:benchmarks/warmup/*.py", "dependency:GRAALPYTHON_PYFLATE_BENCHMARK_RESOURCE", ], + "./warmup/matplotlib/": [ + "file:graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/*.py", + ], }, },