Skip to content

Manual xlim log stackplots not showing, linear stackplots segfault #3626

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

Closed
nicktimko opened this issue Oct 9, 2014 · 8 comments
Closed

Comments

@nicktimko
Copy link

I was trying to make some hacky split axes, things didn't show up, thought it was me, then after trying to make a SSCCE and getting it to segfault I think it's maybe only 90% me.

import numpy as np
import matplotlib.pyplot as plt

f, ax = plt.subplots()
#ax.set_xlim(10**-10, 1) # works
ax.set_xlim(10**-9.2679, 1) # sometimes broken
#ax.set_xlim(10**-4, 1) # broken

x = np.logspace(-10, 5, 20)
data = np.random.random((2, 20))
ax.set_xscale('log') # segfaults instead of just not showing if commented
ax.stackplot(x, *data)

f.show()

Uncomment as commented to reproduce. It randomly doesn't work on the seemingly-arbitrary lower bound (10**-9.2679).

The real fun seems to be if you comment out ax.set_xscale('log'), then Python simply segfaults when attempting to either f.show() or f.savefig('test') (after any of the provided set_xlim calls).

Repro'd on

  • MPL 1.4.0
    • Ubuntu 14.04 64-bit, Python 2.7.8 (64-bit), Numpy 1.8.2, TkAgg
    • Windows 7 64-bit, Python 3.4.1 (64-bit), Numpy 1.9.0, TkAgg and Agg
    • (with variant, see below) OS X 10.9.5, Python 3.4.1/2.7.8, Numpy 1.9.0, MacOSX and Agg
@cimarronm
Copy link
Contributor

I am not able to reproduce either issue (segfaulting or missing plots) MacOSX backend and v1.4.0

@nicktimko
Copy link
Author

@cimarronm I concur that the above does not terminate on Mac OS 10.9, MPL 1.4.0, but with some monkeying I was able to get the following to segfault on Python 3.4.2 and 2.7.8 (on OS X). Basically all I had to do was change .show() into .savefig(). Reducing magic magically cures it for me.

import matplotlib.pyplot as plt
magic = 100000 # 61003-61004 is critical point for me
plt.stackplot([1, magic], [1, 1])
plt.gca().set_xlim(1, 10)
plt.savefig('test')

@jenshnielsen
Copy link
Member

I can reproduce the segfault on OS X Python 3.4.2 with the IPython Inline backend i.e. Agg

I cant reproduce the other issue.

@nicktimko
Copy link
Author

Yet more prodding to get the plot to disappear without crashing on OS X:

import matplotlib.pyplot as plt
f, ax = plt.subplots()
ax.set_xlim(10**-7, 1e5) # broken with savefig
#ax.set_xlim(10**-8, 1e5) # works
ax.set_xscale('log')
ax.stackplot([1e-10, 1, 1e5], [1, 2, 4])
f.savefig('test')
#plt.show() # works with either set of xlims

I wonder if the plot not showing and segfault are different issues; both seem to be related to stackplot() though (but that's just how I get them to occur).

@cimarronm
Copy link
Contributor

Yeah, looks like backend related. I can reproduce the crash with TkAgg backend but not MacOSX backend

@tacaswell
Copy link
Member

There was a similar smelling crash related to image boxes fixed recently that had to do with messed up our negative indexing in to the agg buffers

@cimarronm
Copy link
Contributor

The problem occurs when the path collection used to create the stackplot has lines with vertices that are extremely far apart horizontally (in this case due to the exponential x values and a view limit that is only [0 1]) and it is being rendered in agg through backend_agg.draw_markers. While agg is rendering, rasterizer_cells_aa<Cell>::line tries to break up large horizontal paths through a recursive call by breaking the line into two separate lines and calling itself.

    void rasterizer_cells_aa<Cell>::line(int x1, int y1, int x2, int y2)
    {
        enum dx_limit_e { dx_limit = 16384 << poly_subpixel_shift };

        int dx = x2 - x1;

        if(dx >= dx_limit || dx <= -dx_limit)
        {
            int cx = (x1 + x2) >> 1;
            int cy = (y1 + y2) >> 1;
            line(x1, y1, cx, cy);   // RECURSION
            line(cx, cy, x2, y2);   // RECURSION
        }
...

Since the line is so large, it ends up recursing until the stack is depleted and generates a segment fault.

This seems to be fixed by drawing the collection through backend_agg.draw_path_collection only. I have a PR to do just that (#3761) but I'm not sure it's really the right fix. I actually have no idea why draw_markers is being called to draw the path sometimes; it seems like it should always be draw_path_collection. I'm guessing draw_markers is used for some sort of optimization or other reason but I am not familiar enough with that part of the code to know what is really going on. @mdboom, perhaps you could take a look at it.

@mdboom
Copy link
Member

mdboom commented Nov 21, 2014

I'll briefly explain the purpose of the draw_markers optimization. If the collection consists of a single path in a single color, linestyle etc. than we can just render it once in an off-screen buffer and then "rubberstamp" that item multiple times (this is what draw_markers does). This turns out to be much faster than rasterizing each path over and over. But I think as this bug shows, that doesn't work when the stamp is really large -- we can't do any clipping on the stamp because we don't know where it will be drawn on the image. So I think the real fix is to also detect the case where the stamp is really large and fall back to draw_path_collection in that case. I'll see if I can work something up like that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants