.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "api/gallery/ode_dynamics/stability/plot_02_lyapunov_function.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_api_gallery_ode_dynamics_stability_plot_02_lyapunov_function.py: Lyapunov's direct method ============================== For a stable linear system :math:`\dot x = Ax`, solving the Lyapunov equation :math:`A^T P + PA = -Q` gives an energy-like function :math:`V(x) = x^T P x` whose level sets are ellipses that every trajectory crosses inward -- a proof of stability that never solves the ODE itself. .. GENERATED FROM PYTHON SOURCE LINES 13-24 .. code-block:: Python import matplotlib.pyplot as plt import numpy as np from mathematicskit.ode_dynamics.systems.phase_portrait import Linear2D from mathematicskit.ode_dynamics.systems.stability import lyapunov_quadratic_form A = np.array([[-0.3, 2.0], [-1.0, -0.3]]) res = lyapunov_quadratic_form(A) print("P =\n", np.round(res.P, 4)) print("P positive definite (origin asymptotically stable):", res.positive_definite) .. rst-class:: sphx-glr-script-out .. code-block:: none P = [[1.2679 0.1196] [0.1196 2.4641]] P positive definite (origin asymptotically stable): True .. GENERATED FROM PYTHON SOURCE LINES 25-27 Trajectories cross the level sets of V inward --------------------------------------------------- .. GENERATED FROM PYTHON SOURCE LINES 27-45 .. code-block:: Python xs = np.linspace(-2.5, 2.5, 200) X, Y = np.meshgrid(xs, xs) V = res.P[0, 0] * X**2 + 2 * res.P[0, 1] * X * Y + res.P[1, 1] * Y**2 traj = Linear2D([2.0, 1.0], A=A).integrate((0.0, 20.0), dt=1e-2, method="rk4") V_traj = np.einsum("ti,ij,tj->t", traj.y, res.P, traj.y) fig, axes = plt.subplots(1, 2, figsize=(11, 4.5)) axes[0].contour(X, Y, V, levels=12, cmap="viridis") axes[0].plot(traj.y[:, 0], traj.y[:, 1], "k", lw=1) axes[0].set_aspect("equal") axes[0].set_title("Level sets of V(x) = x^T P x and a trajectory") axes[1].semilogy(traj.t, V_traj) axes[1].set_xlabel("t") axes[1].set_ylabel("V(x(t))") axes[1].set_title("V decreases monotonically along the flow") fig.tight_layout() .. image-sg:: /api/gallery/ode_dynamics/stability/images/sphx_glr_plot_02_lyapunov_function_001.png :alt: Level sets of V(x) = x^T P x and a trajectory, V decreases monotonically along the flow :srcset: /api/gallery/ode_dynamics/stability/images/sphx_glr_plot_02_lyapunov_function_001.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 46-48 An unstable matrix admits no positive-definite P ------------------------------------------------------- .. GENERATED FROM PYTHON SOURCE LINES 48-52 .. code-block:: Python print("unstable spiral, P positive definite:", lyapunov_quadratic_form([[0.1, 1.0], [-1.0, 0.1]]).positive_definite) plt.show() .. rst-class:: sphx-glr-script-out .. code-block:: none unstable spiral, P positive definite: False .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.330 seconds) .. _sphx_glr_download_api_gallery_ode_dynamics_stability_plot_02_lyapunov_function.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_02_lyapunov_function.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_02_lyapunov_function.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_02_lyapunov_function.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_