Skip to content
Advertisement

Matplotlib pickle error “TypeError: cannot pickle ‘kiwisolver.Solver’ object”

In my project I’m trying to embed the same matplotlib figure in two places and seem to need to make a copy, as I’m using blitting on the original, and embedding a figure in two places & closing one causes a crash issue w/ blitting an imshow in matplotlib (but not scatterplots). In order to do this I’m using pickle on the matplotlib figure, but I’m getting the error stated in the title. The function where I’m doing the embedding is below, but the first line where dumps/loads from pickle is erroring only my figure w/ scatterplots.

  def embedPlot(self, fig):
    fig = pickle.loads(pickle.dumps(fig))
    temp = self.currentEmbed if self.currentEmbed else None

    # set max width/height based on screensize and dpi
    fig.set_size_inches(self.root.winfo_screenwidth()/self.root.winfo_fpixels('1i')-1, self.root.winfo_screenheight()/self.root.winfo_fpixels('1i')-1)
    self.currentEmbed = Frame(self.root)
    canvas = FigureCanvasTkAgg(fig, master = self.currentEmbed)
    canvas.draw()
    canvas.get_tk_widget().pack()
    self.currentEmbed.grid(row=0, column=0, padx=2, pady=2, columnspan=4)

    # delete old canvas each time, as creating new FigureCanvasTkAgg's can cause big slowdown when closing window (there's probably a better way to do this)
    if temp:
        temp.destroy()

I store my different figures in a list and embed the one that is the current “focus” w/ the embed function above. And for reference here is my (messy) figure that is the one that errors.

def blitgenerateTSNEPlots():
    def getTSNE(idx):
        # get closest points & norms to all points from the example at idx
        norms,idxs,prediction = findNearest(exdata,exoutput,getTSNE.advdata,idx,displayEpsilon)

        # restore backgrounds, clearing foregound and allowing redrawing of artists
        getTSNE.fig.canvas.restore_region(getTSNE.background)
        getTSNE.fig.canvas.restore_region(getTSNE.titleBackground)

        # change array for scatterplot so it'll recolor, changing offsets of cb so the closest 10 points will be in their new positions, and update title based on new model prediction
        getTSNE.scatterPlot.set_array(norms)
        getTSNE.cb.set_offsets([ [getTSNE.X_2d[i,0], getTSNE.X_2d[i,1]] for i in idxs])
        getTSNE.title.set_text(f"Model Prediction: {prediction}nAverage Distance: {round(float(sum(norms))/len(norms),2)}")

        # redraw artists
        getTSNE.ax2.draw_artist(getTSNE.title)
        getTSNE.ax2.draw_artist(getTSNE.scatterPlot)
        getTSNE.ax2.draw_artist(getTSNE.cb)

        # blit bounding boxes of axis and text so figure will update
        getTSNE.fig.canvas.blit(getTSNE.ax2.bbox)
        getTSNE.fig.canvas.blit(getTSNE.title.get_window_extent())

        getTSNE.fig.canvas.flush_events()

        return getTSNE.fig
    # create figures and turn off all axes ticks
    getTSNE.fig, (getTSNE.ax1, getTSNE.ax2) = plt.subplots(1,2,constrained_layout=True)
    getTSNE.ax1.set_xticks([])
    getTSNE.ax1.set_yticks([])
    getTSNE.ax2.set_xticks([])
    getTSNE.ax2.set_yticks([])

    # load adversarial data for epsilon and get closest points to idx 0 for initial plot creation
    getTSNE.advdata = get_data(npys,displayEpsilon)
    norms,idxs,prediction = findNearest(exdata,exoutput,getTSNE.advdata,0,displayEpsilon)

    # generate tsne embedding based on original set of data
    X_2d = []
    if os.path.exists("./embedding.npy"):
        X_2d = np.load('./embedding.npy').astype(np.float64)
    else:
        tsne = TSNE(n_components=2, random_state=4, perplexity=100)
        origdata = get_data(npys,'e0')
        X_2d = tsne.fit_transform(origdata)
        np.save('./embedding.npy', X_2d, allow_pickle=False)
    getTSNE.X_2d = X_2d

    # create scatter of all points colored & labaled by class. this one never needs to be updated, it's static
    colors = 'r', 'g', 'b', 'c', 'm', 'y', 'k', 'aquamarine', 'orange', 'purple'
    for c, label in zip(colors, labels):
        getTSNE.ax1.scatter(X_2d[(testlabels[:] == label), 0], X_2d[(testlabels[:] == label), 1], c=c, label=label, s=3)
    getTSNE.ax1.set_title("Test Data")
    getTSNE.ax1.legend()

    # setting title as blank but with a big size to get background for blitting
    getTSNE.title = getTSNE.ax2.set_title("  n                                                     ")

    colorLim = (4,13)

    # manually create colorbar before second scatterplot has been made
    getTSNE.fig.colorbar(matplotlib.cm.ScalarMappable(norm=matplotlib.colors.Normalize(vmin=colorLim[0],vmax=colorLim[1]),cmap='viridis'),ax=[getTSNE.ax1,getTSNE.ax2],label="norm")

    # draw figure and store bounding boxes of scatter background & title background
    getTSNE.background = getTSNE.fig.canvas.copy_from_bbox(getTSNE.ax2.bbox)
    getTSNE.fig.canvas.draw()
    getTSNE.titleBackground = getTSNE.fig.canvas.copy_from_bbox(getTSNE.title.get_window_extent())

    # create scatter plot of all data colored by example's distance from original data & closest 10 points
    getTSNE.scatterPlot = getTSNE.ax2.scatter(X_2d[:,0], X_2d[:,1], c=norms[:], s=3, cmap='viridis', zorder=1)
    getTSNE.cb = getTSNE.ax2.scatter(X_2d[idxs,0],X_2d[idxs,1], c='red', s=7, zorder=2)
    getTSNE.scatterPlot.set_clim(colorLim[0],colorLim[1])

    return getTSNE
generateTSNEPlots = blitgenerateTSNEPlots()

If anyone knows either how to fix the pickling issue w/ the scatterplots, or a different way to work around the total crash with an imshow, either would achieve what I’m trying to do.

Advertisement

Answer

Turns out the issue was setting constrained_layout. Setting it to false allows figure to be successfully pickled without any errors.

User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement