Source code for beeoptimal.plotting

#++++++++++++++++++++++++++++++++++++
# Libraries and modules
#++++++++++++++++++++++++++++++++++++

import numpy as np
import plotly.graph_objects as go
from PIL import Image
from .utils import get_marker_path
from .benchmarks import BenchmarkFunction
from .bee import Bee

#++++++++++++++++++++++++++++++++++++
# Plotting functions
#++++++++++++++++++++++++++++++++++++

[docs] def contourplot(function, title=None, bounds=None,zoom=1.0,figsize=(600,600)): """ Plots a 2D benchmark function as a contour plot. Args: function (BenchmarkFunction) : A benchmark function object. title (str,optional) : Title of the plot. Defaults to empty string. bounds (numpy.ndarray, optional) : Custom bounds for the plot (different from the default ones). Defaults to None (uses the default bounds). zoom (float, optional) : Zoom factor for the plot. Defaults to 1.0 (no zoom). figsize (tuple, optional) : Size of the figure. Defaults to (600,600). Returns: plotly.graph_objects.Figure: A Plotly figure containing the contour plot of the function. Raises: TypeError : If `function` is not a `BenchmarkFunction` object. TypeError : If `bounds` is not a NumPy array (when `bounds` is provided). ValueError : If `bounds` does not have shape (2, 2) (when `bounds` is provided). ValueError : If `zoom` is not greater than zero. """ if not isinstance(function, BenchmarkFunction): raise TypeError("`function` must be a `BenchmarkFunction` object.") if bounds is not None: if not isinstance(bounds, np.ndarray): raise TypeError("`bounds` must be provided as a NumPy array.") if bounds.shape != (2, 2): raise ValueError(f"`bounds` must have shape (2, 2), but got {bounds.shape}") if not (isinstance(zoom, (int, float)) and zoom > 0): raise ValueError(f"`zoom` must be greater than zero, but got {zoom}") # Determine the bounds (use predefined ones if no custom bounds are provided) if bounds is not None: x_bounds = (bounds[0, 0], bounds[0, 1]) y_bounds = (bounds[1, 0], bounds[1, 1]) else: x_bounds = (function.bounds[0, 0], function.bounds[0, 1]) y_bounds = (function.bounds[1, 0], function.bounds[1, 1]) # Apply zoom factor x_range = x_bounds[1] - x_bounds[0] y_range = y_bounds[1] - y_bounds[0] x_center = (x_bounds[0] + x_bounds[1]) / 2 y_center = (y_bounds[0] + y_bounds[1]) / 2 x_bounds = ( x_center - (x_range / 2) * zoom, x_center + (x_range / 2) * zoom ) y_bounds = ( y_center - (y_range / 2) * zoom, y_center + (y_range / 2) * zoom ) # Generate grid and make contourplot x = np.linspace(x_bounds[0], x_bounds[1], 100) y = np.linspace(y_bounds[0], y_bounds[1], 100) X, Y = np.meshgrid(x, y) points = np.c_[X.ravel(), Y.ravel()] Z = np.array([function.evaluate(p) for p in points]).reshape(X.shape) fig = go.Figure( data=go.Contour( x=x, y=y, z=Z, colorscale='Oranges', opacity=0.6, contours=dict( showlabels=False, labelfont=dict(size=12,color='white') ) ) ) fig.update_layout( title= str(title), xaxis_title='x1', yaxis_title='x2', width=figsize[0], height=figsize[1] ) fig.update_xaxes(showgrid=False) fig.update_yaxes(showgrid=False) return fig
[docs] def surfaceplot(function,title='',bounds=None,zoom=1.0,figsize=(600,600)): """ Plots the surface of a 2D benchmark function. Args: function (BenchmarkFunction) : A benchmark function object. title (str,optional) : Title of the plot. Defaults to empty string. bounds (numpy.ndarray, optional) : Custom bounds for the plot (different from the default ones). Defaults to None (uses the default bounds). zoom (float, optional) : Zoom factor for the plot. Defaults to 1.0 (no zoom). figsize (tuple,optional) : Size of the figure. Defaults to (600, 600). Returns: plotly.graph_objects.Figure: A Plotly figure containing the surface plot of the function. Raises: TypeError : If `function` is not a `BenchmarkFunction` object. TypeError : If `bounds` is not a NumPy array (when bounds is provided). ValueError : If `bounds` does not have shape (2, 2) (when `bounds` is provided). ValueError : If `zoom` is not greater than zero. """ # Input checks if not isinstance(function, BenchmarkFunction): raise TypeError("`function` must be a `BenchmarkFunction` object.") if bounds is not None: if not isinstance(bounds, np.ndarray): raise TypeError("`bounds` must be provided as a NumPy array.") if bounds.shape != (2, 2): raise ValueError("`bounds` must have shape (2, 2).") if not (isinstance(zoom, (int, float)) and zoom > 0): raise ValueError(f"`zoom` must be greater than zero, but got {zoom}") # Determine the bounds (use predefined ones if no custom bounds are provided) if bounds is not None: x_bounds = (bounds[0, 0], bounds[0, 1]) y_bounds = (bounds[1, 0], bounds[1, 1]) else: x_bounds = (function.bounds[0, 0], function.bounds[0, 1]) y_bounds = (function.bounds[1, 0], function.bounds[1, 1]) # Apply zoom factor x_range = x_bounds[1] - x_bounds[0] y_range = y_bounds[1] - y_bounds[0] x_center = (x_bounds[0] + x_bounds[1]) / 2 y_center = (y_bounds[0] + y_bounds[1]) / 2 x_bounds = ( x_center - (x_range / 2) * zoom, x_center + (x_range / 2) * zoom ) y_bounds = ( y_center - (y_range / 2) * zoom, y_center + (y_range / 2) * zoom ) # Generate grid and make surfaceplot x = np.linspace(x_bounds[0], x_bounds[1], 100) y = np.linspace(y_bounds[0], y_bounds[1], 100) X, Y = np.meshgrid(x, y) points = np.c_[X.ravel(), Y.ravel()] Z = np.array([function.evaluate(p) for p in points]).reshape(X.shape) fig = go.Figure(data=[go.Surface(z=Z, x=x, y=y, colorscale='Oranges')]) fig.update_layout( title = str(title), scene = dict( xaxis_title='x1', yaxis_title='x2', zaxis_title='f(x1,x2)', ), width=figsize[0], height=figsize[1] ) return fig
[docs] def contourplot_bees(function,bee_colony,optimal_solution=None,title='',bounds=None,zoom=1.0,bee_marker_size=None,figsize=(600,600)): """ Create a contour plot with bee markers and an optional optimal solution point. Args: function (BenchmarkFunction) : A benchmark function object. bee_colony (list of Bee) : List of Bee objects. optimal_solution (numpy.ndarray) : The optimal solution point. Defaults to None. title (str) : Title of the plot. Defaults to empty string. bounds (numpy.ndarray, optional) : Custom bounds for the plot (different from the default ones). Defaults to None (uses the default bounds). zoom (float, optional) : Zoom factor for the plot. Defaults to 1.0 (no zoom). bee_marker_size (int or float, optional): Size of the bee markers. Defaults to None. figsize (tuple) : Size of the figure. Defaults to (600, 600). Returns: plotly.graph_objects.Figure: A Plotly figure containing the contour plot with bee markers and, optionally, the optimal solution marker. Raises: TypeError : If `bee_colony` is not a list ValueError : If `bee_colony` is empty TypeError : If elements of `bee_colony` are not `Bee` objects. TypeError : If `optimal_solution` is not a NumPy array (when `optimal_solution` is provided). ValueError : If `optimal_solution` is not a 2D point (when `optimal_solution` is provided). """ # Input checks if not isinstance(bee_colony, list): raise TypeError("`bee_colony` must be a list.") if not len(bee_colony): raise ValueError("Bee colony cannot be empty!") if not isinstance(bee_colony[0], Bee): raise TypeError("Elements of `bee_colony` must be Bee objects.") fig = contourplot(function,title,bounds=bounds,zoom=zoom,figsize=figsize) # Bee marker settings bee_marker_path = get_marker_path() bee_marker = Image.open(bee_marker_path) if bee_marker_size is None: x_range = np.abs(function.bounds[0,1]-function.bounds[0,0]) y_range = np.abs(function.bounds[1,1]-function.bounds[1,0]) bee_marker_size = min(x_range,y_range) * 0.05 # Add bees for bee in bee_colony: fig.add_layout_image( dict( source=bee_marker, xref="x", yref="y", xanchor="center", yanchor="middle", x=bee.position[0], y=bee.position[1], sizex= bee_marker_size, sizey=bee_marker_size, sizing="contain", opacity=1 ) ) # Add optimal solution (if specified) if optimal_solution is not None: if not isinstance(optimal_solution,np.ndarray): raise TypeError("optimal_solution must be a NumPy array.") optimal_solution = optimal_solution.reshape(1,-1) if optimal_solution.shape != (1,2): raise ValueError("optimal_solution must be a 2D point.") fig.add_trace(go.Scatter( x=[optimal_solution[0,0]], y=[optimal_solution[0,1]], mode='markers', marker=dict(size=12, color='red', symbol='x'), name='Optimal Solution') ) return fig