v1_np = np.array([1, 3], dtype=float)
v2_np = np.array([2, 1], dtype=float)
t_fwd = np.linspace(0, 5, 400)
t_bwd = np.linspace(0, -3, 400)
fig, ax = plt.subplots(figsize=(6, 6))
ax.axhline(0, color='gray', lw=0.7)
ax.axvline(0, color='gray', lw=0.7)
# Several orbits: vary (c1, c2)
for c1v, c2v in [(1, 0), (-1, 0), (0, 1), (0, -1),
(1, 1), (1, -1), (-1, 1), (-1, -1),
(2, 1), (-2, 1)]:
for t_range in [t_fwd, t_bwd]:
xc = c1v * v1_np[0] * np.exp(-1*t_range) + c2v * v2_np[0] * np.exp(-4*t_range)
yc = c1v * v1_np[1] * np.exp(-1*t_range) + c2v * v2_np[1] * np.exp(-4*t_range)
# Highlight pure eigenvector orbits
if c2v == 0 and c1v != 0:
ax.plot(xc, yc, color='tomato', lw=2.0, alpha=0.9)
elif c1v == 0 and c2v != 0:
ax.plot(xc, yc, color='steelblue', lw=2.0, alpha=0.9)
else:
ax.plot(xc, yc, color='gray', lw=1.0, alpha=0.5)
# Eigenvector direction arrows
ax.annotate('', xy=1.5*v1_np, xytext=np.zeros(2),
arrowprops=dict(arrowstyle='->', color='tomato', lw=1.8))
ax.annotate('', xy=-1.5*v1_np, xytext=np.zeros(2),
arrowprops=dict(arrowstyle='->', color='tomato', lw=1.8))
ax.annotate('', xy=1.5*v2_np, xytext=np.zeros(2),
arrowprops=dict(arrowstyle='->', color='steelblue', lw=1.8))
ax.annotate('', xy=-1.5*v2_np, xytext=np.zeros(2),
arrowprops=dict(arrowstyle='->', color='steelblue', lw=1.8))
ax.plot(0, 0, 'ko', ms=6, zorder=5)
ax.set_xlim(-4, 4)
ax.set_ylim(-5, 5)
ax.set_aspect('equal')
ax.set_xlabel(r'$x$', fontsize=13)
ax.set_ylabel(r'$y$', fontsize=13)
ax.set_title('Asymptotically stable node: '
r'$\lambda_1=-1,\;\lambda_2=-4$', fontsize=11)
from matplotlib.lines import Line2D
legend_handles = [
Line2D([0],[0], color='tomato', lw=2, label=r'Linear orbit along $\mathbf{v}_1$'),
Line2D([0],[0], color='steelblue', lw=2, label=r'Linear orbit along $\mathbf{v}_2$'),
Line2D([0],[0], color='gray', lw=1, label='Other orbits'),
]
ax.legend(handles=legend_handles, fontsize=9, loc='upper right')
plt.tight_layout()
plt.show()