-
Notifications
You must be signed in to change notification settings - Fork 85
Restructuring ControlSystems
For the last couple of months there has been extensive discussions on how to restructure the ControlSystems toolbox to create a more extensible package, simpler and more efficient code and lately on how we should prepare to integrate support for the upcoming Modia implementation of the Modelica language. This page will contain some of the discussed proposals and their strengths and weaknesses. Feel free to update and contribute to this page. There will be a corresponding issue in which we are able to discuss the different approaches, but this page should be limited to the objective aspects of the proposals and the reasoning behind them.
We propose that the ControlSystems.jl
toolbox is split into two separate packages; ControlCore.jl
(or ControlSystemsCore.jl
) and ControlSystems.jl
. The ControlCore.jl
will only contain the basic elements of the toolbox such as the types (currently LTISystem
, TransferFunction
, StateSpace
, SisoTf
and its concrete types), as well as the basic operations and promotions between these types. The ControlSystems.jl
package would contain the rest of the features such as analysis, synthesis and plotting. The ControlSystems.jl
package would have ControlCore.jl
as a dependency.
- This would enable several packages to use the same basic structures without requiring the full set of features and code in
ControlSystems.jl
and any future packages. This would currently include a System Identification toolbox and could in the future be extended to packages for MPC, nonlinear systems, hardware and control for embedded systems. - Does not break any current external functionality.
- Would create a coherent set of functions (interfaces) that should be required for all concrete types
- Would enable us to ensure that we keep type stability of the systems and the types they are defined over (such as Integers, Float64 and Float32).
- As far as we can see there is no real downsides and everyone seems to be on board with this.
We plan to replace all uses of polynomials to be of the types Polynomials.jl
instead of the current internal type. It should be possible to create systems using polynomials, for example sys = tf(Poly([1,2]), Poly([1,2,1]))
. This would make it easier to work with other packages that use polynomials and there is no opposition as far as we know, and should have minimal breakage. This work has already started.
Force all concrete types to be immutable.
- Since for example the size of a MIMO system is stored in the type, this could create inconsistencies if the user does for example
sys.matrix = someOtherMatrix
orsys.ts = 1
. - There might be some computational benifits (plese fill these in here).
- If the user knows what (s)he is doing, we would inhibit the user to modify the values without copying the types.
The current set of types are not easily expendable since the type tree consist of mostly concrete types, we therefore propose to add several Abstract types to the tree. The current structure is as follows, where the SisoTf
tree is only an internal representation of an SISO frequency response, that is not available to the user:
Any --> LTISystem --> TransferFunction{S<:SisoTf}
\-> StateSpace
/-> SisoRational
Any --> SisoTf --> SisoZpk
\-> SisoGeneralized
To allow for new types such as transfer functions with noise models, nonlinear systems, LTV (Linear Time Varying), and to reflect the inherent difference between Discrete and Continuous systems we propose to add the following types. The SisoFunction
tree is an internal representation of a SISO frequency response, that is never handled directly by the user:
Any --> System --> LTISystem --> TransferFunction{S<:SisoFunction} --> DTf{S}
\ \-> CTf{S}
\-> StateSpace --> DSs{T<:Real}
\-> CSs{T<:Real}
Any --> SisoFunction{T<:Real} --> SisoTf{T<:AbstractFloat} --> SisoRational{T}
\-> SisoZpk{T}
DTf/CTf and Dss/CSs represent Discrete or Continuous, Transfer-function and State-space.
- Will have minimal breakage.
- The user will never have to think about if the system is of a SISO or MIMO type since the output is the same (as compared to Proposal 2).
- The internal types only correspond to a function (frequency response) and would therefore not have to contain any data about sample time, allowing this to be represented only once, in the DTf.
- Some users only work with SISO systems and by assuming that any system is MIMO (with the special case of
1x1
) the output of most functions would be anArray
. For exampledcgain
for any system isdcgain(sys)::Array{2,T}
and freqresp is of sizenw*ny*nu
which sometimes result in that the user has to splice the output to get the desired result. This is mitigated to some part by letting the dimensions of the system be the last two indices, which in current versions of julia can be dropped when indexed into, for examplefreqresp(sys)[:]
andfreqresp(sys)[:,1,1]
has identical behavior for1x1
systems.
Just as in the following proposals, we should consider allowing any Number (or Any) to be stored in the types, to allow for complex Transfer-functions and automatic differentiation.
This is similar to Proposal 1 but with the additional property that a SISO system is itself a System that the user has access to. This proposal can be found here (and was slightly modified on this page). The tree was split on this page to make it fit on one line. The main difference here compared to the previous proposal is that the SisoSystem
tree is is fully available to the user and contains all the information to represent a transfer function, such as sampling time:
Any --> System --> LinearSystem --> abstract SisoSystem{T<:Real}
\-> abstract MimoSystem{S<:SisoSystem}
SisoSystem{T<:Real} ------> SisoTf{T<:AbstractFloat} --> DCSisoTf{T} --> DSisoRational{T}
\ \ \-> DSisoRational{T}
\ \-> CSisoTf{T} --> CSisoZpk{T}
\ \-> CSisoRational{T}
\
\-> SisoSs{T} --> DSs{T<:Real}
\-> CSs{T<:Real}
MimoSystem{S<:SisoSystem} --> CMimo{T<:CSiso}
\-> CMimoSs{T<:Real}
\-> DMimo{T<:DSiso}
\-> DMimoSs{T<:Real}
with the additional definitions
CSiso = Union{CSisoTf,CSisoSs}
DSiso = Union{DSisoTf,DSisoSs}
This would mean that all MIMO systems are a collection of SISO systems that can be extracted using sys[i,j]
.
- The output from functions can be simplified when working with SISO systems without having undesired type instability.
- The properties of a SISO/MIMO system can be viewed as as analogy to the properties of Reals/Arrays.
- It would be possible(?) to create a MIMO system of dimension larger than 2, e.g with an array of signals as input or output at each time point instead of just a vector.
- SIMO and MISO would also have to be types to produce the expected result of dropping dimensions corresponding to singular dimensions.
- The user has to keep track on if they are working with SISO or MIMO systems because of the different sized output.
- Actually not. If you are thinking of simulation environments or some other things,
numoutputs
andnuminputs
interface is provided just for this purpose. if you want to add a siso to a mimo or do some interconnection, thepromote_type
can promote the siso to a mimo. Then, the interconnection will be transparent to the user. - This does not address the issue that I was referring to. It was regarding the output of for example
dcgain
being either a scalal or matrix for the different types, instead of being consistent. A direct reference to the possible "Pro" of simpler output above.
- Actually not. If you are thinking of simulation environments or some other things,
- This might lead to more code when implementing, which could deter contributions, but this is debatable.
- Contribution is not about the amount of code, I believe. It is about how you represent the work and showcase your program with concrete examples/needs. Some goodreads about it are here: 1, 2, 3. Well, you might buy these arguments, or not, I do not know. But if you check the references and such therein, you will see that good documentation and some guidelines are the ones to attract contributors. Remember, we are ourselves contributors to the
julia
society, and we have not read the codebase, but the manuals.
- Contribution is not about the amount of code, I believe. It is about how you represent the work and showcase your program with concrete examples/needs. Some goodreads about it are here: 1, 2, 3. Well, you might buy these arguments, or not, I do not know. But if you check the references and such therein, you will see that good documentation and some guidelines are the ones to attract contributors. Remember, we are ourselves contributors to the
- A SISO system can be seen as a special case of a MIMO so its need to exist as a separate type is debatable. This would result in two different possible representations of the same theoretical construct.
- For a siso system, there is no direction information for the zeros, for example, whereas for mimo systems there is direction information. Apparently there is a difference between a siso and a mimo.
- Most, if not all properties of a MIMO has SISO as a special case. By providing the function
zeros
(for input/output zeros) andtzeros
(for multivariate zeros) both the input/output zeros and the multivariate zeros can be extracted transparently.
- Most, if not all properties of a MIMO has SISO as a special case. By providing the function
- For a siso system, there is no direction information for the zeros, for example, whereas for mimo systems there is direction information. Apparently there is a difference between a siso and a mimo.
- A SISO and a MIMO system would need to be created using different methods.
- We have
ss
,zpk
andtf
implementations for creating siso and mimo systems. The output depends on what you give to these functions.
- We have
- Properties such as sampling time would be kept in each SISO system and would be required to be consistent.
- As a remark from our talk {Mattias and Arda}, even if you keep one Ts information in a mimo construct, the user is still able to assign a different sampled system in an element of the matrix provided at the time of construction.
- There would be no sample-time information in the
SisoFunction
from Proposal 1, they are just functions representing a frequency response.
- There would be no sample-time information in the
- As a remark from our talk {Mattias and Arda}, even if you keep one Ts information in a mimo construct, the user is still able to assign a different sampled system in an element of the matrix provided at the time of construction.
- (Edit: This is an implementation detail and not very relevant to the proposal) A SisoSs is proposed to have the B and C "matrices" as vectors which would inhibit using the same code for the two types, or require promotion from SISO to MIMO and then stripping dimensions from the output.
- Actually, right now B and C "matrices" are not "matrices" anymore, as I have explained in our discussion. Now they all inherit from abstractarrays of dimension
0 <= n < 3
, which, together with the sanity check in the constructor, allows for any meaningfulsparse
,dense
andsubarray
constructs available tojulia
. please bear with us a bit more to see the latest construct.- Yes, thus the quotes for "matrices". There is nothing inhibiting having sparse arrays in Proposal 1, using a similar construction if desired. However this proposal seems to inhibit working with A,B and C as matrices of dimension 2 (for example CAB) for the (possible?) computational and memory reduction of allowing them to be of dimension 1. This is to some part a different discussion that might not be relevant to the rest of the proposal.
- Actually, right now B and C "matrices" are not "matrices" anymore, as I have explained in our discussion. Now they all inherit from abstractarrays of dimension
- This enables the user to create MIMO systems that consist of both SISOTf and SISOSs. Whether this is a Pro or Con is debatable.
- Multiplication (
sys1*sys2
) of a MIMO and a SISO could be interpreted as a broadcast implicitly without having to check the dimensions of the systems. If everything is MIMO this has to be done either usingsys1.*sys2
(which requires the dimensions to be checked so that one of them is SISO) orsys1*sys2
(requiring the multiplication to consider the dimensions and doing implicit broadcast).
This point is included in the proposal on the link for Proposal 2 but is kept in a separate point since it seems it is not necessary in the proposal above and there are different ways to implement this. To allow for easier creation of collections of transfer functions, the proposal is to use the syntax
sys = tf([1,2],[1,2,3])
for creating SISO systems and for example
sys = tf([1,2],[1,2,3])
mimoSys = tf([1*sys 2*sys; 3*sys 4*sys])
to create MIMO systems. I.e. when a collection of systems is input a MIMO system is created.
- It keeps consistency (and simplifies) in creating 2 dimensional Arrays of systems, for example
[1*sys 2*sys; 3*sys 4*sys]
would still be an array, just like (in the current implementation)[1*sys, 2*sys, 3*sys]
is a vector of systems.
- It breaks the syntax that users are used to from example MATLAB where
[1*sys 2*sys; 3*sys 4*sys]
creates a MIMO system. - Any package that does analysis of systems would have to consider whether or not to treat an Array of systems as a MIMO system. It adds the question if
tf([1*sys, 2*sys, 3*sys])
and[1*sys, 2*sys, 3*sys]
should be treated as SIMO or MISO or not be allowed at all. - If a collection of SISO/MIMO systems is accepted as inputs to analysis/synthesis functions, then compatibility between the dimensions would have to be checked at every call.
Instead of not allowing the array syntax for creating a MIMO, a separate constructor could be used for SISO and MIMO systems (for example tfs
for SISO and tf
for MIMO , where the syntax
[1*sys 2*sys; 3*sys 4*sys]
would create an Array for SISO systems sys
and a MIMO system for MIMO sys
.
Similar for the alternative above.
- The additional downside of having multiple constructors, possibly confusing the user.
- Has the benefit of allowing the syntax that the user is used to, when not explicitly working with MIMO systems.