@@ -126,7 +126,7 @@ class for a set of subclasses that are used to implement specific
126126 # Allow ndarray * InputOutputSystem to give IOSystem._rmul_() priority
127127 __array_priority__ = 12 # override ndarray, matrix, SS types
128128
129- def __init__ (self , params = {} , ** kwargs ):
129+ def __init__ (self , params = None , ** kwargs ):
130130 """Create an input/output system.
131131
132132 The InputOutputSystem constructor is used to create an input/output
@@ -148,7 +148,7 @@ def __init__(self, params={}, **kwargs):
148148 states = states , name = name , dt = dt )
149149
150150 # default parameters
151- self .params = params .copy ()
151+ self .params = {} if params is None else params .copy ()
152152
153153 def __mul__ (sys2 , sys1 ):
154154 """Multiply two input/output systems (series interconnection)"""
@@ -357,7 +357,7 @@ def _update_params(self, params, warning=False):
357357 if warning :
358358 warn ("Parameters passed to InputOutputSystem ignored." )
359359
360- def _rhs (self , t , x , u , params = {} ):
360+ def _rhs (self , t , x , u ):
361361 """Evaluate right hand side of a differential or difference equation.
362362
363363 Private function used to compute the right hand side of an
@@ -369,23 +369,24 @@ def _rhs(self, t, x, u, params={}):
369369 NotImplemented ("Evaluation not implemented for system of type " ,
370370 type (self ))
371371
372- def dynamics (self , t , x , u ):
372+ def dynamics (self , t , x , u , params = None ):
373373 """Compute the dynamics of a differential or difference equation.
374374
375375 Given time `t`, input `u` and state `x`, returns the value of the
376376 right hand side of the dynamical system. If the system is continuous,
377377 returns the time derivative
378378
379- dx/dt = f(t, x, u)
379+ dx/dt = f(t, x, u[, params] )
380380
381381 where `f` is the system's (possibly nonlinear) dynamics function.
382382 If the system is discrete-time, returns the next value of `x`:
383383
384- x[t+dt] = f(t, x[t], u[t])
384+ x[t+dt] = f(t, x[t], u[t][, params] )
385385
386- Where `t` is a scalar.
386+ where `t` is a scalar.
387387
388- The inputs `x` and `u` must be of the correct length.
388+ The inputs `x` and `u` must be of the correct length. The `params`
389+ argument is an optional dictionary of parameter values.
389390
390391 Parameters
391392 ----------
@@ -395,14 +396,17 @@ def dynamics(self, t, x, u):
395396 current state
396397 u : array_like
397398 input
399+ params : dict (optional)
400+ system parameter values
398401
399402 Returns
400403 -------
401404 dx/dt or x[t+dt] : ndarray
402405 """
406+ self ._update_params (params )
403407 return self ._rhs (t , x , u )
404408
405- def _out (self , t , x , u , params = {} ):
409+ def _out (self , t , x , u ):
406410 """Evaluate the output of a system at a given state, input, and time
407411
408412 Private function used to compute the output of of an input/output
@@ -414,13 +418,13 @@ def _out(self, t, x, u, params={}):
414418 # If no output function was defined in subclass, return state
415419 return x
416420
417- def output (self , t , x , u ):
421+ def output (self , t , x , u , params = None ):
418422 """Compute the output of the system
419423
420424 Given time `t`, input `u` and state `x`, returns the output of the
421425 system:
422426
423- y = g(t, x, u)
427+ y = g(t, x, u[, params] )
424428
425429 The inputs `x` and `u` must be of the correct length.
426430
@@ -432,14 +436,17 @@ def output(self, t, x, u):
432436 current state
433437 u : array_like
434438 input
439+ params : dict (optional)
440+ system parameter values
435441
436442 Returns
437443 -------
438444 y : ndarray
439445 """
446+ self ._update_params (params )
440447 return self ._out (t , x , u )
441448
442- def feedback (self , other = 1 , sign = - 1 , params = {} ):
449+ def feedback (self , other = 1 , sign = - 1 , params = None ):
443450 """Feedback interconnection between two input/output systems
444451
445452 Parameters
@@ -507,7 +514,7 @@ def feedback(self, other=1, sign=-1, params={}):
507514 # Return the newly created system
508515 return newsys
509516
510- def linearize (self , x0 , u0 , t = 0 , params = {} , eps = 1e-6 ,
517+ def linearize (self , x0 , u0 , t = 0 , params = None , eps = 1e-6 ,
511518 name = None , copy = False , ** kwargs ):
512519 """Linearize an input/output system at a given state and input.
513520
@@ -651,7 +658,7 @@ def __init__(self, linsys, **kwargs):
651658 # Note: don't use super() to override StateSpace MRO
652659 InputOutputSystem .__init__ (
653660 self , inputs = inputs , outputs = outputs , states = states ,
654- params = {} , dt = dt , name = name )
661+ params = None , dt = dt , name = name )
655662
656663 # Initalize additional state space variables
657664 StateSpace .__init__ (
@@ -668,7 +675,7 @@ def __init__(self, linsys, **kwargs):
668675 #: number of states, use :attr:`nstates`.
669676 states = property (StateSpace ._get_states , StateSpace ._set_states )
670677
671- def _update_params (self , params = {} , warning = True ):
678+ def _update_params (self , params = None , warning = True ):
672679 # Parameters not supported; issue a warning
673680 if params and warning :
674681 warn ("Parameters passed to LinearIOSystems are ignored." )
@@ -756,7 +763,7 @@ class NonlinearIOSystem(InputOutputSystem):
756763 defaults.
757764
758765 """
759- def __init__ (self , updfcn , outfcn = None , params = {} , ** kwargs ):
766+ def __init__ (self , updfcn , outfcn = None , params = None , ** kwargs ):
760767 """Create a nonlinear I/O system given update and output functions."""
761768 # Process keyword arguments
762769 name , inputs , outputs , states , dt = _process_namedio_keywords (
@@ -791,7 +798,7 @@ def __init__(self, updfcn, outfcn=None, params={}, **kwargs):
791798 "(and nstates not known)." )
792799
793800 # Initialize current parameters to default parameters
794- self ._current_params = params .copy ()
801+ self ._current_params = {} if params is None else params .copy ()
795802
796803 def __str__ (self ):
797804 return f"{ InputOutputSystem .__str__ (self )} \n \n " + \
@@ -838,7 +845,8 @@ def __call__(sys, u, params=None, squeeze=None):
838845 def _update_params (self , params , warning = False ):
839846 # Update the current parameter values
840847 self ._current_params = self .params .copy ()
841- self ._current_params .update (params )
848+ if params :
849+ self ._current_params .update (params )
842850
843851 def _rhs (self , t , x , u ):
844852 xdot = self .updfcn (t , x , u , self ._current_params ) \
@@ -862,20 +870,22 @@ class InterconnectedSystem(InputOutputSystem):
862870 See :func:`~control.interconnect` for a list of parameters.
863871
864872 """
865- def __init__ (self , syslist , connections = [] , inplist = [] , outlist = [] ,
866- params = {} , warn_duplicate = None , ** kwargs ):
873+ def __init__ (self , syslist , connections = None , inplist = None , outlist = None ,
874+ params = None , warn_duplicate = None , ** kwargs ):
867875 """Create an I/O system from a list of systems + connection info."""
868876 # Convert input and output names to lists if they aren't already
869- if not isinstance (inplist , (list , tuple )):
877+ if inplist is not None and not isinstance (inplist , (list , tuple )):
870878 inplist = [inplist ]
871- if not isinstance (outlist , (list , tuple )):
879+ if outlist is not None and not isinstance (outlist , (list , tuple )):
872880 outlist = [outlist ]
873881
874882 # Check if dt argument was given; if not, pull from systems
875883 dt = kwargs .pop ('dt' , None )
876884
877885 # Process keyword arguments (except dt)
878- defaults = {'inputs' : len (inplist ), 'outputs' : len (outlist )}
886+ defaults = {
887+ 'inputs' : len (inplist or []),
888+ 'outputs' : len (outlist or [])}
879889 name , inputs , outputs , states , _ = _process_namedio_keywords (
880890 kwargs , defaults , end = True )
881891
@@ -894,6 +904,12 @@ def __init__(self, syslist, connections=[], inplist=[], outlist=[],
894904
895905 # Go through the system list and keep track of counts, offsets
896906 for sysidx , sys in enumerate (syslist ):
907+ # If we were passed a SS or TF system, convert to LinearIOSystem
908+ if isinstance (sys , (StateSpace , TransferFunction )) and \
909+ not isinstance (sys , LinearIOSystem ):
910+ sys = LinearIOSystem (sys )
911+ syslist [sysidx ] = sys
912+
897913 # Make sure time bases are consistent
898914 dt = common_timebase (dt , sys .dt )
899915
@@ -969,7 +985,7 @@ def __init__(self, syslist, connections=[], inplist=[], outlist=[],
969985
970986 # Convert the list of interconnections to a connection map (matrix)
971987 self .connect_map = np .zeros ((ninputs , noutputs ))
972- for connection in connections :
988+ for connection in connections or [] :
973989 input_index = self ._parse_input_spec (connection [0 ])
974990 for output_spec in connection [1 :]:
975991 output_index , gain = self ._parse_output_spec (output_spec )
@@ -980,7 +996,7 @@ def __init__(self, syslist, connections=[], inplist=[], outlist=[],
980996
981997 # Convert the input list to a matrix: maps system to subsystems
982998 self .input_map = np .zeros ((ninputs , self .ninputs ))
983- for index , inpspec in enumerate (inplist ):
999+ for index , inpspec in enumerate (inplist or [] ):
9841000 if isinstance (inpspec , (int , str , tuple )):
9851001 inpspec = [inpspec ]
9861002 if not isinstance (inpspec , list ):
@@ -995,7 +1011,7 @@ def __init__(self, syslist, connections=[], inplist=[], outlist=[],
9951011
9961012 # Convert the output list to a matrix: maps subsystems to system
9971013 self .output_map = np .zeros ((self .noutputs , noutputs + ninputs ))
998- for index , outspec in enumerate (outlist ):
1014+ for index , outspec in enumerate (outlist or [] ):
9991015 if isinstance (outspec , (int , str , tuple )):
10001016 outspec = [outspec ]
10011017 if not isinstance (outspec , list ):
@@ -1009,13 +1025,14 @@ def __init__(self, syslist, connections=[], inplist=[], outlist=[],
10091025 self .output_map [index , ylist_index ] += gain
10101026
10111027 # Save the parameters for the system
1012- self .params = params .copy ()
1028+ self .params = {} if params is None else params .copy ()
10131029
10141030 def _update_params (self , params , warning = False ):
10151031 for sys in self .syslist :
10161032 local = sys .params .copy () # start with system parameters
10171033 local .update (self .params ) # update with global params
1018- local .update (params ) # update with locally passed parameters
1034+ if params :
1035+ local .update (params ) # update with locally passed parameters
10191036 sys ._update_params (local , warning = warning )
10201037
10211038 def _rhs (self , t , x , u ):
@@ -1565,7 +1582,7 @@ def __init__(self, io_sys, ss_sys=None):
15651582
15661583
15671584def input_output_response (
1568- sys , T , U = 0. , X0 = 0 , params = {} ,
1585+ sys , T , U = 0. , X0 = 0 , params = None ,
15691586 transpose = False , return_x = False , squeeze = None ,
15701587 solve_ivp_kwargs = {}, t_eval = 'T' , ** kwargs ):
15711588 """Compute the output response of a system to a given input.
@@ -1781,7 +1798,7 @@ def input_output_response(
17811798
17821799 # Update the parameter values
17831800 sys ._update_params (params )
1784-
1801+
17851802 #
17861803 # Define a function to evaluate the input at an arbitrary time
17871804 #
@@ -1900,7 +1917,7 @@ def ivp_rhs(t, x):
19001917 transpose = transpose , return_x = return_x , squeeze = squeeze )
19011918
19021919
1903- def find_eqpt (sys , x0 , u0 = [] , y0 = None , t = 0 , params = {} ,
1920+ def find_eqpt (sys , x0 , u0 = None , y0 = None , t = 0 , params = None ,
19041921 iu = None , iy = None , ix = None , idx = None , dx0 = None ,
19051922 return_y = False , return_result = False ):
19061923 """Find the equilibrium point for an input/output system.
@@ -2151,7 +2168,7 @@ def rootfun(z):
21512168
21522169
21532170# Linearize an input/output system
2154- def linearize (sys , xeq , ueq = [] , t = 0 , params = {} , ** kw ):
2171+ def linearize (sys , xeq , ueq = None , t = 0 , params = None , ** kw ):
21552172 """Linearize an input/output system at a given state and input.
21562173
21572174 This function computes the linearization of an input/output system at a
@@ -2242,7 +2259,7 @@ def ss(*args, **kwargs):
22422259 Convert a linear system into space system form. Always creates a
22432260 new system, even if sys is already a state space system.
22442261
2245- ``ss(updfcn, outfucn )``
2262+ ``ss(updfcn, outfcn )``
22462263 Create a nonlinear input/output system with update function ``updfcn``
22472264 and output function ``outfcn``. See :class:`NonlinearIOSystem` for
22482265 more information.
@@ -2523,9 +2540,9 @@ def tf2io(*args, **kwargs):
25232540
25242541
25252542# Function to create an interconnected system
2526- def interconnect (syslist , connections = None , inplist = [] , outlist = [], params = {} ,
2527- check_unused = True , ignore_inputs = None , ignore_outputs = None ,
2528- warn_duplicate = None , ** kwargs ):
2543+ def interconnect (syslist , connections = None , inplist = None , outlist = None ,
2544+ params = None , check_unused = True , ignore_inputs = None ,
2545+ ignore_outputs = None , warn_duplicate = None , ** kwargs ):
25292546 """Interconnect a set of input/output systems.
25302547
25312548 This function creates a new system that is an interconnection of a set of
@@ -2767,10 +2784,10 @@ def interconnect(syslist, connections=None, inplist=[], outlist=[], params={},
27672784 connections = []
27682785
27692786 # If inplist/outlist is not present, try using inputs/outputs instead
2770- if not inplist and inputs is not None :
2771- inplist = list (inputs )
2772- if not outlist and outputs is not None :
2773- outlist = list (outputs )
2787+ if inplist is None :
2788+ inplist = list (inputs or [] )
2789+ if outlist is None :
2790+ outlist = list (outputs or [] )
27742791
27752792 # Process input list
27762793 if not isinstance (inplist , (list , tuple )):
0 commit comments