1. User API

This section describes the main API users are expected to interact with.

1.1. Functions

ipysensitivityprofiler.profiler(models: list[Callable], xmin: list[float] | ndarray, xmax: list[float] | ndarray, ymin: list[float] | ndarray, ymax: list[float] | ndarray, x0: list[float] | ndarray | None = None, resolution: int = 25, width: int = 300, height: int | None = None, xlabels: list[str] | None = None, ylabels: list[str] | None = None, colors: list[str] | None = None, line_styles: list[str] | None = None, model_labels: list[str] | None = None, show_legend: bool | None = None) Profiler[source]

Create sensitivity profilers for given models.

Example:
import ipysensitivityprofiler as isp

def f1(x):
    return -0.1 * x[:, 0] ** 3 - 0.5 * x[:, 1] ** 2

def f2(x):
    return -0.2 * x[:, 0] ** 3 - 0.25 * x[:, 1] ** 2

isp.profiler(
    models=[f1, f2],
    xmin=[-5, -5],
    xmax=[5, 5],
    ymin=[-10],
    ymax=[10],
    x0=[1, 1],
    resolution=100,
    xlabels=["x1", "x2"],
    ylabels=["y"],
)
Args:
models: List[callable]

List of callable functions with the same signature y = f(x). There will be one profile per model. x must be a numpy array of shape (-1, nx) and y an array of shape (-1, ny).

xmin: Union[List[float], np.ndarray]

Lower bounds of inputs.

xmax: Union[List[float], np.ndarray]

Upper bounds of inputs.

ymin: Union[List[float], np.ndarray]

Lower bounds of outputs.

ymax: Union[List[float], np.ndarray]

Upper bounds of outputs.

x0: Union[List[float], np.ndarray] Defaults to use for initial x0 (red dot in plots). Default is None (which turns into mean of range).

resolution: int, optional

Line resolution. Default is 25 points.

width: int, optional

Width of each plot. Default is 300 pixels.

height: int, optional

Height of each plot. Default is None (match width).

xlabels: List[str]

Labels to use for inputs. Default is None (which becomes x1, x2, …)

ylabels: Union[List[float], np.ndarray]

Labels to use for outputs. Default is None (which becomes y1, y2, …)

colors: List[str], optional

Line color per model, by position, as any CSS color. Cycled if shorter than models. Default is None, which uses a colorblind-safe categorical palette. Pass ipysensitivityprofiler._utils.DARK_COLORS under a dark theme.

line_styles: List[str], optional

Line style per model, by position: one of “solid”, “dashed”, “dotted”, “dash_dotted”. Cycled if shorter than models. Default is None, which cycles all four so models stay distinguishable without relying on color alone.

model_labels: List[str], optional

Legend label per model. Default is None, which uses each model’s function name.

show_legend: bool, optional

Show a legend keying models to their color and stroke. Default is None, which shows one when there is more than one model.

Returns:

Profiler: Jupyter Widget.

Note:

The models are evaluated exactly once here, after the widget has been fully built. A slow model therefore shows an empty set of axes while it runs rather than blocking the widget from rendering at all.

1.2. Classes

class ipysensitivityprofiler.Profiler(*args: t.Any, **kwargs: t.Any)[source]

Bases: VBox

Profiler Widget.

Attributes:
view: View

Profiler widget controlled by controller.

controller: Controller

Widget to control profilers

__init__(view: View, controller: Controller, **kwargs: Any) None[source]

Public constructor

class ipysensitivityprofiler.Controller(*args: t.Any, **kwargs: t.Any)[source]

Bases: VBox

Control panel for profiler.

Attributes:
range_sliders: Dict[str, List[W.FloatRangeSlider]]:

Range sliders to control axis limits of inputs and outputs:

range_sliders = {
    "x": [...],  # list of range sliders associated with inputs
    "y": [...],  # list of range sliders associated with outputs
}
sliders: List[W.FloatSlider]

Sliders to control input values (and automatically update view).

__init__(view: View, **kwargs: Any)[source]

Public constructor.

view: View

Profiler widget to be controlled by controller.

class ipysensitivityprofiler.View(*args: t.Any, **kwargs: t.Any)[source]

Bases: Box

Widget to display grid of sensivity profiles.

Construction builds the whole figure grid but does not evaluate the models: the curves start flat and the first evaluation happens on refresh(). See refresh().

predict: Callable

Callable function with signature y = f(x) where x must be a numpy array of shape (-1, n_x) and y an array of shape (-1, n_y).

xmin: ndarray

An array of shape (n_x,) representing the lower bound on the inputs

xmax: ndarray

An array of shape (n_x,) representing the upper bound on the inputs

ymin: ndarray

An array of shape (n_y,) representing the lower bound on the outputs

ymax: ndarray

An array of shape (n_y,) representing the upper bound on the outputs

width: int

The width of each figure in pixels. Default is 300.

height: int

The height of each figure in pixels. Default is 300.

resolution: int

The number of point in each curve. Default is 25.

x0: ndarray

The point about which to compute the sensitivities

y0: ndarray

The value of the outputs evaluates at x0

xlabels: list[str]

The name of each input

ylabels: list[str]

The name of each output

n_models: int

Number of models being profiled, i.e. the number of curves per figure.

data: Data

object in charge of updating data

grid: GridspecLayout

Grid containing all figures

colors: list[str]

Line color per model, by position. Cycled if shorter than the number of models. Defaults to a colorblind-safe categorical palette.

line_styles: list[str]

Line style per model, by position. Cycled if shorter than the number of models. One of solid, dashed, dotted, dash_dotted.

model_labels: list[str]

Legend label per model

show_legend: bool

Show a legend keying models to their style

__init__(**kwargs: Any)[source]

Public constructor

refresh() None[source]

Evaluate the models and redraw.

Construction draws flat placeholder curves rather than evaluating, so that every widget exists before a slow model is allowed to block (#6). ipysensitivityprofiler.profiler() calls this once the whole widget tree has been assembled; call it yourself when building a View directly.

save_png(xlabel: str, ylabel: str, filename: str | None = None) None[source]

Save figure as PNG.

Args:
xlabel: str

The label of the input shown in the figure.

ylabel: str

The label of the output shown in the figure.

filename: str, optional

File name to save to. Default is “profiler_{xlabel}_vs_{ylabel}.png”

2. Core API

This section describes backend support functions and classes. It is intended for developers.

class ipysensitivityprofiler._data.Data(*args: t.Any, **kwargs: t.Any)[source]

Bases: HasTraits

Automatically evaluate outputs whenever inputs change.

This class is in charge of managing source data and calling user prediction models as needed.

Construction deliberately does not evaluate: y starts as a placeholder of the right shape and the models run only once refresh() is called. See refresh().

xlabels: list[str]

An instance of a Python list.

ylabels: list[str]

An instance of a Python list.

n_models: int

Number of models, i.e. the number of curves in each figure.

predict: Callable

Callback that calls user provided models to update data as needed.

x: NDArray

A numpy array trait type.

y: NDArray

A numpy array trait type.

__init__(**kwargs: Any) None[source]
refresh() None[source]

Evaluate the models over the current grid.

property n_x: int

Number of inputs.

property n_y: int

Number of outputs.

property N: int

Number of models (i.e. number of lines on plot).

ipysensitivityprofiler._utils.grid_size(n_x: int, resolution: int) int[source]

Number of rows in a grid built by create_grid().

Callers that must pre-size buffers for a fixed number of evaluation points (such as an OpenMDAO problem’s num_nodes) should size them with this.

ipysensitivityprofiler._utils.create_grid(x0: NDArray, xmin: NDArray, xmax: NDArray, resolution: int = 10) NDArray[source]

Generate grid data for sensitivity profilers.

Args:
x0: NDArray

Local point about which to plot sensivities. Array of shape (n,) where n is the number of input variables.

xmin: NDArray

Min bound for plotting sensitivities. Array of shape (n,)

xmax: NDArray

Max bound for plotting sensitivities. Array of shape (n,)

resolution: int, optional

Number of points between xmin and xmax. Default is 10.

Returns:
NDArray

Array of shape (resolution * n + 1, n). The first resolution * n rows sweep one input at a time; the final row is x0 itself.

Example:
x = create_grid(
    x0=[ 0, 1, 2],
    xmin=[-5, -5, -5],
    xmax=[ 5, 5, 5],
    resolution=10,
)

>> x = [[-5,  1,  2],
        [-3,  1,  2],
        [-2,  1,  2],
        [-1,  1,  2],
        [ 0,  1,  2],
        [ 0,  1,  2],
        [ 1,  1,  2],
        [ 2,  1,  2],
        [ 3,  1,  2],
        [ 5,  1,  2],
        [ 0, -5,  2],
        [ 0, -3,  2],
        [ 0, -2,  2],
        [ 0, -1,  2],
        [ 0,  0,  2],
        [ 0,  0,  2],
        [ 0,  1,  2],
        [ 0,  2,  2],
        [ 0,  3,  2],
        [ 0,  5,  2],
        [ 0,  1, -5],
        [ 0,  1, -3],
        [ 0,  1, -2],
        [ 0,  1, -1],
        [ 0,  1,  0],
        [ 0,  1,  0],
        [ 0,  1,  1],
        [ 0,  1,  2],
        [ 0,  1,  3],
        [ 0,  1,  5],
        [ 0,  1,  2]]  # <- x0
ipysensitivityprofiler._utils.batch_slice(index: int, resolution: int) slice[source]

Return the rows of a grid that sweep input index.

Grids from create_grid() lay out one contiguous sweep per input, so the rows for a given input are a plain slice. Using a slice rather than a list of indices keeps the lookup a view instead of a copy, which matters because this sits in the redraw path.

Args:
index: int

Index of the input variable.

resolution: int

Number of points per sweep.

Returns:
slice

Row slice corresponding to one grid permutation.

ipysensitivityprofiler._utils.make_figure(N: int, num_x_ticks: int = 3, num_y_ticks: int = 3, tick_style: dict | None = None, xmin: float | None = None, xmax: float | None = None, ymin: float | None = None, ymax: float | None = None, xs: LinearScale | None = None, ys: LinearScale | None = None, colors: list[str] | None = None, line_styles: list[str] | None = None, labels: list[str] | None = None, show_legend: bool = False) Figure[source]

Create initial figure for profiler trait (data will be replaced).

Pass xs / ys to share a scale with the other figures in the same column / row; omit them and the figure owns a private scale built from the bounds.

colors and line_styles are assigned to models by position and cycled if shorter than N, so each model is distinguishable by hue and by stroke.

ipysensitivityprofiler._utils.make_grid(n_x: int, n_y: int, N: int, width: int | None = None, height: int | None = None, xscales: list[LinearScale] | None = None, yscales: list[LinearScale] | None = None, colors: list[str] | None = None, line_styles: list[str] | None = None, labels: list[str] | None = None, show_legend: bool = False) GridspecLayout[source]

Create grid layout of specified width and height.

Every figure in column j shows the same input and every figure in row i the same output, so xscales / yscales let one scale per column / row be shared across the whole grid rather than each cell owning a private pair.