Skip to content

Commit c102929

Browse files
authored
added xperm function: reorder state variables in a ss model.
1 parent 373ff11 commit c102929

1 file changed

Lines changed: 107 additions & 0 deletions

File tree

control/statesp.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2142,6 +2142,113 @@ def _parse_list(signals, signame='input', prefix='u'):
21422142
return StateSpace(
21432143
ss_sys, inputs=input_names, outputs=output_names, name=name)
21442144

2145+
# Reorder states in state-space object
2146+
def xperm(sys, P):
2147+
r"""Reorder states in a state-space representation.
2148+
2149+
``xperm(sys,P)``
2150+
Reorder a space system. Always creates a new system.
2151+
2152+
Parameters
2153+
----------
2154+
sys : StateSpace or TransferFunction
2155+
A linear system.
2156+
A, B, C, D : array_like or string
2157+
System, control, output, and feed forward matrices.
2158+
P : permutation vector P.
2159+
It is a 0:N-1 where N-1 is the number of states in sys.
2160+
2161+
Returns
2162+
-------
2163+
out: :class:`StateSpace`
2164+
Linear input/output system.
2165+
2166+
Raises
2167+
------
2168+
ValueError
2169+
ValueError: If matrix sizes are not self-consistent or if the order
2170+
is invalid.
2171+
2172+
Example
2173+
-------
2174+
2175+
Reorder a ss model:
2176+
>>> G = ct.ss([[-1, -2], [3, -4]], [[5], [7]], [[6, 8]], [[9]])
2177+
>>> ct.xperm(G,[1, 0, 2])
2178+
2179+
"""
2180+
# TODO: transfer the original sys parameters to the new output sys to preserve labels.
2181+
# TODO: create docstrings for this function - WIP
2182+
2183+
# Convert `order` to numpy array if it's a list
2184+
P = np.array(P)
2185+
2186+
# Get order of A
2187+
n = sys.A.shape[0]
2188+
_sorted_P = np.sort(P)
2189+
_sorted_sys_labels = np.sort(sys.state_labels)
2190+
2191+
# Check if inputs are state labels from sys
2192+
if np.array_equal(_sorted_sys_labels, _sorted_P):
2193+
order_contains_labels = True
2194+
elif np.array_equal(np.arange(n), _sorted_P):
2195+
order_contains_labels = False
2196+
else:
2197+
raise ValueError('Invalid P array. P must be a list of unique integers from 0 to N-1, or a list of state labels of sys.')
2198+
2199+
# Check dimensions of matrices
2200+
if sys.A.shape[0] != sys.A.shape[1]:
2201+
raise ValueError("Matrix A must be square.")
2202+
if sys.B.shape[0] != n:
2203+
raise ValueError("Matrix B must have the same number of rows as A.")
2204+
if sys.C.shape[1] != n:
2205+
raise ValueError("Matrix C must have the same number of columns as A.")
2206+
if sys.D.shape[0] != sys.C.shape[0] or sys.D.shape[1] != sys.B.shape[1]:
2207+
raise ValueError("Matrix D dimensions must be consistent with matrices C and B.")
2208+
2209+
# if P contains labels, find their order
2210+
if order_contains_labels:
2211+
# list1 is the reference, we want indices of list2 elements in list1
2212+
P = np.searchsorted(sys.state_labels, P)
2213+
2214+
# Construct the permutation matrix T
2215+
T = np.zeros((n, n))
2216+
for i, j in enumerate(P):
2217+
T[i, j] = 1
2218+
2219+
P_inv = np.linalg.inv(T)
2220+
2221+
# Apply the transformation
2222+
A_perm = T @ sys.A @ P_inv
2223+
B_perm = T @ sys.B
2224+
C_perm = sys.C @ P_inv
2225+
D_perm = sys.D # D remains unchanged
2226+
2227+
return ss(A_perm, B_perm, C_perm, D_perm)
2228+
"""
2229+
Example usage
2230+
>>> A = np.array([[-10., -24., 0.],
2231+
[ 1., 0., 0.],
2232+
[ 0., 1., 0.]])
2233+
2234+
>>> B = np.array([[1],[0],[0]])
2235+
>>> C = np.array([[0, 1, 0]])
2236+
>>> D = np.array([[0]])
2237+
>>> sys = ct.ss(A,B,C,D)
2238+
2239+
# Desired order of state variables (e.g., swap x1 and x3):
2240+
>>> P = [2, 1, 0] # permutations: x3->x1, x2->x2, x1->x3.
2241+
2242+
try:
2243+
sys_reordered = xperm(sys, P)
2244+
print("A' =\n", sys_reordered.A)
2245+
print("B' =\n", sys_reordered.B)
2246+
print("C' =\n", sys_reordered.C)
2247+
print("D' =\n", sys_reordered.D)
2248+
except ValueError as e:
2249+
print("Error:", e)
2250+
"""
2251+
21452252
#
21462253
# Utility functions
21472254
#

0 commit comments

Comments
 (0)