ronantakizawa/codereview-bench huggingface dataset:
Repository: matplotlib/matplotlib
File: lib/matplotlib/scale.pyi (Python)

Code before:
            Callable[[ArrayLike], ArrayLike], Callable[[ArrayLike], ArrayLike]
        ],
    ) -> None: ...

class LogTransform(Transform):
    input_dims: int
    output_dims: int
    base: float
    def __init__(
        self, base: float, nonpositive: Literal["clip", "mask"] = ...
    ) -> None: ...
    def inverted(self) -> InvertedLogTransform: ...

class InvertedLogTransform(Transform):
    input_dims: int
    output_dims: int
    base: float
    def __init__(self, base: float) -> None: ...
    def inverted(self) -> LogTransform: ...

class LogScale(ScaleBase):
    name: str
    subs: Iterable[int] | None
    def __init__(
        self: LogScale,
        axis: Union[Axis, None] = None,
        *,
        base: float = 10,
        subs: Union[Iterable[int], None] = None,
        nonpositive: Union[Literal['clip'], Literal['mask']] = 'clip'
    ) -> None: ...
    @property
    def base(self) -> float: ...
    def get_transform(self) -> Transform: ...

class FuncScaleLog(LogScale):
    def __init__(
        self,
        axis: Axis | None,
        functions: tuple[
            Callable[[ArrayLike], ArrayLike], Callable[[ArrayLike], ArrayLike]
        ],
        base: float = ...,
    ) -> None: ...
    @property
    def base(self) -> float: ...
    def get_transform(self) -> Transform: ...

class SymmetricalLogTransform(Transform):
    input_dims: int
    output_dims: int

Review comment (suggestion):
```suggestion
        axis: Axis | None = ...,
```

`Axis | None` is equivalent to `Union[Axis, None]`, but the preferred spelling. You don't repeat default values in stub files. Instead, you place an ellipsis.

Code after:
        self,
        axis: Axis | None,
        functions: tuple[
            Callable[[ArrayLike], ArrayLike], Callable[[ArrayLike], ArrayLike]
        ],
    ) -> None: ...

class LogTransform(Transform):
    input_dims: int
    output_dims: int
    base: float
    def __init__(
        self, base: float, nonpositive: Literal["clip", "mask"] = ...
    ) -> None: ...
    def inverted(self) -> InvertedLogTransform: ...

class InvertedLogTransform(Transform):
    input_dims: int
    output_dims: int
    base: float
    def __init__(self, base: float) -> None: ...
    def inverted(self) -> LogTransform: ...

class LogScale(ScaleBase):
    name: str
    subs: Iterable[int] | None
    def __init__(
        self,
        axis: Axis | None = ...,
        *,
        base: float = ...,
        subs: Iterable[int] | None = ...,
        nonpositive: Literal["clip", "mask"] = ...
    ) -> None: ...
    @property
    def base(self) -> float: ...
    def get_transform(self) -> Transform: ...

class FuncScaleLog(LogScale):
    def __init__(
        self,
        axis: Axis | None,
        functions: tuple[
            Callable[[ArrayLike], ArrayLike], Callable[[ArrayLike], ArrayLike]
        ],
        base: float = ...,
    ) -> None: ...
    @property
    def base(self) -> float: ...
    def get_transform(self) -> Transform: ...
Repository: matplotlib/matplotlib
File: lib/matplotlib/scale.py (Python)

Code before:
    'function': FuncScale,
    'functionlog': FuncScaleLog,
    }


def get_scale_names():
    """Return the names of the available scales."""
    return sorted(_scale_mapping)


def scale_factory(scale, axis, **kwargs):
    """
    Return a scale class by name.

    Parameters
    ----------
    scale : {%(names)s}
    axis : `~matplotlib.axis.Axis`
    """
    scale_cls = _api.check_getitem(_scale_mapping, scale=scale)
    try:
        return scale_cls(axis, **kwargs)
    except TypeError as e:
        if 'unexpected keyword argument' in str(e) or 'positional argument' in str(e):
            return scale_cls(**kwargs)
        raise


if scale_factory.__doc__:
    scale_factory.__doc__ = scale_factory.__doc__ % {
        "names": ", ".join(map(repr, get_scale_names()))}


