@@ -204,7 +204,7 @@ impl<U: Time + 'static> Ss<U> {
204204 let is_valid = self . is_valid ( ) ;
205205 assert ! (
206206 is_valid. is_ok( ) ,
207- "Ss is invalid: Message: {}, Ss: {:? }" ,
207+ "Ss is invalid: Message: {}, Ss: {}" ,
208208 is_valid. unwrap_err( ) ,
209209 self
210210 ) ;
@@ -278,17 +278,45 @@ impl<U: Time + 'static> Ss<U> {
278278 ///
279279 /// # Returns
280280 /// A new system representing the series connection of `self` and `sys2`.
281- pub fn series ( self , sys2 : Self ) -> Self {
281+ pub fn series ( & self , sys2 : & Self ) -> Self {
282282 assert_eq ! (
283283 self . noutputs( ) ,
284284 sys2. ninputs( ) ,
285285 "Self output must be same as sys2 inputs"
286286 ) ;
287287 self . assert_valid ( ) ;
288288 sys2. assert_valid ( ) ;
289- let ret = sys2 * self ;
290- ret. assert_valid ( ) ;
291- ret
289+
290+ let nx2 = sys2. order ( ) ;
291+ let nu2 = sys2. ninputs ( ) ;
292+ let ny2 = sys2. noutputs ( ) ;
293+ let nx1 = self . order ( ) ;
294+ let nu1 = self . ninputs ( ) ;
295+ let ny1 = self . noutputs ( ) ;
296+ assert_eq ! ( nu2, ny1) ;
297+
298+ let mut a_new = DMatrix :: zeros ( nx1 + nx2, nx1 + nx2) ;
299+ a_new. view_mut ( ( 0 , 0 ) , ( nx1, nx1) ) . copy_from ( self . a ( ) ) ;
300+ a_new
301+ . view_mut ( ( nx1, 0 ) , ( nx2, nx1) )
302+ . copy_from ( & ( sys2. b ( ) * self . c ( ) ) ) ;
303+ a_new. view_mut ( ( nx1, nx1) , ( nx2, nx2) ) . copy_from ( sys2. a ( ) ) ;
304+
305+ let mut b_new = DMatrix :: zeros ( nx1 + nx2, nu1) ;
306+ b_new. view_mut ( ( 0 , 0 ) , ( nx1, nu1) ) . copy_from ( self . b ( ) ) ;
307+ b_new
308+ . view_mut ( ( nx1, 0 ) , ( nx2, nu1) )
309+ . copy_from ( & ( sys2. b ( ) * self . d ( ) ) ) ;
310+
311+ let mut c_new = DMatrix :: zeros ( ny2, nx1 + nx2) ;
312+ c_new
313+ . view_mut ( ( 0 , 0 ) , ( ny2, nx1) )
314+ . copy_from ( & ( sys2. d ( ) * self . c ( ) ) ) ;
315+ c_new. view_mut ( ( 0 , nx1) , ( ny2, nx2) ) . copy_from ( sys2. c ( ) ) ;
316+
317+ let d_new = sys2. d ( ) * self . d ( ) ;
318+
319+ Ss :: < U > :: new ( a_new, b_new, c_new, d_new) . unwrap ( )
292320 }
293321
294322 /// Connects two systems in **parallel**.
@@ -305,17 +333,36 @@ impl<U: Time + 'static> Ss<U> {
305333 ///
306334 /// # Returns
307335 /// A new system representing the parallel connection of `self` and `sys2`.
308- pub fn parallel ( self , sys2 : Self ) -> Self {
336+ pub fn parallel ( & self , sys2 : & Self ) -> Self {
309337 assert_eq ! (
310338 self . shape( ) ,
311339 sys2. shape( ) ,
312340 "System must have same shape to connnect in parallel"
313341 ) ;
314342 self . assert_valid ( ) ;
315343 sys2. assert_valid ( ) ;
316- let ret = self + sys2;
317- ret. assert_valid ( ) ;
318- ret
344+
345+ let nu = self . ninputs ( ) ;
346+ let ny = self . noutputs ( ) ;
347+
348+ let nx1 = self . order ( ) ;
349+ let nx2 = sys2. order ( ) ;
350+
351+ let mut a_new = DMatrix :: zeros ( nx1 + nx2, nx1 + nx2) ;
352+ a_new. view_mut ( ( 0 , 0 ) , ( nx1, nx1) ) . copy_from ( self . a ( ) ) ;
353+ a_new. view_mut ( ( nx1, nx1) , ( nx2, nx2) ) . copy_from ( sys2. a ( ) ) ;
354+
355+ let mut b_new = DMatrix :: zeros ( nx1 + nx2, nu) ;
356+ b_new. view_mut ( ( 0 , 0 ) , ( nx1, nu) ) . copy_from ( self . b ( ) ) ;
357+ b_new. view_mut ( ( nx1, 0 ) , ( nx2, nu) ) . copy_from ( sys2. b ( ) ) ;
358+
359+ let mut c_new = DMatrix :: zeros ( ny, nx1 + nx2) ;
360+ c_new. view_mut ( ( 0 , 0 ) , ( ny, nx1) ) . copy_from ( self . c ( ) ) ;
361+
362+ c_new. view_mut ( ( 0 , nx1) , ( ny, nx2) ) . copy_from ( sys2. c ( ) ) ;
363+ let d_new = self . d ( ) + sys2. d ( ) ;
364+
365+ Ss :: < U > :: new ( a_new, b_new, c_new, d_new) . unwrap ( )
319366 }
320367
321368 /// **Negative** Feedback connection of `self` with `sys2`.
@@ -414,7 +461,7 @@ impl<U: Time + 'static> Ss<U> {
414461 }
415462}
416463
417- impl < U : Time + ' static > Add for Ss < U > {
464+ impl < ' b , U : Time + ' static > Add < & ' b Ss < U > > for & Ss < U > {
418465 type Output = Ss < U > ;
419466
420467 /// Adds two state-space systems in parallel.
@@ -423,32 +470,26 @@ impl<U: Time + 'static> Add for Ss<U> {
423470 ///
424471 /// # Panics
425472 /// Panics if the input-output dimensions of both systems do not match.
426- fn add ( self , rhs : Self ) -> Self :: Output {
473+ fn add ( self , rhs : & ' b Ss < U > ) -> Self :: Output {
427474 assert_eq ! ( self . shape( ) , rhs. shape( ) ) ;
428475 self . assert_valid ( ) ;
429476 rhs. assert_valid ( ) ;
430477
431- let nu = self . ninputs ( ) ;
432- let ny = self . noutputs ( ) ;
433-
434- let nx1 = self . order ( ) ;
435- let nx2 = rhs. order ( ) ;
436-
437- let mut a_new = DMatrix :: zeros ( nx1 + nx2, nx1 + nx2) ;
438- a_new. view_mut ( ( 0 , 0 ) , ( nx1, nx1) ) . copy_from ( self . a ( ) ) ;
439- a_new. view_mut ( ( nx1, nx1) , ( nx2, nx2) ) . copy_from ( rhs. a ( ) ) ;
440-
441- let mut b_new = DMatrix :: zeros ( nx1 + nx2, nu) ;
442- b_new. view_mut ( ( 0 , 0 ) , ( nx1, nu) ) . copy_from ( self . b ( ) ) ;
443- b_new. view_mut ( ( nx1, 0 ) , ( nx2, nu) ) . copy_from ( rhs. b ( ) ) ;
444-
445- let mut c_new = DMatrix :: zeros ( ny, nx1 + nx2) ;
446- c_new. view_mut ( ( 0 , 0 ) , ( ny, nx1) ) . copy_from ( self . c ( ) ) ;
478+ self . parallel ( rhs)
479+ }
480+ }
447481
448- c_new . view_mut ( ( 0 , nx1 ) , ( ny , nx2 ) ) . copy_from ( rhs . c ( ) ) ;
449- let d_new = self . d ( ) + rhs . d ( ) ;
482+ impl < U : Time + ' static > Add for Ss < U > {
483+ type Output = Ss < U > ;
450484
451- Ss :: < U > :: new ( a_new, b_new, c_new, d_new) . unwrap ( )
485+ /// Adds two state-space systems in parallel.
486+ ///
487+ /// y = y1 + y2
488+ ///
489+ /// # Panics
490+ /// Panics if the input-output dimensions of both systems do not match.
491+ fn add ( self , rhs : Self ) -> Self :: Output {
492+ & self + & rhs
452493 }
453494}
454495
@@ -459,13 +500,26 @@ impl<U: Time + 'static> Neg for Ss<U> {
459500 ///
460501 /// I.e. y_new = -y
461502 fn neg ( mut self ) -> Self :: Output {
462- self . c = -self . c ( ) ;
463- self . d = -self . d ( ) ;
503+ self . c * = -1.0 ;
504+ self . d * = -1.0 ;
464505 self . assert_valid ( ) ;
465506 self
466507 }
467508}
468509
510+ impl < ' b , U : Time + ' static > Sub < & ' b Ss < U > > for & Ss < U > {
511+ type Output = Ss < U > ;
512+
513+ /// Subtracts two state-space systems by adding one and negating the other.
514+ fn sub ( self , rhs : & ' b Ss < U > ) -> Self :: Output {
515+ self . assert_valid ( ) ;
516+ rhs. assert_valid ( ) ;
517+
518+ // TODO: should not need to use clone
519+ self + & ( -rhs. clone ( ) )
520+ }
521+ }
522+
469523impl < U : Time + ' static > Sub for Ss < U > {
470524 type Output = Ss < U > ;
471525
@@ -477,7 +531,7 @@ impl<U: Time + 'static> Sub for Ss<U> {
477531 }
478532}
479533
480- impl < U : Time + ' static > Mul for Ss < U > {
534+ impl < ' b , U : Time + ' static > Mul < & ' b Ss < U > > for & Ss < U > {
481535 type Output = Ss < U > ;
482536
483537 /// Multiplies two state-space systems in series (cascading connection).
@@ -488,40 +542,49 @@ impl<U: Time + 'static> Mul for Ss<U> {
488542 /// # Panics
489543 /// Panics if the output size of `rhs` does not match the input size of
490544 /// `self`.
491- fn mul ( self , rhs : Self ) -> Self :: Output {
545+ fn mul ( self , rhs : & ' b Ss < U > ) -> Self :: Output {
492546 self . assert_valid ( ) ;
493547 rhs. assert_valid ( ) ;
494548
495- let nx2 = self . order ( ) ;
496- let nu2 = self . ninputs ( ) ;
497- let ny2 = self . noutputs ( ) ;
498- let nx1 = rhs. order ( ) ;
499- let nu1 = rhs. ninputs ( ) ;
500- let ny1 = rhs. noutputs ( ) ;
501- assert_eq ! ( nu2, ny1) ;
549+ rhs. series ( self )
550+ }
551+ }
502552
503- let mut a_new = DMatrix :: zeros ( nx1 + nx2, nx1 + nx2) ;
504- a_new. view_mut ( ( 0 , 0 ) , ( nx1, nx1) ) . copy_from ( rhs. a ( ) ) ;
505- a_new
506- . view_mut ( ( nx1, 0 ) , ( nx2, nx1) )
507- . copy_from ( & ( self . b ( ) * rhs. c ( ) ) ) ;
508- a_new. view_mut ( ( nx1, nx1) , ( nx2, nx2) ) . copy_from ( self . a ( ) ) ;
553+ impl < U : Time + ' static > Mul for Ss < U > {
554+ type Output = Ss < U > ;
509555
510- let mut b_new = DMatrix :: zeros ( nx1 + nx2, nu1) ;
511- b_new. view_mut ( ( 0 , 0 ) , ( nx1, nu1) ) . copy_from ( rhs. b ( ) ) ;
512- b_new
513- . view_mut ( ( nx1, 0 ) , ( nx2, nu1) )
514- . copy_from ( & ( self . b ( ) * rhs. d ( ) ) ) ;
556+ /// Multiplies two state-space systems in series (cascading connection).
557+ ///
558+ /// The resulting system represents `y <- self <- rhs <- u`, where `self`
559+ /// follows `rhs`.
560+ ///
561+ /// # Panics
562+ /// Panics if the output size of `rhs` does not match the input size of
563+ /// `self`.
564+ fn mul ( self , rhs : Self ) -> Self :: Output {
565+ & self * & rhs
566+ }
567+ }
515568
516- let mut c_new = DMatrix :: zeros ( ny2, nx1 + nx2) ;
517- c_new
518- . view_mut ( ( 0 , 0 ) , ( ny2, nx1) )
519- . copy_from ( & ( self . d ( ) * rhs. c ( ) ) ) ;
520- c_new. view_mut ( ( 0 , nx1) , ( ny2, nx2) ) . copy_from ( self . c ( ) ) ;
569+ impl < ' b , U : Time + ' static > Div < & ' b Ss < U > > for & Ss < U > {
570+ type Output = Ss < U > ;
521571
522- let d_new = self . d ( ) * rhs. d ( ) ;
572+ /// Divides one state-space system by another (right multiplication by
573+ /// inverse).
574+ ///
575+ /// # Warning
576+ /// The need for division often arrised due to feedback connections. Such as
577+ /// T = L/(1 + L). For such cases use the `feedback` function, which is more
578+ /// numerically stable.
579+ ///
580+ /// # Panics
581+ /// Panics if the inverse of `rhs` cannot be computed.
582+ #[ allow( clippy:: suspicious_arithmetic_impl) ]
583+ fn div ( self , rhs : & ' b Ss < U > ) -> Self :: Output {
584+ self . assert_valid ( ) ;
585+ rhs. assert_valid ( ) ;
523586
524- Ss :: < U > :: new ( a_new , b_new , c_new , d_new ) . unwrap ( )
587+ self * & rhs . inv ( ) . unwrap ( )
525588 }
526589}
527590
@@ -540,9 +603,7 @@ impl<U: Time + 'static> Div for Ss<U> {
540603 /// Panics if the inverse of `rhs` cannot be computed.
541604 #[ allow( clippy:: suspicious_arithmetic_impl) ]
542605 fn div ( self , rhs : Self ) -> Self :: Output {
543- self . assert_valid ( ) ;
544- rhs. assert_valid ( ) ;
545- self * rhs. inv ( ) . unwrap ( )
606+ & self / & rhs
546607 }
547608}
548609
@@ -557,6 +618,14 @@ macro_rules! impl_compound_assign {
557618 * self = self . clone( ) . $method( rhs)
558619 }
559620 }
621+
622+ impl <' a, U : Time + ' static > $assign_trait<& ' a $struct_type<U >> for $struct_type<U >
623+ {
624+ fn $assign_method( & mut self , rhs: & ' a $struct_type<U >) {
625+ * self = <& Self as $trait<& Self >>:: $method( self , rhs) ;
626+ }
627+ }
628+
560629 ) *
561630 } ;
562631}
@@ -621,14 +690,15 @@ mod tests {
621690 use core:: f64;
622691
623692 use approx:: assert_abs_diff_eq;
693+ use num_complex:: c64;
624694 use rand:: Rng ;
625695
626696 use super :: * ;
627697 use crate :: {
628698 analysis:: frequency_response:: lin_space,
629699 systems:: Tf ,
630700 transformations:: SsRealization :: { ControllableCF , ObservableCF } ,
631- utils:: traits:: Continuous ,
701+ utils:: traits:: Continuous , FrequencyResponse ,
632702 } ;
633703
634704 fn rand_proper_tf < U : Rng > (
@@ -854,11 +924,11 @@ mod tests {
854924 assert_abs_diff_eq ! ( tf_fb, 0.5 * Tf :: s( ) / ( Tf :: s( ) + 0.5 ) ) ;
855925
856926 let ss_add = ss1. clone ( ) + ss2. clone ( ) ;
857- let ss_parallel = ss1. clone ( ) . parallel ( ss2. clone ( ) ) ;
927+ let ss_parallel = ss1. parallel ( & ss2) ;
858928 assert_eq ! ( ss_add, ss_parallel) ;
859929
860930 let ss_mul = ss2. clone ( ) * ss1. clone ( ) ;
861- let ss_series = ss1. series ( ss2) ;
931+ let ss_series = ss1. series ( & ss2) ;
862932 assert_eq ! ( ss_mul, ss_series) ;
863933 }
864934
@@ -875,7 +945,7 @@ mod tests {
875945
876946 let ss_fb = ss1. feedback ( ss2) ;
877947 let ss_fb = ss_fb. to_tf ( ) . unwrap ( ) ;
878- let tf_fb = tf1. feedback ( tf2) ;
948+ let tf_fb = tf1. feedback ( & tf2) ;
879949
880950 assert_abs_diff_eq ! ( tf_fb, ss_fb, epsilon = 1e-1 ) ;
881951 }
@@ -894,4 +964,22 @@ mod tests {
894964 . unwrap ( ) ;
895965 println ! ( "3: {sys}after" ) ;
896966 }
967+
968+ #[ test]
969+ fn ss_reference_arithmetic ( ) {
970+ let mut ss = ( 1.0 / Tf :: s ( ) . powi ( 2 ) ) . to_ss ( ) . unwrap ( ) ;
971+ let ss_org = ss. clone ( ) ;
972+
973+ ss += & ss_org;
974+ ss += & ss_org;
975+
976+ let ans = ( ss_org. clone ( ) + ss_org. clone ( ) ) + ss_org. clone ( ) ;
977+
978+ let freq = c64 ( 0.0 , 1.2 ) ;
979+ let resp1 = ss. freq_response ( & freq) ;
980+ let resp2 = ans. freq_response ( & freq) ;
981+
982+ assert_abs_diff_eq ! ( resp1. re, resp2. re, epsilon = 1e-9 ) ;
983+ assert_abs_diff_eq ! ( resp1. im, resp2. im, epsilon = 1e-9 ) ;
984+ }
897985}
0 commit comments