Skip to content

[Bug]: Layout Managers are confused by complex arrangement of sub-figures and gridspec's #30076

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
boffi opened this issue May 19, 2025 · 4 comments · May be fixed by #30089
Open

[Bug]: Layout Managers are confused by complex arrangement of sub-figures and gridspec's #30076

boffi opened this issue May 19, 2025 · 4 comments · May be fixed by #30089
Labels
status: confirmed bug topic: geometry manager LayoutEngine, Constrained layout, Tight layout
Milestone

Comments

@boffi
Copy link

boffi commented May 19, 2025

Bug summary

When I run the code below

  1. compressed.png and constrained.png are (visually) identical, and the axes in the left of each sub-figure are getting smaller and smaller
  2. tight.png has all the axes in each sub-figure the same sizes, but those sizes are wrong
  3. none.png is correct (all the sizes remain unchanged, the sizes are OK) but the axes decorations overlap the plotting area

For your convenience I've uploaded the image files produced by my code.

Thank you in advance — gb


This behaviour was brought to my attention by
https://stackoverflow.com/questions/79614691/why-are-the-subplots-in-the-subfigures-getting-smaller

Code for reproduction

import matplotlib.pyplot as plt


def test(layout):
    fig = plt.figure(figsize=(12, 16), layout=layout)
    figures = fig.subfigures(4, 2)

    for f in figures.flatten():
        gs = f.add_gridspec(3, 2)
        for i in range(3):
            f.add_subplot(gs[i, 0]).plot()
        f.add_subplot(gs[:, 1]).plot()
    fig.savefig(layout + ".png")


test("compressed")
test("constrained")
test("none")
test("tight")

Actual outcome

Image
Image
Image
Image

Expected outcome

Something like none.png w/o the axes decoration overlapping the plot areas

Additional information

No response

Operating system

Open Suse Tumbleweed

Matplotlib Version

3.10.1

Matplotlib Backend

qtagg

Python version

Python 3.13.3

Jupyter version

No response

Installation

Linux package manager

@tacaswell
Copy link
Member

I have seen similar things with code that set a fixed aspect ratio and manually adjusted the locations before running the layout code.

dropping to either the figures rows or cols being 1 makes it work and

import matplotlib.pyplot as plt


def test(layout):
    fig = plt.figure(figsize=(12, 16), layout=layout)
    figures = fig.subfigures(4, 2)
    fig.suptitle(layout)
    for f in figures.flatten():
        axd = fig.subplot_mosaic('AD;BD;CD')
        for ax in axd.values():
            ax.plot()
    # fig.savefig(layout + ".png")


test("compressed")
test("constrained")
test("none")
test("tight")

is broken even worse

@tacaswell tacaswell added this to the v3.11.0 milestone May 19, 2025
@jklymak
Copy link
Member

jklymak commented May 19, 2025

Hmmm, well somehow the margin for the first subfigure is the starting point for the second figure, and then the margin gets made even larger for the second.

import matplotlib.pyplot as plt



def test(layout, labelled=False):
    ll = labelled
    fig = plt.figure(figsize=(4, 5), layout=layout)
    fig.suptitle(f'Labelled = {not labelled}', fontsize='small')
    figures = fig.subfigures(2, 1)
    for f in figures.flatten():
        gs = f.add_gridspec(2, 2)
        for i in range(2):
            ax = f.add_subplot(gs[i, 0])
            ax.plot()
            if not labelled:
                ax.set_xlabel('BOOO', fontsize='large')
                labelled = True
        f.add_subplot(gs[:, 1]).plot()
    fig.savefig(f'fig_{not ll}.png')
test("constrained")
test("constrained", labelled=True)
plt.show()

No label

Image

big label

Image

@jklymak
Copy link
Member

jklymak commented May 19, 2025

I played with this for a while, and I do not see any obvious error in the logic.

At its root, I think this is a limitation with the we we have chosen to set up the constraint solver, in that the behaviour above is following the constraints we have given it, but they are not rigorous enough to narrow the gap.

I appreciate that it would be more sensible for the subfigures to not care about each other's layout, but practically, that is not how the constraint engine is set up, and the constraints are all solved at the same time.

If you want deterministic behaviour here without changing the internals of constrained layout, you can simply create more subfigures so that the left-hand axes are not tied to what the right-hand column is doing:

import matplotlib.pyplot as plt

def test(layout):
    fig = plt.figure(figsize=(4, 5), layout='constrained')
    figures = fig.subfigures(2, 1)
    figures[0].set_facecolor('grey')
    figures[1].set_facecolor('red')
    for nn, f in enumerate(figures.flatten()):
        ffigs = f.subfigures(1, 2)
        ffigs[0].set_facecolor('lightgreen')
        ax = ffigs[0].subplots(2, 1)
        for i in range(2):
            ax[i].plot()
            if (nn==0) and (i == 0):
                ax[i].set_xlabel('BOOOOO', fontsize=20)
        ffigs[1].set_facecolor('lightblue')
        ax = ffigs[1].add_subplot()
        ax.plot()
    fig.savefig(f'Fixed.png')

test("constrained")
plt.show()

Image

We can keep this open, but not sure anything will be done about it in the near future. If we were to do anything, it might be to try and make subfigure1 independent of subfigure2. The other thing that could be improved is the assignment of space between axes in one column when the other column does not have any axes spines at that row (eg the case here). That case is mildly under constrained, and as shown here doesn't work consistently.

@jklymak jklymak added the topic: geometry manager LayoutEngine, Constrained layout, Tight layout label May 19, 2025
@jklymak
Copy link
Member

jklymak commented May 20, 2025

Actually I found the bug #30089 is an easy fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: confirmed bug topic: geometry manager LayoutEngine, Constrained layout, Tight layout
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants