Search code examples
pythonmatplotlibanimationcomplex-numbers

How to animate a complex function with matplotlib?


I want to make an animation with the function ((phi^n)-((-1/phi)^n))/(5^0.5) (Binet's formula) as n ∈ ℝ, so that the graph starts as a straight line on the real axes then shifts into the actual graph. I have tried to add

from matplotlib.animation import FuncAnimation
.
.
.
    def g(val):
        main_graph.set_ydata(imag(f(x))*val)
        return main_graph,
    animation = FuncAnimation(main_graph, func=g, frames=arange(0, 10, 0.1), interval=10)
plt.show

However, it did not work and I have no clue why I followed various tutorials and all of them had the same result (An error) I also tried

import matplotlib.animation as animation
.
.
.
def init():
    main_graph.set_ydata([np.nan] * len(real(f(x))))
    return main_graph,
def g(val):
    main_graph.set_ydata(imag(f(x))*val)
    return main_graph,
ani = animation.FuncAnimation(main_graph, g, init_func=init, interval=2, blit=True, save_count=50)

The error, in both cases, is AttributeError: 'Line2D' object has no attribute 'canvas'.
Here is the full code

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from numpy import arange, real, imag
phi = (1+(5**0.5))/2
x = arange(0,5,0.01)
def f(x):
    return ((phi**(x+0j))-((-1/phi)**(x+0j)))/(5**0.5)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.spines['left'].set_position(('data', 0.0))
ax.spines['bottom'].set_position(('data', 0))
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
#labels for x and y axes
plt.xlabel('real')
plt.ylabel('imag')
plt.grid(alpha=.4,linestyle=':')
main_graph, = plt.plot(real(f(x)),imag(f(x)), label='((phi**(x+0j))-((-1/phi)**(x+0j)))/(5**0.5)')
plt.legend()
    def g(val):
        main_graph.set_ydata(imag(f(x))*val)
        return main_graph,
    animation = FuncAnimation(main_graph, func=g, frames=arange(0, 10, 0.1), interval=10)
plt.show()

To see the final graph use this code

import matplotlib.pyplot as plt
from numpy import arange, real, imag
phi = (1+(5**0.5))/2
x = arange(0,5,0.01)
def f(x):
    return ((phi**(x+0j))-((-1/phi)**(x+0j)))/(5**0.5)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.spines['left'].set_position(('data', 0.0))
ax.spines['bottom'].set_position(('data', 0))
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
#labels for x and y axes
plt.xlabel('real')
plt.ylabel('imag')
plt.grid(alpha=.4,linestyle=':')
main_graph, = plt.plot(real(f(x)),imag(f(x)), label='((phi**(x+0j))-((-1/phi)**(x+0j)))/(5**0.5)')
plt.legend()
plt.show()

Solution

  • I have adapted the example from matplotlib's animation documentation. Here's how the code has been modified to allow for the modification of axis elements (in this case, the legend) by setting blit=False

    import matplotlib.pyplot as plt
    from matplotlib.animation import FuncAnimation
    from numpy import arange, real, imag
    phi = (1+(5**0.5))/2
    x = arange(0,5,0.01)
    def f(x):
        return ((phi**(x+0j))-((-1/phi)**(x+0j)))/(5**0.5)
    fig = plt.figure()
    main_graph, = plt.plot(real(f(x)),imag(f(x)), label='((phi**(x+0j))-((-1/phi)**(x+0j)))/(5**0.5)')
    #labels for x and y axes
    plt.xlabel('real')
    plt.ylabel('imag')
    plt.grid(alpha=.4,linestyle=':')
    #plt.legend(loc=4)
    
    def init():
        global legs
        ax = fig.add_subplot(1, 1, 1)
        ax.spines['left'].set_position(('data', 0.0))
        ax.spines['bottom'].set_position(('data', 0))
        ax.spines['right'].set_color('none')
        ax.spines['top'].set_color('none')
        ax.set_ylim(-4,4)
        legs=ax.legend(loc=4, prop={'size': 12})
        return main_graph, 
        
    def g(val):
        main_graph.set_ydata(imag(f(x))*val)
        label = '((phi**(x+0j))-((-1/phi)**(x+0j)))/(5**0.5)x{}'.format(val)
        legs.texts[0].set_text(label)
        return main_graph, 
    #Note that blit has been set to False, because axes elements are being modified
    animation = FuncAnimation(fig, func=g,frames=arange(0, 10, 0.1), init_func=init,interval=10,blit=False)
    animation.save('animation.gif', writer='imagemagick', fps=30)
    plt.show()
    

    Here's how the animation is: animation