# A simple example demonstrating real pole placement using both KNV and YT
# algorithms.  This is example number 1 from section 4 of the reference KNV
# publication ([R258]_):

from scipy import signal
import matplotlib.pyplot as plt

A = np.array([[ 1.380,  -0.2077,  6.715, -5.676  ],
              [-0.5814, -4.290,   0,      0.6750 ],
              [ 1.067,   4.273,  -6.654,  5.893  ],
              [ 0.0480,  4.273,   1.343, -2.104  ]])
B = np.array([[ 0,      5.679 ],
              [ 1.136,  1.136 ],
              [ 0,      0,    ],
              [-3.146,  0     ]])
P = np.array([-0.2, -0.5, -5.0566, -8.6659])

# Now compute K with KNV method 0, with the default YT method and with the YT
# method while forcing 100 iterations of the algorithm and print some results
# after each call.

fsf1 = signal.place_poles(A, B, P, method='KNV0')
fsf1.gain_matrix
# array([[ 0.20071427, -0.96665799,  0.24066128, -0.10279785],
# [ 0.50587268,  0.57779091,  0.51795763, -0.41991442]])

fsf2 = signal.place_poles(A, B, P)  # uses YT method
fsf2.computed_poles
# array([-8.6659, -5.0566, -0.5   , -0.2   ])

fsf3 = signal.place_poles(A, B, P, rtol=-1, maxiter=100)
fsf3.X
# array([[ 0.52072442+0.j, -0.08409372+0.j, -0.56847937+0.j,  0.74823657+0.j],
# [-0.04977751+0.j, -0.80872954+0.j,  0.13566234+0.j, -0.29322906+0.j],
# [-0.82266932+0.j, -0.19168026+0.j, -0.56348322+0.j, -0.43815060+0.j],
# [ 0.22267347+0.j,  0.54967577+0.j, -0.58387806+0.j, -0.40271926+0.j]])

# The absolute value of the determinant of X is a good indicator to check the
# robustness of the results, both ``'KNV0'`` and ``'YT'`` aim at maximizing
# it.  Below a comparison of the robustness of the results above:

abs(np.linalg.det(fsf1.X)) < abs(np.linalg.det(fsf2.X))
# True
abs(np.linalg.det(fsf2.X)) < abs(np.linalg.det(fsf3.X))
# True

# Now a simple example for complex poles:

A = np.array([[ 0,  7/3.,  0,   0   ],
              [ 0,   0,    0,  7/9. ],
              [ 0,   0,    0,   0   ],
              [ 0,   0,    0,   0   ]])
B = np.array([[ 0,  0 ],
              [ 0,  0 ],
              [ 1,  0 ],
              [ 0,  1 ]])
P = np.array([-3, -1, -2-1j, -2+1j]) / 3.
fsf = signal.place_poles(A, B, P, method='YT')

# We can plot the desired and computed poles in the complex plane:

t = np.linspace(0, 2*np.pi, 401)
plt.plot(np.cos(t), np.sin(t), 'k--')  # unit circle
plt.plot(fsf.requested_poles.real, fsf.requested_poles.imag,
         'wo', label='Desired')
plt.plot(fsf.computed_poles.real, fsf.computed_poles.imag, 'bx',
         label='Placed')
plt.grid()
plt.axis('image')
plt.axis([-1.1, 1.1, -1.1, 1.1])
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, numpoints=1)