def register_scale(scale_class):
    """
    Register a new kind of scale.

    Parameters
    ----------
    scale_class : subclass of `ScaleBase`
        The scale to register.
    """
    _scale_mapping[scale_class.name] = scale_class


def _get_scale_docs():
    """
    Helper function for generating docstrings related to scales.
    """
    docs = []
    for name, scale_class in _scale_mapping.items():

Review comment (refactor):
Are you sure this is safe? Basing logic on the exception message only can be brittle. Minimally, I'd want an explicit comment on what we catch here. But I'd rather split this out into a separte PR as we need tests.

Code after:
    'log':    LogScale,
    'symlog': SymmetricalLogScale,
    'asinh':  AsinhScale,
    'logit':  LogitScale,
    'function': FuncScale,
    'functionlog': FuncScaleLog,
    }


def get_scale_names():
    """Return the names of the available scales."""
    return sorted(_scale_mapping)


def scale_factory(scale, axis, **kwargs):
    """
    Return a scale class by name.

    Parameters
    ----------
    scale : {%(names)s}
    axis : `~matplotlib.axis.Axis`
    """
    scale_cls = _api.check_getitem(_scale_mapping, scale=scale)
    return scale_cls(axis, **kwargs)


if scale_factory.__doc__:
    scale_factory.__doc__ = scale_factory.__doc__ % {
        "names": ", ".join(map(repr, get_scale_names()))}


def register_scale(scale_class):
    """
    Register a new kind of scale.

    Parameters
    ----------
    scale_class : subclass of `ScaleBase`
        The scale to register.
    """
    _scale_mapping[scale_class.name] = scale_class


def _get_scale_docs():
    """
    Helper function for generating docstrings related to scales.
    """
    docs = []
    for name, scale_class in _scale_mapping.items():
        docstring = inspect.getdoc(scale_class.__init__) or ""
Repository: matplotlib/matplotlib
File: doc/_embedded_plots/figure_subplots_adjust.py (Python)

Code before:
import matplotlib.pyplot as plt


fig, axs = plt.subplots(2, 2, figsize=(6.5, 4))
fig.set_facecolor('lightblue')
fig.subplots_adjust(0.1, 0.1, 0.9, 0.9, 0.4, 0.4)

overlay = fig.add_axes([0, 0, 1, 1], zorder=100)
overlay.axis("off")
xycoords='figure fraction'
arrowprops=dict(arrowstyle="<->", shrinkA=0, shrinkB=0)

for ax in axs.flat:
    ax.set(xticks=[], yticks=[])

overlay.annotate("", (0, 0.75), (0.1, 0.75),
                 xycoords=xycoords, arrowprops=arrowprops)  # left
overlay.annotate("", (0.435, 0.25), (0.565, 0.25),
                 xycoords=xycoords, arrowprops=arrowprops)  # wspace
overlay.annotate("", (0, 0.8), (0.9, 0.8),
                 xycoords=xycoords, arrowprops=arrowprops)  # right
fig.text(0.05, 0.7, "left", ha="center")
fig.text(0.5, 0.3, "wspace", ha="center")
fig.text(0.05, 0.83, "right", ha="center")

overlay.annotate("", (0.75, 0), (0.75, 0.1),
                 xycoords=xycoords, arrowprops=arrowprops)  # bottom
overlay.annotate("", (0.25, 0.435), (0.25, 0.565),
                 xycoords=xycoords, arrowprops=arrowprops)  # hspace
overlay.annotate("", (0.8, 0), (0.8, 0.9),
                 xycoords=xycoords, arrowprops=arrowprops)  # top
fig.text(0.65, 0.05, "bottom", va="center")
fig.text(0.28, 0.5, "hspace", va="center")
fig.text(0.82, 0.05, "top", va="center")


Review comment (suggestion):
```suggestion
xycoords = 'figure fraction'
arrowprops = dict(arrowstyle="<->", shrinkA=0, shrinkB=0)
```

Code after:
import matplotlib.pyplot as plt


fig, axs = plt.subplots(2, 2, figsize=(6.5, 4))
fig.set_facecolor('lightblue')
fig.subplots_adjust(0.1, 0.1, 0.9, 0.9, 0.4, 0.4)

