@@ -368,38 +368,104 @@ def minreal(sys, tol=None, verbose=True):
368368 return sysr
369369
370370
371- def era (YY , m , n , nin , nout , r ):
372- """Calculate an ERA model of order `r` based on the impulse-response data
373- `YY`.
371+ def era (data , r , m = None , n = None , dt = True ):
372+ """Calculate an ERA model of order `r` based on the impulse-response data.
374373
375- .. note:: This function is not implemented yet.
374+ This function computes a discrete time system
375+
376+ .. math::
377+
378+ x[k+1] &= A x[k] + B u[k] \\ \\
379+ y[k] &= C x[k] + D u[k]
380+
381+ for a given impulse-response data (see [1]_).
376382
377383 Parameters
378384 ----------
379- YY: array
380- `nout` x `nin` dimensional impulse-response data
381- m: integer
382- Number of rows in Hankel matrix
383- n: integer
384- Number of columns in Hankel matrix
385- nin: integer
386- Number of input variables
387- nout: integer
388- Number of output variables
389- r: integer
390- Order of model
385+ data : TimeResponseData
386+ impulse-response data from which the StateSpace model is estimated.
387+ r : integer
388+ Order of model.
389+ m : integer, optional
390+ Number of rows in Hankel matrix.
391+ Default is 2*r.
392+ n : integer, optional
393+ Number of columns in Hankel matrix.
394+ Default is 2*r.
395+ dt : True or float, optional
396+ True indicates discrete time with unspecified sampling time,
397+ positive number is discrete time with specified sampling time.
398+ It can be used to scale the StateSpace model in order to match
399+ the impulse response of this library.
400+ Default values is True.
391401
392402 Returns
393403 -------
394- sys: StateSpace
395- A reduced order model sys=ss(Ar,Br,Cr,Dr)
404+ sys : StateSpace
405+ A reduced order model sys=StateSpace(Ar,Br,Cr,Dr,dt)
406+ S : array
407+ Singular values of Hankel matrix.
408+ Can be used to choose a good r value.
409+
410+ References
411+ ----------
412+ .. [1] Samet Oymak and Necmiye Ozay
413+ Non-asymptotic Identification of LTI Systems
414+ from a Single Trajectory.
415+ https://arxiv.org/abs/1806.05722
396416
397417 Examples
398418 --------
399- >>> rsys = era(YY, m, n, nin, nout, r) # doctest: +SKIP
400-
419+ >>> T = np.linspace(0, 10, 100)
420+ >>> response = ct.impulse_response(ct.tf([1], [1, 0.5], True), T)
421+ >>> sysd, _ = ct.era(response, r=1)
401422 """
402- raise NotImplementedError ('This function is not implemented yet.' )
423+ def block_hankel_matrix (Y , m , n ):
424+
425+ q , p , _ = Y .shape
426+ YY = Y .transpose (0 ,2 ,1 ) # transpose for reshape
427+
428+ H = np .zeros ((q * m ,p * n ))
429+
430+ for r in range (m ):
431+ # shift and add row to hankel matrix
432+ new_row = YY [:,r :r + n ,:]
433+ H [q * r :q * (r + 1 ),:] = new_row .reshape ((q ,p * n ))
434+
435+ return H
436+
437+ Y = np .array (data .outputs , ndmin = 3 )
438+ if data .transpose :
439+ Y = np .transpose (Y )
440+ q , p , l = Y .shape
441+
442+ if m is None :
443+ m = 2 * r
444+ if n is None :
445+ n = 2 * r
446+
447+ if m * q < r or n * p < r :
448+ raise ValueError ("Hankel parameters are to small" )
449+
450+ if (l - 1 ) < m + n :
451+ raise ValueError ("Not enough data for requested number of parameters" )
452+
453+ H = block_hankel_matrix (Y [:,:,1 :], m , n + 1 ) # Hankel matrix (q*m, p*(n+1))
454+ Hf = H [:,:- p ] # first p*n columns of H
455+ Hl = H [:,p :] # last p*n columns of H
456+
457+ U ,S ,Vh = np .linalg .svd (Hf , True )
458+ Ur = U [:,0 :r ]
459+ Vhr = Vh [0 :r ,:]
460+
461+ # balanced realizations
462+ Sigma_inv = np .diag (1. / np .sqrt (S [0 :r ]))
463+ Ar = Sigma_inv @ Ur .T @ Hl @ Vhr .T @ Sigma_inv
464+ Br = Sigma_inv @ Ur .T @ Hf [:,0 :p ]* dt
465+ Cr = Hf [0 :q ,:] @ Vhr .T @ Sigma_inv
466+ Dr = Y [:,:,0 ]
467+
468+ return StateSpace (Ar ,Br ,Cr ,Dr ,dt ), S
403469
404470
405471def markov (Y , U , m = None , transpose = False ):
0 commit comments