Source code for pybop.plot.problem

import numpy as np

from pybop.costs.design_cost import DesignCost
from pybop.costs.error_measures import ErrorMeasure
from pybop.parameters.parameter import Inputs
from pybop.plot.util import get_backend_from_figure, remove_brackets
from pybop.problems.meta_problem import MetaProblem
from pybop.problems.problem import Problem
from pybop.simulators.solution import Solution


[docs] def problem( problem: Problem, inputs: Inputs = None, title="Scatter Plot", show: bool = True, backend: str = None, figures=None, axes=None, ): """ Produce a quick plot of the target dataset against optimised model output. Generates an interactive plot comparing the simulated model output with an optional target dataset and visualises uncertainty. Parameters ---------- problem : pybop.Problem Problem object with dataset and targets attributes. inputs : Inputs Optimised (or example) parameter values. title: str, optional: The title of the plot (default: "Scatter Plot") show : bool, optional If True, the figure is shown upon creation (default: True). backend: str or pybop.plot.backends.PlotBackend, optional The plotting backend to be used. figures: figure object, optional Figure for plotting. If not provided a new figure is created for each problem. Can be a single figure or one figure per target. axes: axis, optional The axes to be used for plotting. One axis per target is expected. plotly: axes expected to be of the form list of tuple(row, col) Returns ------- None: if show is True Figure or list of figures: if show is False A single figure or a list of figures containing the plots for each target in the problem. If show is True, the figures will be displayed and None will be returned. """ if inputs is None: inputs = problem.parameters.to_dict() elif not isinstance(inputs, dict): raise TypeError(f"Expecting a dictionary, received {type(inputs)}") domain = problem.domain if problem.domain_data is None: # Simulate the model for both the initial and the given inputs target = problem.target problem.set_target(target + [domain]) initial_inputs = problem.parameters.to_dict("initial") target_output = problem.simulate(initial_inputs) target_domain = target_output[domain].data model_output = problem.simulate(inputs) model_domain = model_output[domain].data problem.set_target(target) else: # Extract the time data and simulate the model for the given inputs target_output = Solution() for target in problem.target: target_output.set_solution_variable( target, data=problem.target_data[target] ) target_domain = problem.domain_data model_output = problem.simulate(inputs) model_domain = target_domain[: len(model_output[target].data)] # Create a plot for each output # Import plotting backend backend = get_backend_from_figure(backend, figures) # Process input figures, axes, create_figure, _ = backend.parse_input_axes( figures, axes, num_plots=len(problem.target), allow_single_axis=False ) for i, var in enumerate(problem.target): ax = axes[i % len(axes)] if create_figure: fig = backend.create_figure( style={"bg_color": "white", "width": 600, "height": 600}, ) figures = np.append(figures, fig) else: fig = figures[i % len(figures)] backend.update_axes_titles( fig, ax, remove_brackets(domain), remove_brackets(var) ) backend.update_plot_titles(fig, ax, title) traces = [] model_trace = backend.line( x=model_domain, y=model_output[var].data, label="Optimised" if isinstance(problem.cost, DesignCost) else "Model", style={ "linestyle": "none" if isinstance(problem, MetaProblem) else "solid", "marker": "." if isinstance(problem, MetaProblem) else "none", }, ) traces.append(model_trace) target_trace = backend.line( x=target_domain, y=target_output[var].data, label="Reference", style={"linestyle": "none", "marker": "."}, ) traces.append(target_trace) if isinstance(problem.cost, ErrorMeasure) and len( model_output[var].data ) == len(target_output[var].data): # Compute the standard deviation as proxy for uncertainty sigma = np.std(model_output[var].data - target_output[var].data) # Convert x and upper and lower limits into lists to create a filled trace x = target_domain.tolist() y_upper = (model_output[var].data + sigma).tolist() y_lower = (model_output[var].data - sigma).tolist() fill_trace = backend.fill_between(x, y_upper, y_lower, color="#FFE5CC") traces.append(fill_trace) # Reverse the order of the traces to put the model on top traces = traces[::-1] for trace in traces: backend.plot_trace(trace, fig, ax=ax) backend.legend(fig, axes=ax) # Generate the figure and update the layout if show: backend.show_figure(fig) if not show: return figures[0] if len(figures) == 1 else figures