overlay = fig.add_axes([0, 0, 1, 1], zorder=100)
overlay.axis("off")
xycoords = 'figure fraction'
arrowprops = dict(arrowstyle="<->", shrinkA=0, shrinkB=0)

for ax in axs.flat:
    ax.set(xticks=[], yticks=[])

overlay.annotate("", (0, 0.75), (0.1, 0.75),
                 xycoords=xycoords, arrowprops=arrowprops)  # left
overlay.annotate("", (0.435, 0.25), (0.565, 0.25),
                 xycoords=xycoords, arrowprops=arrowprops)  # wspace
overlay.annotate("", (0, 0.8), (0.9, 0.8),
                 xycoords=xycoords, arrowprops=arrowprops)  # right
fig.text(0.05, 0.7, "left", ha="center")
fig.text(0.5, 0.3, "wspace", ha="center")
fig.text(0.05, 0.83, "right", ha="center")

overlay.annotate("", (0.75, 0), (0.75, 0.1),
                 xycoords=xycoords, arrowprops=arrowprops)  # bottom
overlay.annotate("", (0.25, 0.435), (0.25, 0.565),
                 xycoords=xycoords, arrowprops=arrowprops)  # hspace
overlay.annotate("", (0.8, 0), (0.8, 0.9),
                 xycoords=xycoords, arrowprops=arrowprops)  # top
fig.text(0.65, 0.05, "bottom", va="center")
fig.text(0.28, 0.5, "hspace", va="center")
fig.text(0.82, 0.05, "top", va="center")
Repository: matplotlib/matplotlib
File: src/ft2font_wrapper.cpp (C++)

Code before:
    ----------
    image : 2d array of uint8
        The image buffer on which to draw the glyph.
    x, y : int
        The pixel location at which to draw the glyph.
    glyph : Glyph
        The glyph to draw.
    antialiased : bool, default: True
        Whether to render glyphs 8-bit antialiased or in pure black-and-white.

    See Also
    --------
    .draw_glyphs_to_bitmap
)""";

static void
PyFT2Font_draw_glyph_to_bitmap(PyFT2Font *self, py::buffer &image,
                               double_or_<int> vxd, double_or_<int> vyd,
                               PyGlyph *glyph, bool antialiased = true)
{
    auto xd = _double_to_<int>("x", vxd);
    auto yd = _double_to_<int>("y", vyd);

    self->x->draw_glyph_to_bitmap(
      py::array_t<uint8_t, py::array::c_style>{image},
      xd, yd, glyph->glyphInd, antialiased);
}

const char *PyFT2Font_get_glyph_name__doc__ = R"""(
    Retrieve the ASCII name of a given glyph *index* in a face.

    Due to Matplotlib's internal design, for fonts that do not contain glyph names (per
    ``FT_FACE_FLAG_GLYPH_NAMES``), this returns a made-up name which does *not*
    roundtrip through `.get_name_index`.

    Parameters
    ----------
    index : int
        The glyph number to query.

    Returns
    -------
    str
        The name of the glyph, or if the font does not contain names, a name synthesized
        by Matplotlib.

    See Also
    --------
    .get_name_index
)""";


Review comment (suggestion):
```suggestion
    self->x->draw_glyph_to_bitmap(
        py::array_t<uint8_t, py::array::c_style>{image},
        xd, yd, glyph->glyphInd, antialiased);
```

Code after:
    ----------
    image : 2d array of uint8
        The image buffer on which to draw the glyph.
    x, y : int
        The pixel location at which to draw the glyph.
    glyph : Glyph
        The glyph to draw.
    antialiased : bool, default: True
        Whether to render glyphs 8-bit antialiased or in pure black-and-white.

    See Also
    --------
    .draw_glyphs_to_bitmap
)""";

static void
PyFT2Font_draw_glyph_to_bitmap(PyFT2Font *self, py::buffer &image,
                               double_or_<int> vxd, double_or_<int> vyd,
                               PyGlyph *glyph, bool antialiased = true)
{
    auto xd = _double_to_<int>("x", vxd);
    auto yd = _double_to_<int>("y", vyd);

    self->x->draw_glyph_to_bitmap(
        py::array_t<uint8_t, py::array::c_style>{image},
        xd, yd, glyph->glyphInd, antialiased);
}

const char *PyFT2Font_get_glyph_name__doc__ = R"""(
    Retrieve the ASCII name of a given glyph *index* in a face.

    Due to Matplotlib's internal design, for fonts that do not contain glyph names (per
    ``FT_FACE_FLAG_GLYPH_NAMES``), this returns a made-up name which does *not*
    roundtrip through `.get_name_index`.

    Parameters
    ----------
    index : int
        The glyph number to query.

    Returns
    -------
    str
        The name of the glyph, or if the font does not contain names, a name synthesized
        by Matplotlib.

    See Also
    --------
    .get_name_index
)""";
Repository: matplotlib/matplotlib
File: lib/matplotlib/tests/test_axes.py (Python)

Code before:
def test_pie_non_finite_values():
    fig, ax = plt.subplots()
    df = [5, float('nan'), float('inf')]

    with pytest.raises(ValueError, match='Wedge sizes must be finite numbers'):
        ax.pie(df, labels=['A', 'B', 'C'])


def test_pie_all_zeros():
    fig, ax = plt.subplots()
    with pytest.raises(ValueError, match="All wedge sizes are zero"):
        ax.pie([0, 0], labels=["A", "B"])


def test_animated_artists_not_drawn_by_default():
    fig, (ax1, ax2) = plt.subplots(ncols=2)
    canvas = FigureCanvasAgg(fig)

    imdata = np.random.random((20, 20))
    lndata = imdata[0]

    im = ax1.imshow(imdata, animated=True)
    (ln,) = ax2.plot(lndata, animated=True)

    with (mock.patch.object(im, "draw", name="im.draw") as mocked_im_draw,
          mock.patch.object(ln, "draw", name="ln.draw") as mocked_ln_draw):
        fig.draw_without_rendering()

    mocked_im_draw.assert_not_called()
    mocked_ln_draw.assert_not_called()


Review comment (suggestion):
```suggestion
    with (unittest.mock.patch.object(im, "draw", name="im.draw") as mocked_im_draw,
          unittest.mock.patch.object(ln, "draw", name="ln.draw") as mocked_ln_draw):
```

Code after:
def test_pie_non_finite_values():
    fig, ax = plt.subplots()
    df = [5, float('nan'), float('inf')]

    with pytest.raises(ValueError, match='Wedge sizes must be finite numbers'):
        ax.pie(df, labels=['A', 'B', 'C'])


def test_pie_all_zeros():
    fig, ax = plt.subplots()
    with pytest.raises(ValueError, match="All wedge sizes are zero"):
        ax.pie([0, 0], labels=["A", "B"])


def test_animated_artists_not_drawn_by_default():
    fig, (ax1, ax2) = plt.subplots(ncols=2)

    imdata = np.random.random((20, 20))
    lndata = imdata[0]

    im = ax1.imshow(imdata, animated=True)
    (ln,) = ax2.plot(lndata, animated=True)

    with (unittest.mock.patch.object(im, "draw", name="im.draw") as mocked_im_draw,
          unittest.mock.patch.object(ln, "draw", name="ln.draw") as mocked_ln_draw):
        fig.draw_without_rendering()

    mocked_im_draw.assert_not_called()
    mocked_ln_draw.assert_not_called()
Repository: matplotlib/matplotlib
File: src/ft2font.h (C/C++)

Code before:
    size_t get_last_glyph_index() const
    {
        return glyphs.size() - 1;
    }
    size_t get_num_glyphs() const
    {
        return glyphs.size();
    }
    long get_hinting_factor() const
    {
        return hinting_factor;
    }
    FT_Bool has_kerning() const
    {
        return FT_HAS_KERNING(face);
    }

  protected:
    virtual void ft_glyph_warn(FT_ULong charcode, std::set<FT_String*> family_names) = 0;
  private:
    bool warn_if_used;
    py::array_t<uint8_t, py::array::c_style> image;
    FT_Face face;
    FT_Vector pen;    /* untransformed origin  */
  public:
    std::vector<FT_Glyph> glyphs;
  private:
    std::vector<FT2Font *> fallbacks;
    std::unordered_map<long, FT2Font *> char_to_font;
    FT_BBox bbox;
    FT_Pos advance;
    long hinting_factor;
    int kerning_factor;

    // prevent copying
    FT2Font(const FT2Font &);
    FT2Font &operator=(const FT2Font &);
};

