Skip to content

[Bug]: Matplotlib 3.5 breaks unyt integration of error bars #21669

Closed
@neutrinoceros

Description

@neutrinoceros

Bug summary

Discovered this in unyt's daily CI jobs: https://github.com/yt-project/unyt/actions

Code for reproduction

import matplotlib.pyplot as plt
import unyt as un

un.matplotlib_support.enable()
fig, ax = plt.subplots()

x = un.unyt_array([1, 2], "cm")
y = un.unyt_array([3, 4], "kg")
yerr = un.unyt_array([0.1, 0.2], "kg")
ax.errorbar(x, y, yerr=(yerr, yerr))

Actual outcome

UnitOperationError: The <ufunc 'subtract'> operator for unyt_arrays with units "kg" 
(dimensions "(mass)") and "dimensionless" (dimensions "1") is not well defined.
detailed raceback
╭──────────────────────────── Traceback (most recent call last) ────────────────────────────╮
│                                                                                           │
│ /private/tmp/matplotlib/t.py:10 in <module>                                               │
│                                                                                           │
│    7 x = un.unyt_array([1, 2], "cm")                                                      │
│    8 y = un.unyt_array([3, 4], "kg")                                                      │
│    9 yerr = un.unyt_array([0.1, 0.2], "kg")                                               │
│ ❱ 10 ax.errorbar(x, y, yerr=yerr)                                                         │
│   11                                                                                      │
│ /private/tmp/matplotlib/lib/matplotlib/__init__.py:1348 in inner                          │
│                                                                                           │
│   1345 │   @functools.wraps(func)                                                         │
│   1346 │   def inner(ax, *args, data=None, **kwargs):                                     │
│   1347 │   │   if data is None:                                                           │
│ ❱ 1348 │   │   │   return func(ax, *map(sanitize_sequence, args), **kwargs)               │
│   1349 │   │                                                                              │
│   1350 │   │   bound = new_sig.bind(ax, *args, **kwargs)                                  │
│   1351 │   │   auto_label = (bound.arguments.get(label_namer)                             │
│                                                                                           │
│ /private/tmp/matplotlib/lib/matplotlib/axes/_axes.py:3474 in errorbar                     │
│                                                                                           │
│   3471 │   │   │   │   │   │   xo, yo, marker='|', **eb_cap_style))                       │
│   3472 │   │                                                                              │
│   3473 │   │   if yerr is not None:                                                       │
│ ❱ 3474 │   │   │   lower, upper = extract_err('y', yerr, y, lolims, uplims)               │
│   3475 │   │   │   barcols.append(self.vlines(                                            │
│   3476 │   │   │   │   *apply_mask([x, lower, upper], everymask), **eb_lines_style))      │
│   3477 │   │   │   # select points without upper/lower limits in y and                    │
│                                                                                           │
│ /private/tmp/matplotlib/lib/matplotlib/axes/_axes.py:3434 in extract_err                  │
│                                                                                           │
│   3431 │   │   │   │   │   f"'{name}err' (shape: {np.shape(err)}) must be a scalar "      │
│   3432 │   │   │   │   │   f"or a 1D or (2, n) array-like whose shape matches "           │
│   3433 │   │   │   │   │   f"'{name}' (shape: {np.shape(data)})") from None               │
│ ❱ 3434 │   │   │   return data - low * ~lolims, data + high * ~uplims  # low, high        │
│   3435 │   │                                                                              │
│   3436 │   │   if xerr is not None:                                                       │
│   3437 │   │   │   left, right = extract_err('x', xerr, x, xlolims, xuplims)              │
│                                                                                           │
│ /Users/robcleme/.pyenv/versions/3.9.7/envs/tmp_unit/lib/python3.9/site-packages/unyt/arra │
│ y.py:1764 in __array_ufunc__                                                              │
│                                                                                           │
│   1761 │   │   │   │   │   │   │   │   else:                                              │
│   1762 │   │   │   │   │   │   │   │   │   raise UnitOperationError(ufunc, u0, u1)        │
│   1763 │   │   │   │   │   │   else:                                                      │
│ ❱ 1764 │   │   │   │   │   │   │   raise UnitOperationError(ufunc, u0, u1)                │
│   1765 │   │   │   │   │   conv, offset = u1.get_conversion_factor(u0, inp1.dtype)        │
│   1766 │   │   │   │   │   new_dtype = np.dtype("f" + str(inp1.dtype.itemsize))           │
│   1767 │   │   │   │   │   conv = new_dtype.type(conv)                                    │
╰───────────────────────────────────────────────────────────────────────────────────────────╯
UnitOperationError: The <ufunc 'subtract'> operator for unyt_arrays with units "kg" 
(dimensions "(mass)") and "dimensionless" (dimensions "1") is not well defined.

Expected outcome

This is the output figure with matplotlib 3.4.3
unyt_err

Additional information

The regression bisects to #19526 (8cd22b4)

I note that if replace the last line with

ax.errorbar(x, y, yerr=yerr)

(passing a single array instead of a tuple), it doesn't break on the main branch.

Operating system

Unbuntu

Matplotlib Version

3.5.0

Matplotlib Backend

not relevant (I get an identical error with the 'agg' backend)

Python version

tested on 3.7 to 3.9

Jupyter version

No response

Installation

pip

Metadata

Metadata

Assignees

No one assigned

    Labels

    Release criticalFor bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions.topic: units and array ducktypes

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions