From 750dcef067d89a2fd5c95a461d34ceb1895c4664 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 13 Jul 2023 14:52:47 +0200 Subject: [PATCH 1/9] optimizations --- lib/matplotlib/cbook.py | 7 ++++++- lib/matplotlib/transforms.py | 7 ++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index 87656b5c3c00..938b57e4053d 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -1620,6 +1620,7 @@ def safe_first_element(obj): """ return _safe_first_finite(obj, skip_nonfinite=False) +_NoValue = object() def _safe_first_finite(obj, *, skip_nonfinite=True): """ @@ -1661,7 +1662,11 @@ def safe_isfinite(val): raise RuntimeError("matplotlib does not " "support generators as input") else: - return next((val for val in obj if safe_isfinite(val)), safe_first_element(obj)) + value = next((val for val in obj if safe_isfinite(val)), _NoValue) + if value is _NoValue: + return safe_first_element(obj) + else: + return value def sanitize_sequence(data): diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py index f2ffa09932de..1a9f7e1ad1c3 100644 --- a/lib/matplotlib/transforms.py +++ b/lib/matplotlib/transforms.py @@ -189,13 +189,14 @@ def set_children(self, *children): # Parents are stored as weak references, so that if the # parents are destroyed, references from the children won't # keep them alive. + id_self = id(self) for child in children: # Use weak references so this dictionary won't keep obsolete nodes # alive; the callback deletes the dictionary entry. This is a # performance improvement over using WeakValueDictionary. ref = weakref.ref( - self, lambda _, pop=child._parents.pop, k=id(self): pop(k)) - child._parents[id(self)] = ref + self, lambda _, pop=child._parents.pop, k=id_self: pop(k)) + child._parents[id_self] = ref def frozen(self): """ @@ -1773,7 +1774,7 @@ def __array__(self, *args, **kwargs): def __eq__(self, other): if getattr(other, "is_affine", False) and hasattr(other, "get_matrix"): - return np.all(self.get_matrix() == other.get_matrix()) + return (self.get_matrix() == other.get_matrix()).all() return NotImplemented def transform(self, values): From b33ba058a81874c6f6ee1fd541012719afa2982f Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 13 Jul 2023 15:12:41 +0200 Subject: [PATCH 2/9] compile nth color re; fast path for int in isfinite --- lib/matplotlib/cbook.py | 2 ++ lib/matplotlib/colors.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index 938b57e4053d..766db3a385dc 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -1635,6 +1635,8 @@ def _safe_first_finite(obj, *, skip_nonfinite=True): def safe_isfinite(val): if val is None: return False + if isinstance(val, int): + return True try: return np.isfinite(val) if np.isscalar(val) else True except TypeError: diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index 434bb5423543..2241a5eed311 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -210,10 +210,11 @@ def _sanitize_extrema(ex): ret = float(ex) return ret +_nth_color_re = re.compile(r"\AC[0-9]+\Z") def _is_nth_color(c): """Return whether *c* can be interpreted as an item in the color cycle.""" - return isinstance(c, str) and re.match(r"\AC[0-9]+\Z", c) + return isinstance(c, str) and _nth_color_re.match(c) def is_color_like(c): From d35fa2eb8b78ca5b285480369b095f396bd6396b Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 13 Jul 2023 19:12:37 +0200 Subject: [PATCH 3/9] eliminate generator --- lib/matplotlib/cbook.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index 766db3a385dc..18c08b197f76 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -1620,7 +1620,6 @@ def safe_first_element(obj): """ return _safe_first_finite(obj, skip_nonfinite=False) -_NoValue = object() def _safe_first_finite(obj, *, skip_nonfinite=True): """ @@ -1664,11 +1663,10 @@ def safe_isfinite(val): raise RuntimeError("matplotlib does not " "support generators as input") else: - value = next((val for val in obj if safe_isfinite(val)), _NoValue) - if value is _NoValue: - return safe_first_element(obj) - else: - return value + for val in obj: + if safe_isfinite(val): + return val + return safe_first_element(obj) def sanitize_sequence(data): From 735038f48e6d0801683e92a1e9b18832a3a1aa51 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 13 Jul 2023 19:37:49 +0200 Subject: [PATCH 4/9] eliminate conversion to list and assignment of cid --- lib/matplotlib/cbook.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index 18c08b197f76..a4e820c63268 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -286,7 +286,7 @@ def process(self, s, *args, **kwargs): """ if self._signals is not None: _api.check_in_list(self._signals, signal=s) - for cid, ref in list(self.callbacks.get(s, {}).items()): + for ref in self.callbacks.get(s, {}).values(): func = ref() if func is not None: try: From 6ae9b665293014b63b5213bf42746cbc8304b8b5 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 13 Jul 2023 20:47:30 +0200 Subject: [PATCH 5/9] avoid mutation of dict while iterating --- lib/matplotlib/cbook.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index a4e820c63268..6b2bef482439 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -286,7 +286,7 @@ def process(self, s, *args, **kwargs): """ if self._signals is not None: _api.check_in_list(self._signals, signal=s) - for ref in self.callbacks.get(s, {}).values(): + for ref in list(self.callbacks.get(s, {}).values()): func = ref() if func is not None: try: From 2cafe975bca65ac12735fe56d635bdae5bb7f115 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 13 Jul 2023 20:48:03 +0200 Subject: [PATCH 6/9] faster minpos construction --- lib/matplotlib/transforms.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py index 1a9f7e1ad1c3..cbd613716a37 100644 --- a/lib/matplotlib/transforms.py +++ b/lib/matplotlib/transforms.py @@ -110,7 +110,7 @@ class TransformNode: invalidated, even if 'self' is already invalid. """ - def __init__(self, shorthand_name=None): + def __init__(self, shorthand_name=''): """ Parameters ---------- @@ -122,7 +122,7 @@ def __init__(self, shorthand_name=None): self._parents = {} # Initially invalid, until first computation. self._invalid = self._INVALID_FULL - self._shorthand_name = shorthand_name or '' + self._shorthand_name = shorthand_name if DEBUG: def __str__(self): @@ -671,6 +671,8 @@ def intersection(bbox1, bbox2): y1 = np.minimum(bbox1.ymax, bbox2.ymax) return Bbox([[x0, y0], [x1, y1]]) if x0 <= x1 and y0 <= y1 else None +_default_minpos = np.array([np.inf, np.inf]) + class Bbox(BboxBase): """ @@ -766,7 +768,7 @@ def __init__(self, points, **kwargs): raise ValueError('Bbox points must be of the form ' '"[[x0, y0], [x1, y1]]".') self._points = points - self._minpos = np.array([np.inf, np.inf]) + self._minpos = _default_minpos.copy() self._ignore = True # it is helpful in some contexts to know if the bbox is a # default or has been mutated; we store the orig points to From f3f1b4b3d62c09b48269e2114d0cf7c9e0c8c28d Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 13 Jul 2023 21:01:50 +0200 Subject: [PATCH 7/9] whitespace --- lib/matplotlib/colors.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index 2241a5eed311..f36e19a1d17a 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -212,6 +212,7 @@ def _sanitize_extrema(ex): _nth_color_re = re.compile(r"\AC[0-9]+\Z") + def _is_nth_color(c): """Return whether *c* can be interpreted as an item in the color cycle.""" return isinstance(c, str) and _nth_color_re.match(c) From 2e4bd65d68cc5536ea73e5d87483b345f1f022d0 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Fri, 14 Jul 2023 09:58:12 +0200 Subject: [PATCH 8/9] revert change to shorthand --- lib/matplotlib/transforms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py index cbd613716a37..7e39807e4c71 100644 --- a/lib/matplotlib/transforms.py +++ b/lib/matplotlib/transforms.py @@ -110,7 +110,7 @@ class TransformNode: invalidated, even if 'self' is already invalid. """ - def __init__(self, shorthand_name=''): + def __init__(self, shorthand_name=None): """ Parameters ---------- @@ -122,7 +122,7 @@ def __init__(self, shorthand_name=''): self._parents = {} # Initially invalid, until first computation. self._invalid = self._INVALID_FULL - self._shorthand_name = shorthand_name + self._shorthand_name = shorthand_name or '' if DEBUG: def __str__(self): From 18b4a542b26e0fb30dc7b94e0760f0eb445d01cd Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sat, 15 Jul 2023 21:33:01 +0200 Subject: [PATCH 9/9] use math.isfinite --- lib/matplotlib/cbook.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index 6b2bef482439..0c49c70b37fa 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -1634,8 +1634,10 @@ def _safe_first_finite(obj, *, skip_nonfinite=True): def safe_isfinite(val): if val is None: return False - if isinstance(val, int): - return True + try: + return math.isfinite(val) + except TypeError: + pass try: return np.isfinite(val) if np.isscalar(val) else True except TypeError: