Matplotlib Quiver memory leak - python

I have created quite a nice visualization program using Qt as the interface layer and matplotlib to draw much of the content. I use contour and countourf as well as text and lines and never run into problems with releasing objects.
As soon as I start calling Axes.quiver I get leaks. With the size of the dataset it adds up quickly. The following code demonstrates the problem:
from pylab import *
from numpy import ma
import time
X,Y = meshgrid( arange(0,2*pi,.04),arange(0,2*pi,.04) )
U = cos(X)
V = sin(Y)
fig = figure()
ax = fig.add_axes([0.1,0.1,0.8,0.8])
for i in range(90):
Q = ax.quiver( U, V)
time.sleep(0.2)
Q.remove()
I know this simple snippet isnt drawing to screen (leak becomes worse then).
Q has a sys.getrefcount of 4 so nothing I am able to do seems to get rid of it. Calls to fig and ax clear only reduce the refcount to 2. Quiver draws the image i want, but I am completely out of ideas.
My code is much more complex than this. I have tried completely replacing the axes objects but that doesnt help. I really am not free to replace the Figure instance.

Related

Matplotlib Imshow Doesn't Update With Draw

I'm having trouble getting imshow to update with new data. For reference I'm pulling data off a serial port and trying to plot it, updating every second or so. I had been accumulating the data with a thread, so I initially thought that might be the problem as matplotlib isn't thread safe. However, I can't get the following simpler example to work:
import numpy as np
import matplotlib.pyplot as plt
import time
dat = np.random.rand(100,10)
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
image = ax.imshow(np.zeros((10,10)))
fig.canvas.draw()
count = 0
while count < 100:
image.set_data(dat[count:count+10])
fig.canvas.draw()
count += 10
time.sleep(1)
Using TkAgg, I just get the plot of all zeros, it never updates then quits.
With Qt5Agg, an empty window pops up before quitting.
I've tried various combinations of draw_idle(), flush_events() and plt.show(block=False), with the same results.
python 3.8.10 , matplotlib 3.2.2
Immediately after posting this I figured out the solution.
I initialized the plot with all zeros - changing this to be a random array of values fixes it fixes it.
I'm not sure why starting with all zeros broke the color scaling, though from the matplotlib documentation the default normalization scales the input data on [0,1], so I suspect that was the issue.

Live plotting on bloch sphere

I am trying to plot live data on a bloch sphere using Qutip's function bloch().
So far, the code always interrupts, when I have a b.show() in there.
I found a lot of solutions online to similar problems, but most of them make use of direct matplotlib commands like matplotlib.draw() which doesn't seem to work with the bloch class.
Then, there are other solutions which make use of for example Tk or GTKagg (e.g. https://stackoverflow.com/a/15742183/3276735 or real-time plotting in while loop with matplotlib)
Can somebody please help me how to deal with the same problem in the bloch class?
Edit:
Here's a minimal example:
Basically, I want to update my plot with one point at a time, preferably in a loop. My goal is to display live data in the plot that has to be read from a file.
import qutip as qt
import numpy as np
b = qt.Bloch()
theta = np.arange(0,np.pi,0.1)
for ii in range(len(theta)):
b.add_points([np.sin(theta[ii]),0,np.cos(theta[ii])])
b.show()
I think you are breaking your plot because you are calling show for every point. Try calling show outside the loop (in the end).
import qutip as qt
import numpy as np
b = qt.Bloch()
theta = np.arange(0,np.pi,0.1)
for ii in range(len(theta)):
b.add_points([np.sin(theta[ii]),0,np.cos(theta[ii])])
b.show() # Changed here
EDIT: Animated plot
Consider show as an absolute command to call the plot into view. It's not a draw command (or redraw). If you do want to show an image every "n" seconds or so you'll need to clear the plot before calling it again. You may try this:
import qutip as qt
import numpy as np
b = qt.Bloch()
theta = np.arange(0,np.pi,0.1)
for ii in range(len(theta)):
b.clear()
b.add_points([np.sin(theta[ii]),0,np.cos(theta[ii])])
b.show()
# wait time step and load new value from file.
, I don't have QuTip in my current distribution so I can't really test it but I'm betting its heavily based in matplotlib. My best advise however is for you to use the formulation give for animation in the QuTiP docs. By following this recipe:
from pylab import *
import matplotlib.animation as animation
from mpl_toolkits.mplot3d import Axes3D
fig = figure()
ax = Axes3D(fig,azim=-40,elev=30)
sphere=Bloch(axes=ax)
def animate(i):
sphere.clear()
sphere.add_vectors([sin(theta),0,cos(theta)])
sphere.add_points([sx[:i+1],sy[:i+1],sz[:i+1]])
sphere.make_sphere()
return ax
def init():
sphere.vector_color = ['r']
return ax
ani = animation.FuncAnimation(fig, animate, np.arange(len(sx)),
init_func=init, blit=True, repeat=False)
ani.save('bloch_sphere.mp4', fps=20, clear_temp=True)
, you should be able to modify the animate function to perform all operations you need.

Real-time plot in matplotlib - python

I'm trying to get real-time spectrum analyzer type plot in matplotlib. I've got some code working (with help from other posts on StackOverflow) as follows:
import time
import numpy as np
import matplotlib.pyplot as plt
plt.axis([0, 1000, 0, 1])
plt.ion()
plt.show()
i=0
np.zeros([1,500],'float')
lines=plt.plot(y[0])
while 1:
i=i+1
lines.pop(0).remove()
y = np.random.rand(1,100)
lines=plt.plot(y[0])
plt.draw()
The code works and I'm getting what I want, but there is a serious problem. The plot window would freeze after some time. I know the program is still running by inspecting the i variable (I'm running the code in Anaconda/Spyder so I can see the variables). However the plot window would show "Non responding" and if I terminate the python program in Spyder by ctrl+c, the plot window comes back to life and show the latest plot.
I'm out of wits here as how to further debug the issue. Anyone to help?
Thanks
I am not sure that adding plt.pause will entirely solve your issue. It may just take longer before the application crash. The memory used by your application seems to constantly increase over time (even after adding plt.pause). Below are two suggestions that may help you with your current issue:
Instead of removing/recreating the lines artists with each iteration with remove and plot, I would use the same artist throughout the whole animation and simply update its ydata.
I'll use explicit handlers for the axe and figure and call show and draw explicitly on the figure manager and canvas instead of going with implicit calls through pyplot, following the advices given in a post by tcaswell.
Following the above, the code would look something like this:
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.axis([0, 100, 0, 1])
y = np.random.rand(100)
lines = ax.plot(y)
fig.canvas.manager.show()
i=0
while 1:
i=i+1
y = np.random.rand(100)
lines[0].set_ydata(y)
fig.canvas.draw()
fig.canvas.flush_events()
I've run the above code for a good 10 minutes and the memory used by the application remained stable the whole time, while the memory used by your current code (without plt.pause) increased by about 30MiB over the same period.
To answer myself, I solved the issue by adding
plt.pause(0.01)
after the
plt.draw()
This probably allows the GUI to finish the drawing and clear the buffer somewhere (my guess) before the new data comes in.
I know I'm late to answer this question, but for your issue you could look into the "joystick" package. It is based on the line.set_data() and canvas.draw() methods, with optional axes re-scaling, hence most probably faster than removing a line and adding a new one. It also allows for interactive text logging or image plotting (in addition to graph plotting).
No need to do your own loops in a separate thread, the package takes care of it, just give the update frequency you wish. Plus the terminal remains available for more monitoring commands while live plotting, which is not possible with a "while True" loop.
See http://www.github.com/ceyzeriat/joystick/ or https://pypi.python.org/pypi/joystick (use pip install joystick to install)
try:
import joystick as jk
import numpy as np
import time
class test(jk.Joystick):
# initialize the infinite loop decorator
_infinite_loop = jk.deco_infinite_loop()
def _init(self, *args, **kwargs):
"""
Function called at initialization, see the doc
"""
self._t0 = time.time() # initialize time
self.xdata = np.array([self._t0]) # time x-axis
self.ydata = np.array([0.0]) # fake data y-axis
# create a graph frame
self.mygraph = self.add_frame(jk.Graph(name="test", size=(500, 500), pos=(50, 50), fmt="go-", xnpts=100, xnptsmax=1000, xylim=(None, None, 0, 1)))
#_infinite_loop(wait_time=0.2)
def _generate_data(self): # function looped every 0.2 second to read or produce data
"""
Loop starting with the simulation start, getting data and
pushing it to the graph every 0.2 seconds
"""
# concatenate data on the time x-axis
self.xdata = jk.core.add_datapoint(self.xdata, time.time(), xnptsmax=self.mygraph.xnptsmax)
# concatenate data on the fake data y-axis
self.ydata = jk.core.add_datapoint(self.ydata, np.random.random(), xnptsmax=self.mygraph.xnptsmax)
self.mygraph.set_xydata(t, self.ydata)
t = test()
t.start()
t.stop()

Update/Refresh matplotlib plots on second monitor

At the moment I am working with Spyder and doing my plotting with matplotlib. I have two monitors, one for development and another for (data) browsing and other stuff. Since I am doing some calculations and my code often changes, I often (re)execute the code and have a look at the plots to check if the results are valid.
Is there any way to place my matplotlib plots on a second monitor and refresh them from the main monitor?
I have already searched for a solution but could not find anything. It would be really helpful for me!
Here's some additional information:
OS: Ubuntu 14.04 (64 Bit)
Spyder-Version: 2.3.2
Matplotlib-Version: 1.3.1.-1.4.2.
I know it's an old question but I came across a similar problem and found this question. I managed to move my plots to a second display using the QT4Agg backend.
import matplotlib.pyplot as plt
plt.switch_backend('QT4Agg')
# a little hack to get screen size; from here [1]
mgr = plt.get_current_fig_manager()
mgr.full_screen_toggle()
py = mgr.canvas.height()
px = mgr.canvas.width()
mgr.window.close()
# hack end
x = [i for i in range(0,10)]
plt.figure()
plt.plot(x)
figManager = plt.get_current_fig_manager()
# if px=0, plot will display on 1st screen
figManager.window.move(px, 0)
figManager.window.showMaximized()
figManager.window.setFocus()
plt.show()
[1] answer from #divenex: How do you set the absolute position of figure windows with matplotlib?
This has to do with matplotlib, not Spyder. Placing the location of a figure explicitly appears to be one of those things for which there's really just workarounds ... see the answers to the question here. That's an old question, but I'm not sure there's been change since then (any matplotlib devs, feel free to correct me!).
The second monitor shouldn't make any difference, it sounds like the issue is just that the figure is being replaced with a new one.
Fortunately you can update figures you've moved to where you want them pretty easily, by using the object interface specifically, and updating the Axes object without creating a new figure. An example is below:
import matplotlib.pyplot as plt
import numpy as np
# Create the figure and axes, keeping the object references
fig = plt.figure()
ax = fig.add_subplot(111)
p, = ax.plot(np.linspace(0,1))
# First display
plt.show()
# Some time to let you look at the result and move/resize the figure
plt.pause(3)
# Replace the contents of the Axes without making a new window
ax.cla()
p, = ax.plot(2*np.linspace(0,1)**2)
# Since the figure is shown already, use draw() to update the display
plt.draw()
plt.pause(3)
# Or you can get really fancy and simply replace the data in the plot
p.set_data(np.linspace(-1,1), 10*np.linspace(-1,1)**3)
ax.set_xlim(-1,1)
ax.set_ylim(-1,1)
plt.draw()

Step by step showing 2D/3D add/remove points

I'm writing some python functions that deals with manipulating sets of 2D/3D coordinates, mostly 2D.
The issue is debugging such code is made difficult just by looking at the points. So I'm looking for some software that could display the points and showing which points have been added/removed after each step. Basically, I'm looking for a turn my algorithm into an animation.
I've seen a few applets online that do things similar what I was looking for, but I lack the graphics/GUI programming skills to write something similar at this point, and I'm not sure it's wise to email the authors of things whose last modified timestamps reads several years ago. I should note I'm not against learning some graphics/GUI programming in the process, but I'd rather not spend more than 1-3 days if it can't be helped, although such links are still appreciated. In this sense, linking to a how-to site for writing such a step-by-step program might be acceptable.
With the matplotlib library it is very easy to get working animations up. Below is a minimal example to work with. The function generate_data can be adapted to your needs:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
def generate_data():
X = np.arange(25)
Y = X**2 * np.random.rand(25)
return X,Y
def update(data):
mat[0].set_xdata(data[0])
mat[0].set_ydata(data[1])
return mat
def data_gen():
while True:
yield generate_data()
fig, ax = plt.subplots()
X,Y = generate_data()
mat = ax.plot(X,Y,'o')
ani = animation.FuncAnimation(fig, update, data_gen, interval=500,
save_count=10)
ani.save('animation.mp4')
plt.show()
This example was adapted from a previous answer and modified to show a line plot instead of a colormap.

Categories

Resources