From 2eeaa513a93a00cf7ff6b8866b92a1f465e39dae Mon Sep 17 00:00:00 2001 From: Scott Shambaugh Date: Tue, 20 Jan 2026 15:24:46 -0700 Subject: [PATCH 1/2] Refactor bezier poly coefficient calcs for speedup --- lib/matplotlib/bezier.py | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/lib/matplotlib/bezier.py b/lib/matplotlib/bezier.py index b9b67c9a72d6..8369a78ea31b 100644 --- a/lib/matplotlib/bezier.py +++ b/lib/matplotlib/bezier.py @@ -3,7 +3,7 @@ """ from functools import lru_cache -import math +from math import comb, factorial import warnings import numpy as np @@ -11,15 +11,23 @@ from matplotlib import _api -# same algorithm as 3.8's math.comb -@np.vectorize -@lru_cache(maxsize=128) -def _comb(n, k): - if k > n: - return 0 - k = min(k, n - k) - i = np.arange(1, k + 1) - return np.prod((n + 1 - i)/i).astype(int) +@lru_cache(maxsize=16) +def _get_coeff_matrix(n): + """ + Compute the matrix for converting Bezier control points to polynomial + coefficients for a curve of degree n. + + The matrix M is such that M @ control_points gives polynomial coefficients. + Entry M[j, i] = C(n, j) * (-1)^(i+j) * C(j, i) where C is the binomial + coefficient. + """ + def _comb(n, k): + return np.vectorize(comb)(n, k) + + j = np.arange(n + 1)[:, None] + i = np.arange(n + 1)[None, :] # _comb is non-zero for i <= j + prefactor = (-1) ** (i + j) * _comb(j, i) # j on axis 0, i on axis 1 + return (_comb(n, j) * prefactor).astype(float) class NonIntersectingPathException(ValueError): @@ -203,8 +211,8 @@ def __init__(self, control_points): self._cpoints = np.asarray(control_points) self._N, self._d = self._cpoints.shape self._orders = np.arange(self._N) - coeff = [math.factorial(self._N - 1) - // (math.factorial(i) * math.factorial(self._N - 1 - i)) + coeff = [factorial(self._N - 1) + // (factorial(i) * factorial(self._N - 1 - i)) for i in range(self._N)] self._px = (self._cpoints.T * coeff).T @@ -279,11 +287,7 @@ def polynomial_coefficients(self): if n > 10: warnings.warn("Polynomial coefficients formula unstable for high " "order Bezier curves!", RuntimeWarning) - P = self.control_points - j = np.arange(n+1)[:, None] - i = np.arange(n+1)[None, :] # _comb is non-zero for i <= j - prefactor = (-1)**(i + j) * _comb(j, i) # j on axis 0, i on axis 1 - return _comb(n, j) * prefactor @ P # j on axis 0, self.dimension on 1 + return _get_coeff_matrix(n) @ self.control_points def axis_aligned_extrema(self): """ From 95107628d59483fa7af46e7c4725b37ad2abeb09 Mon Sep 17 00:00:00 2001 From: Scott Shambaugh Date: Mon, 2 Feb 2026 17:17:01 -0700 Subject: [PATCH 2/2] Rework imports --- lib/matplotlib/bezier.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/bezier.py b/lib/matplotlib/bezier.py index 8369a78ea31b..7daca897e74d 100644 --- a/lib/matplotlib/bezier.py +++ b/lib/matplotlib/bezier.py @@ -3,7 +3,7 @@ """ from functools import lru_cache -from math import comb, factorial +import math import warnings import numpy as np @@ -22,7 +22,7 @@ def _get_coeff_matrix(n): coefficient. """ def _comb(n, k): - return np.vectorize(comb)(n, k) + return np.vectorize(math.comb)(n, k) j = np.arange(n + 1)[:, None] i = np.arange(n + 1)[None, :] # _comb is non-zero for i <= j @@ -211,8 +211,8 @@ def __init__(self, control_points): self._cpoints = np.asarray(control_points) self._N, self._d = self._cpoints.shape self._orders = np.arange(self._N) - coeff = [factorial(self._N - 1) - // (factorial(i) * factorial(self._N - 1 - i)) + coeff = [math.factorial(self._N - 1) + // (math.factorial(i) * math.factorial(self._N - 1 - i)) for i in range(self._N)] self._px = (self._cpoints.T * coeff).T