#endif


Review comment (suggestion):
I think to be consistent with `face`, the implementation here should be with a public `get_glyphs` method.

Code after:
    FT_Glyph const &get_last_glyph() const
    {
        return glyphs.back();
    }
    size_t get_last_glyph_index() const
    {
        return glyphs.size() - 1;
    }
    size_t get_num_glyphs() const
    {
        return glyphs.size();
    }
    long get_hinting_factor() const
    {
        return hinting_factor;
    }
    FT_Bool has_kerning() const
    {
        return FT_HAS_KERNING(face);
    }

  protected:
    virtual void ft_glyph_warn(FT_ULong charcode, std::set<FT_String*> family_names) = 0;
  private:
    bool warn_if_used;
    py::array_t<uint8_t, py::array::c_style> image;
    FT_Face face;
    FT_Vector pen;    /* untransformed origin  */
    std::vector<FT_Glyph> glyphs;
    std::vector<FT2Font *> fallbacks;
    std::unordered_map<long, FT2Font *> char_to_font;
    FT_BBox bbox;
    FT_Pos advance;
    long hinting_factor;
    int kerning_factor;

    // prevent copying
    FT2Font(const FT2Font &);
    FT2Font &operator=(const FT2Font &);
};

#endif
Repository: matplotlib/matplotlib
File: galleries/examples/color/colorbar_histogram.py (Python)

Code before:
"""
=========================
Colorbar with Histogram
=========================

This example demonstrates how to create a colorbar for an image and
add a histogram of the data values alongside it. This is useful for
visualizing the distribution of values mapped to colors.

"""

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
from mpl_toolkits.axes_grid1 import make_axes_locatable

# Generate random data
x = np.random.random((10, 10))

# Compute histogram
counts, bins = np.histogram(x)

# Set up colormap and normalization
cmap = plt.get_cmap('viridis')
norm = mcolors.BoundaryNorm(bins, cmap.N)

fig, ax = plt.subplots()
im = ax.imshow(x, cmap=cmap, norm=norm)

Review comment (suggestion):
```suggestion
Histogram as Colorbar
```
This example is about using a histogram in place of a regular colorbar

Code after:
"""
=====================
Histogram as colorbar
=====================

This example demonstrates how to use a colored histogram instead of a colorbar
to not only show the color-to-value mapping, but also visualize the
distribution of values.
"""

import matplotlib.pyplot as plt
import numpy as np

import matplotlib.colors as mcolors

# surface data
delta = 0.025
x = y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-(((X + 1) * 1.3) ** 2) - ((Y + 1) * 1.3) ** 2)
Z2 = 2.5 * np.exp(-((X - 1) ** 2) - (Y - 1) ** 2)
Z = Z1**0.25 - Z2**0.5

# colormap & normalization
bins = 30
cmap = plt.get_cmap("RdYlBu_r")
bin_edges = np.linspace(Z.min(), Z.max(), bins + 1)
norm = mcolors.BoundaryNorm(bin_edges, cmap.N)
Repository: matplotlib/matplotlib
File: galleries/examples/color/colorbar_histogram.py (Python)

Code before:

"""

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
from mpl_toolkits.axes_grid1 import make_axes_locatable

# Generate random data
x = np.random.random((10, 10))

# Compute histogram
counts, bins = np.histogram(x)

# Set up colormap and normalization
cmap = plt.get_cmap('viridis')
norm = mcolors.BoundaryNorm(bins, cmap.N)

fig, ax = plt.subplots()
im = ax.imshow(x, cmap=cmap, norm=norm)
cbar = plt.colorbar(im, ax=ax)
cbar.set_label('Value')

# Create an axes on the right side of ax. The width of cax will be 20% of ax and the padding between cax and ax will be fixed at 0.05 inch.
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="20%", pad=0.05)

# Plot histogram
midpoints = bins[:-1] + np.diff(bins) / 2
cax.barh(midpoints, counts, height=np.diff(bins), color=cmap(norm(midpoints)))
cax.set_yticks(bins)
cax.set_xlabel('Count')
cax.set_ylabel('Value')
cax.invert_yaxis()  # Optional: to match the orientation of imshow

plt.tight_layout()
plt.show()



Review comment (suggestion):
As I said in the original issue, I want this example to use axes_inset for the colorbar b/c I think that is more reflective of the usual use case for these types of plots.

Code after:
"""

import matplotlib.pyplot as plt
import numpy as np

import matplotlib.colors as mcolors

# surface data
delta = 0.025
x = y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-(((X + 1) * 1.3) ** 2) - ((Y + 1) * 1.3) ** 2)
Z2 = 2.5 * np.exp(-((X - 1) ** 2) - (Y - 1) ** 2)
Z = Z1**0.25 - Z2**0.5

# colormap & normalization
bins = 30
cmap = plt.get_cmap("RdYlBu_r")
bin_edges = np.linspace(Z.min(), Z.max(), bins + 1)
norm = mcolors.BoundaryNorm(bin_edges, cmap.N)

# main plot
fig, ax = plt.subplots(layout="constrained")
im = ax.imshow(Z, cmap=cmap, origin="lower", extent=[-3, 3, -3, 3], norm=norm)

# inset histogram
cax = ax.inset_axes([1.18, 0.02, 0.25, 0.95])  # left, bottom, width, height

# plot histogram
counts, _ = np.histogram(Z, bins=bin_edges)
midpoints = (bin_edges[:-1] + bin_edges[1:]) / 2
distance = midpoints[1] - midpoints[0]
cax.barh(midpoints, counts, height=0.8 * distance, color=cmap(norm(midpoints)))

# styling
cax.spines[:].set_visible(False)
cax.set_yticks(bin_edges)
cax.tick_params(axis="both", which="both", length=0)

plt.show()
Repository: matplotlib/matplotlib
File: galleries/examples/color/colorbar_histogram.py (Python)

Code before:
"""
=========================
Histogram as Colorbar
=========================

This example demonstrates how to create a colorbar for an image and
add a histogram of the data values alongside it. This is useful for
visualizing the distribution of values mapped to colors.

"""
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
from mpl_toolkits.axes_grid1.inset_locator import inset_axes

# === Surface Data ===
delta = 0.025
x = y = np.arange(-3.0, 3.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-X**2 - Y**2)
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
Z = (Z1 - Z2) * 2

# Purposeful bins for full value range
bins = np.linspace(-1, 1, 11)
counts, _ = np.histogram(Z, bins=bins)

# Normalize to bins for colormap
cmap = plt.get_cmap('RdYlBu')
norm = mcolors.BoundaryNorm(bins, cmap.N)

# === Plot heatmap ===
fig, ax = plt.subplots()
im = ax.imshow(Z, interpolation='bilinear', cmap=cmap,
               origin='lower', extent=[-3, 3, -3, 3],
               norm=norm)

# === Inset histogram as colorbar ===
cax = inset_axes(ax,

Review comment (suggestion):
You should just use the ax.inset_axes method directly, please see the original issue for an example of doing so

Code after:
"""
=====================
Histogram as colorbar
=====================

This example demonstrates how to use a colored histogram instead of a colorbar
to not only show the color-to-value mapping, but also visualize the
distribution of values.
"""

import matplotlib.pyplot as plt
import numpy as np

import matplotlib.colors as mcolors

# surface data
delta = 0.025
x = y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-(((X + 1) * 1.3) ** 2) - ((Y + 1) * 1.3) ** 2)
Z2 = 2.5 * np.exp(-((X - 1) ** 2) - (Y - 1) ** 2)
Z = Z1**0.25 - Z2**0.5

# colormap & normalization
bins = 30
cmap = plt.get_cmap("RdYlBu_r")
bin_edges = np.linspace(Z.min(), Z.max(), bins + 1)
norm = mcolors.BoundaryNorm(bin_edges, cmap.N)

# main plot
fig, ax = plt.subplots(layout="constrained")
im = ax.imshow(Z, cmap=cmap, origin="lower", extent=[-3, 3, -3, 3], norm=norm)

# inset histogram
cax = ax.inset_axes([1.18, 0.02, 0.25, 0.95])  # left, bottom, width, height

# plot histogram
counts, _ = np.histogram(Z, bins=bin_edges)
midpoints = (bin_edges[:-1] + bin_edges[1:]) / 2