Repository: matplotlib/matplotlib
Issue category: Bug Report

[Bug]: Figure.align_labels() confused by GridSpecFromSubplotSpec
### Bug summary

In a composite figure with nested gridspecs, `Figure.align_labels()` (and `align_xlabels()`, `align_ylabels()`) can end up aligning labels that should not intuitively be. Likewise with `align_titles()`.

### Code for reproduction

```Python
fig = plt.figure(figsize=(6, 4))

gs0 = gridspec.GridSpec(nrows=1, ncols=2, figure=fig)
gs00 = gs0[0].subgridspec(nrows=2, ncols=1, height_ratios=[8, 8])
gs01 = gs0[1].subgridspec(nrows=2, ncols=1, height_ratios=[9, 6])

left_axs = gs00.subplots()
right_axs = gs01.subplots()

left_axs[0].set_ylim(0, 0.02)  # to force nontrivial alignment

left_axs[0].set_ylabel('foo')
left_axs[1].set_ylabel('bar')
right_axs[0].set_ylabel('baz')
right_axs[1].set_ylabel('qux')

left_axs[1].set_title('title')
right_axs[1].set_title('title')

fig.align_labels()
fig.align_titles()
```


### Actual outcome

All labels are aligned. Titles are aligned as well.

![image](https://github.com/user-attachments/assets/b78defcf-e742-4643-b8b9-1f66ce375997)

### Expected outcome

Labels in separate columns are aligned, but labels in different columns should not be. Titles are not aligned:

![image](https://github.com/user-attachments/assets/795a7730-b2b3-442b-896f-cdde31b7ff97)


### Additional information

Right now, the ylabel (xlabel) alignment code seems to attempt to align labels on Axes with the same column index (resp. row index) without checking if those indexes are for the same gridspec. To fix this, we should probably add a check that two Axes share the same gridspec (in addition to being in the same row/col) before we align their labels. (This would not allow label alignment across gridspecs, but if a user wants to align labels between two Axes, it seems reasonable to expect them to put the Axes in the same gridspec.)

The same thing happens with align_titles().

For now, a workaround for labels is to call `Figure.align_labels()` separately for each sub-gridspec with the `axs` kwarg (as done for the expected outcome figure above).

### Operating system

macOS 14.1.1

### Matplotlib Version

3.9.2

### Matplotlib Backend

module://matplotlib_inline.backend_inline

### Python version

3.12.2

### Jupyter version

_No response_

### Installation

conda


Functions to edit:
lib/matplotlib/figure.py:FigureBase.align_xlabels
lib/matplotlib/figure.py:FigureBase.align_ylabels
lib/matplotlib/figure.py:FigureBase.align_titles
lib/matplotlib/figure.py:FigureBase.align_labels

Patch:
diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py
index e5cf88131178..3d6f9a7f4c16 100644
--- a/lib/matplotlib/figure.py
+++ b/lib/matplotlib/figure.py
@@ -1382,8 +1382,8 @@ def align_xlabels(self, axs=None):
 
         Notes
         -----
-        This assumes that ``axs`` are from the same `.GridSpec`, so that
-        their `.SubplotSpec` positions correspond to figure positions.
+        This assumes that all Axes in ``axs`` are from the same `.GridSpec`,
+        so that their `.SubplotSpec` positions correspond to figure positions.
 
         Examples
         --------
@@ -1444,8 +1444,8 @@ def align_ylabels(self, axs=None):
 
         Notes
         -----
-        This assumes that ``axs`` are from the same `.GridSpec`, so that
-        their `.SubplotSpec` positions correspond to figure positions.
+        This assumes that all Axes in ``axs`` are from the same `.GridSpec`,
+        so that their `.SubplotSpec` positions correspond to figure positions.
 
         Examples
         --------
@@ -1500,8 +1500,8 @@ def align_titles(self, axs=None):
 
         Notes
         -----
-        This assumes that ``axs`` are from the same `.GridSpec`, so that
-        their `.SubplotSpec` positions correspond to figure positions.
+        This assumes that all Axes in ``axs`` are from the same `.GridSpec`,
+        so that their `.SubplotSpec` positions correspond to figure positions.
 
         Examples
         --------
@@ -1544,6 +1544,11 @@ def align_labels(self, axs=None):
         matplotlib.figure.Figure.align_xlabels
         matplotlib.figure.Figure.align_ylabels
         matplotlib.figure.Figure.align_titles
+
+        Notes
+        -----
+        This assumes that all Axes in ``axs`` are from the same `.GridSpec`,
+        so that their `.SubplotSpec` positions correspond to figure positions.
         """
         self.align_xlabels(axs=axs)
         self.align_ylabels(axs=axs)