.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "api/gallery/control_systems/stability/plot_04_hurwitz_determinants.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 or to run this example in your browser via JupyterLite. .. rst-class:: sphx-glr-example-title .. _sphx_glr_api_gallery_control_systems_stability_plot_04_hurwitz_determinants.py: Hurwitz's determinant test ========================== Aurel Stodola, designing turbine regulators in Zürich, asked Adolf Hurwitz for a practical stability condition. Hurwitz (1895), unaware of Routh's work, gave it as determinants: build the matrix :math:`H_{ij} = a_{2j-i}` from the coefficients, and every root lies in the left half-plane exactly when all leading minors are positive (:func:`~engineeringkit.control_systems.hurwitz_determinants`). At the boundary :math:`\Delta_{n-1}` passes through zero as a pair of roots crosses the imaginary axis. .. GENERATED FROM PYTHON SOURCE LINES 16-21 .. code-block:: Python import matplotlib.pyplot as plt import numpy as np from engineeringkit.control_systems import hurwitz_determinants .. GENERATED FROM PYTHON SOURCE LINES 22-27 A quartic family ---------------- The roots of :math:`(s+1)^4 + K` are :math:`-1 + K^{1/4}e^{i(\pi/4 + k\pi/2)}`, so the rightmost pair reaches the imaginary axis at :math:`K = 4`. .. GENERATED FROM PYTHON SOURCE LINES 27-50 .. code-block:: Python gains = np.linspace(0.1, 8, 400) deltas = np.array([hurwitz_determinants(np.poly([-1, -1, -1, -1]) + [0, 0, 0, 0, K]) for K in gains]) rightmost = np.array([np.roots(np.poly([-1, -1, -1, -1]) + [0, 0, 0, 0, K]).real.max() for K in gains]) stable_by_hurwitz = np.all(deltas > 0, axis=1) assert np.array_equal(stable_by_hurwitz, rightmost < 0) boundary = gains[np.argmin(np.abs(deltas[:, 2]))] print(f"Delta_3 vanishes near K = {boundary:.3f} (exact 4)") assert abs(boundary - 4) < 0.02 fig, axes = plt.subplots(1, 2, figsize=(10, 3.8)) for k in range(4): axes[0].plot(gains, deltas[:, k], label=rf"$\Delta_{k + 1}$") axes[0].axhline(0, color="k", lw=0.8) axes[0].axvline(4, color="C3", ls="--", lw=0.8) axes[0].set(xlabel="$K$", ylabel="Hurwitz minor", ylim=(-60, 120)) axes[0].legend() axes[1].plot(gains, rightmost) axes[1].axhline(0, color="k", lw=0.8) axes[1].set(xlabel="$K$", ylabel="largest real part of a root") fig.tight_layout() plt.show() .. image-sg:: /api/gallery/control_systems/stability/images/sphx_glr_plot_04_hurwitz_determinants_001.png :alt: plot 04 hurwitz determinants :srcset: /api/gallery/control_systems/stability/images/sphx_glr_plot_04_hurwitz_determinants_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none Delta_3 vanishes near K = 4.001 (exact 4) .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.263 seconds) .. _sphx_glr_download_api_gallery_control_systems_stability_plot_04_hurwitz_determinants.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: lite-badge .. image:: images/jupyterlite_badge_logo.svg :target: ../../../../lite/lab/index.html?path=api/gallery/control_systems/stability/plot_04_hurwitz_determinants.ipynb :alt: Launch JupyterLite :width: 150 px .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_04_hurwitz_determinants.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_04_hurwitz_determinants.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_04_hurwitz_determinants.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_