Repository: python-control/python-control

Issue:
rlocus evaluation for a single gain value
I recall in the past being able to evaluate rlocus for a single gain to obtain the closed-loop poles (as well as plotting), along the lines of:

L = tf([1,1],[1,2,3])
rr = rlocus(L, gains=np.array([1]))

but this fails giving the final message: 
.../site-packages/control/pzmap.py:124
ValueError: zero-size array to reduction operation minimum which has no identity

While this does work:
rr = rlocus(L, gains=np.array([1,1]))
allowing for an evaluation at a single gain would be nice.

Discussion:

Solution patch:
diff --git a/control/phaseplot.py b/control/phaseplot.py
index 6f7ed355f..e6123bc0e 100644
--- a/control/phaseplot.py
+++ b/control/phaseplot.py
@@ -532,8 +532,7 @@ def equilpoints(
     out = []
     for xeq in equilpts:
         with plt.rc_context(rcParams):
-            out.append(
-                ax.plot(xeq[0], xeq[1], marker='o', color=color))
+            out += ax.plot(xeq[0], xeq[1], marker='o', color=color)
     return out
 
 
@@ -649,8 +648,7 @@ def separatrices(
     for i, xeq in enumerate(equilpts):
         # Plot the equilibrium points
         with plt.rc_context(rcParams):
-            out.append(
-                ax.plot(xeq[0], xeq[1], marker='o', color='k'))
+            out += ax.plot(xeq[0], xeq[1], marker='o', color='k')
 
         # Figure out the linearization and eigenvectors
         evals, evecs = np.linalg.eig(sys.linearize(xeq, 0, params=params).A)
@@ -691,13 +689,13 @@ def separatrices(
                 # Plot the trajectory (if there is one)
                 if traj.shape[1] > 1:
                     with plt.rc_context(rcParams):
-                        out.append(ax.plot(
-                            traj[0], traj[1], color=color, linestyle=linestyle))
+                        out += ax.plot(
+                            traj[0], traj[1], color=color, linestyle=linestyle)
 
                     # Add arrows to the lines at specified intervals
                     with plt.rc_context(rcParams):
                         _add_arrows_to_line2D(
-                            ax, out[-1][0], arrow_pos, arrowstyle=arrow_style,
+                            ax, out[-1], arrow_pos, arrowstyle=arrow_style,
                             dir=1)
     return out
 
diff --git a/control/pzmap.py b/control/pzmap.py
index 7b0f8d096..400db2807 100644
--- a/control/pzmap.py
+++ b/control/pzmap.py
@@ -607,14 +607,16 @@ def _compute_root_locus_limits(response):
         # Find the local maxima of root locus curve
         xpeaks = np.where(
             np.diff(np.abs(locus.real)) < 0, locus.real[0:-1], 0)
-        xlim = [
-            min(xlim[0], np.min(xpeaks) * rho),
-            max(xlim[1], np.max(xpeaks) * rho)
-        ]
+        if xpeaks.size > 0:
+            xlim = [
+                min(xlim[0], np.min(xpeaks) * rho),
+                max(xlim[1], np.max(xpeaks) * rho)
+            ]
 
         ypeaks = np.where(
             np.diff(np.abs(locus.imag)) < 0, locus.imag[0:-1], 0)
-        ylim = max(ylim, np.max(ypeaks) * rho)
+        if ypeaks.size > 0:
+            ylim = max(ylim, np.max(ypeaks) * rho)
 
     if isctime(dt=response.dt):
         # Adjust the limits to include some space around features
diff --git a/control/rlocus.py b/control/rlocus.py
index 189c2ccd0..54c672997 100644
--- a/control/rlocus.py
+++ b/control/rlocus.py
@@ -56,10 +56,9 @@ def root_locus_map(sysdata, gains=None):
     Returns
     -------
     rldata : PoleZeroData or list of PoleZeroData
-        Root locus data object(s) corresponding to the .  The loci of
-        the root locus diagram are available in the array
-        `rldata.loci`, indexed by the gain index and the locus index,
-        and the gains are in the array `rldata.gains`.
+        Root locus data object(s).  The loci of the root locus diagram are
+        available in the array `rldata.loci`, indexed by the gain index and
+        the locus index, and the gains are in the array `rldata.gains`.
 
     Notes
     -----


Test patch:
diff --git a/control/tests/rlocus_test.py b/control/tests/rlocus_test.py
index a62bc742b..52546a428 100644
--- a/control/tests/rlocus_test.py
+++ b/control/tests/rlocus_test.py
@@ -287,3 +287,18 @@ def test_root_locus_documentation(savefigs=False):
 
     # Run tests that generate plots for the documentation
     test_root_locus_documentation(savefigs=True)
+
+
+# https://github.com/python-control/python-control/issues/1063
+def test_rlocus_singleton():
+    # Generate a root locus map for a singleton
+    L = ct.tf([1, 1], [1, 2, 3])
+    rldata = ct.root_locus_map(L, 1)
+    np.testing.assert_equal(rldata.gains, np.array([1]))
+    assert rldata.loci.shape == (1, 2)
+
+    # Generate the root locus plot (no loci)
+    cplt = rldata.plot()
+    assert len(cplt.lines[0, 0]) == 1      # poles (one set of markers)
+    assert len(cplt.lines[0, 1]) == 1      # zeros
+    assert len(cplt.lines[0, 2]) == 2      # loci (two 0-length lines)