@@ -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