From 97e1750902d8ff9b28ebb55fb7a22641f2ae1237 Mon Sep 17 00:00:00 2001 From: T Ehrhardt Date: Thu, 18 Jun 2026 15:48:23 +0200 Subject: [PATCH] make map plotting compatible with matplotlib 3.11 and add rudimentary unit test --- pisa/core/map.py | 50 +++++++++++++++++++++++++++++++----------------- setup.py | 2 +- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/pisa/core/map.py b/pisa/core/map.py index 74b0aacba..7e4197b80 100755 --- a/pisa/core/map.py +++ b/pisa/core/map.py @@ -48,7 +48,7 @@ __author__ = 'J.L. Lanfranchi' -__license__ = '''Copyright (c) 2014-2020, The IceCube Collaboration +__license__ = '''Copyright (c) 2014-2026, The IceCube Collaboration Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -784,15 +784,17 @@ def plot(self, symm=False, logz=False, vmin=None, vmax=None, backend=None, # Set cmap. if cmap is None: if symm: - cmap = mpl.cm.get_cmap("RdBu_r").copy() - cmap.set_bad(color=(0.5, 0.9, 0.5), alpha=1) + cmap = mpl.colormaps["RdBu_r"].with_extremes( + bad=((0.5, 0.9, 0.5), 1) + ) else: - cmap = mpl.cm.get_cmap("Spectral_r").copy() - cmap.set_bad(color=(0.0, 0.2, 0.0), alpha=1) + cmap = mpl.colormaps["Spectral_r"].with_extremes( + bad=((0.0, 0.2, 0.0), 1) + ) if isinstance(cmap, str): - cmap = mpl.cm.get_cmap(cmap) + cmap = mpl.colormaps[cmap] if bad_color is not None: - cmap.set_bad(bad_color) + cmap = cmap.with_extremes(bad=bad_color) # Set cmap range. if symm: # symm = True. @@ -3041,6 +3043,7 @@ def allclose(self, other): def test_Map(): """Unit tests for Map class""" import pickle + import matplotlib as mpl n_ebins = 10 n_czbins = 5 n_azbins = 2 @@ -3056,6 +3059,10 @@ def test_Map(): m1 = Map(name='x', hist=unp.uarray(np.ones(shape), np.sqrt(np.ones(shape))), binning=(e_binning, cz_binning)) + + # make a copy of this one for use with plotting further down + map_2d_for_plotting = deepcopy(m1) + # or call init poisson error afterwards m1 = Map(name='x', hist=np.ones((n_ebins, n_czbins)), hash=23, binning=(e_binning, cz_binning)) @@ -3128,6 +3135,9 @@ def test_Map(): m3 = Map(name='z', hist=4*np.ones((n_ebins, n_czbins, n_azbins)), binning=(e_binning, cz_binning, az_binning)) + # make a copy of this one for use with plotting further down + map_3d_for_plotting = deepcopy(m3) + assert m3[0, 0, 0] == 4, 'm3[0, 0, 0] = %s' % m3[0, 0, 0] testdir = tempfile.mkdtemp() try: @@ -3226,17 +3236,21 @@ def test_Map(): deepcopy(m_orig) - #FIXME: Add unit test for plot function: - # - test 3D *and* 2D case - # - test saving option works - # - test return types - - # start implementing some - # test_return = xx.plot() - # assert test_return[0] == matplotlib.figure.Figure - # assert test_return[1] == matplotlib.axes._subplots.AxesSubplot - # assert test_return[2] == matplotlib.collections.QuadMesh - # assert test_return[3] == matplotlib.colorbar.Colorbar + #FIXME: extend unit test for plot function + for m, fname in zip( + (map_2d_for_plotting, map_3d_for_plotting), ("test_map_2d", "test_map_3d") + ): + testdir = tempfile.mkdtemp() + try: + # make plot and store as png and pfd + test_return = m.plot(fname=fname, fmt=("png", "pdf"), outdir=testdir) + finally: + shutil.rmtree(testdir, ignore_errors=True) + assert isinstance(test_return[0], mpl.figure.Figure) + assert isinstance(test_return[1], mpl.axes._axes.Axes) + assert isinstance(test_return[2], mpl.collections.QuadMesh) + # TODO: 3d case returns colormap=None + assert test_return[3] is None or isinstance(test_return[3], mpl.colorbar.Colorbar) logging.info(str(('<< PASS : test_Map >>'))) diff --git a/setup.py b/setup.py index 2c9887821..26a2aff1a 100755 --- a/setup.py +++ b/setup.py @@ -91,7 +91,7 @@ 'h5py', 'iminuit>=2', 'line_profiler', - 'matplotlib>=3.0', # 1.5: inferno colormap; 2.0: 'C0' colorspec + 'matplotlib>=3.5', # 3.5.0: matplotlib.colormaps[name] 'numba>=0.53', # >=0.35: fastmath jit flag; >=0.38: issue #439; 0.44 segfaults 'numpy>=1.17', 'pint>=0.20', # property pint.quantity._Quantity no longer exists in 0.20 (TODO: test whether versions >=0.20 actually required)