Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
23dc966
propagate and allow overriding of signal labels in StateSpace lft fun…
tomaantjes Sep 6, 2026
e6a3e1b
add a check for validity of given nu and ny variables to statespace l…
tomaantjes Sep 6, 2026
37153cf
update Statespace lft function docstring to directly reflect its func…
tomaantjes Sep 9, 2026
6007d4d
add general lft function that can be used with any InputOutputSystem
tomaantjes Sep 12, 2026
47205b1
add lft function to kwarg tests
tomaantjes Sep 12, 2026
6776e2d
add unittests for lft function with LTI systems
tomaantjes Sep 12, 2026
bb06493
add unittest for lft function with general InputOutputSystem input
tomaantjes Sep 12, 2026
d74973d
add lft to function reference
tomaantjes Sep 12, 2026
1694287
add narrative documentation for lft function
tomaantjes Sep 12, 2026
3c9752f
make lft be able to use scalar, array and tf inputs
tomaantjes Sep 13, 2026
351ba99
add lft to see also of other bdalg functions
tomaantjes Sep 13, 2026
b72c3e0
reorder checks to be more logical in lft function
tomaantjes Sep 13, 2026
3262a35
compute maximal values before dimension check in ss.lft
tomaantjes Sep 13, 2026
2f65871
reorder bdalg lft tests
tomaantjes Sep 13, 2026
4cc8e24
add examples to lft functions
tomaantjes Sep 13, 2026
842a10c
fallback to default names if duplicate input or output names occur af…
tomaantjes Sep 13, 2026
7522036
make definition of ny and nu more general in lft docstrings
tomaantjes Sep 13, 2026
66d1f20
align FRD and non-LTI system input error test for lft function
tomaantjes Sep 13, 2026
d2264c6
remove unnecessary blank line
tomaantjes Sep 14, 2026
5a59926
mark lft test that requires slycot
tomaantjes Sep 15, 2026
7502973
add link to CDC article in lft functions
tomaantjes Sep 23, 2026
b2a59e0
add link to MATLAB lft function documentation in lft functions
tomaantjes Sep 23, 2026
76016cd
add fallback behavior for duplicate input and output labels to docstring
tomaantjes Sep 23, 2026
17d4262
remove extra spaces
tomaantjes Sep 23, 2026
c0c778e
use single backticks for Matlab lft function reference
tomaantjes Sep 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
add narrative documentation for lft function
  • Loading branch information
tomaantjes committed Sep 13, 2026
commit 16942870fa44b008986d27042d0aa9225c3e7d20
24 changes: 10 additions & 14 deletions control/bdalg.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,11 +315,11 @@ def feedback(sys1, sys2=1, sign=-1, **kwargs):
return sys

def lft(sys1, sys2, nu=-1, ny=-1, **kwargs):
"""Linear fractional transformation (LFT) of two I/O systems.
"""Linear fractional transformation of two I/O systems.

Forms the Redheffer star product of `sys1` and `sys2` [1]_.
This connects the last `nu` outputs of `sys2` to the first `nu`
inputs of `sys1`, and the first `ny` outputs of `sys1` to the
This connects the first `nu` outputs of `sys2` to the last `nu`
inputs of `sys1`, and the last `ny` outputs of `sys1` to the
first `ny` inputs of `sys2`. If `sys2` has fewer inputs and
outputs than `sys1`, this forms the lower LFT of `sys1` and
`sys2`. If `sys1` has fewer inputs and outputs than `sys2`,
Expand Down Expand Up @@ -393,12 +393,6 @@ def lft(sys1, sys2, nu=-1, ny=-1, **kwargs):
elif not isinstance(sys2, InputOutputSystem):
raise TypeError("sys2 must be an I/O system")

# Fill in default values for nu, ny
if ny == -1:
ny = min(sys2.ninputs, sys1.noutputs)
if nu == -1:
nu = min(sys2.noutputs, sys1.ninputs)

# Check that nu, ny are within bounds
if ny > sys1.noutputs or ny > sys2.ninputs:
raise ValueError(
Expand All @@ -409,14 +403,16 @@ def lft(sys1, sys2, nu=-1, ny=-1, **kwargs):
"nu can't exceed the number of inputs of sys1 or "
"outputs of sys2")

if isinstance(sys1, (ss.StateSpace, tf.TransferFunction)) and \
isinstance(sys2, (ss.StateSpace, tf.TransferFunction)):
# Both systems can be converted to StateSpace; use the fast,
# linear-algebraic implementation in StateSpace.lft directly.
# maximal values for nu, ny
if ny == -1:
ny = min(sys2.ninputs, sys1.noutputs)
if nu == -1:
nu = min(sys2.noutputs, sys1.ninputs)

if isinstance(sys1, ss.StateSpace) and isinstance(sys2, ss.StateSpace):
sys1_ss = ss._convert_to_statespace(sys1)
return sys1_ss.lft(sys2, nu, ny, **kwargs)


n1i, n1o = sys1.ninputs, sys1.noutputs
n2i, n2o = sys2.ninputs, sys2.noutputs

Expand Down
Binary file added doc/figures/bdalg-lft.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/figures/bdalg-ullft.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 59 additions & 0 deletions doc/iosys.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ Block diagram algebra is implemented using the following functions:
series
parallel
feedback
lft
negate
append

Expand All @@ -101,6 +102,64 @@ the following command will also work::

Gyu = G1.feedback(G2)

A generalized form of feedback interconnection, the star
product or linear fractional transformation (LFT), is
available via the :func:`lft` function and illustrated in the following
diagram:

.. image:: figures/bdalg-lft.png
:width: 240
:align: center

This function creates the system `G` by connecting the last `ny`
outputs of `G1` (signal `y1`) to the first `ny` inputs of `G2`, and
the first `nu` outputs of `G2` (signal `y2`) to the last `nu` inputs
of `G1`. The resulting inputs of `G` are `[w1;w2]`, and the outputs
`[z1;z2]`. Such an interconnection could be created for `G1` and `G2`
of any `InputOutputSystem` type using:

.. code::

Gzw = ct.lft(G1, G2, nu, ny)

For `StateSpace` systems, the :func:`lft` function is also available
through the :func:`StateSpace.lft` method, where `G1` must be a
`StateSpace` and `G2` something convertible to `StateSpace` as shown
below:

.. code::

Gzw = G1.lft(G2, nu, ny)

If `nu` and `ny` are omitted, they default to the largest values
allowed by the shapes of `G1` and `G2`. This can be useful for
generating lower and upper LFTs, an example of which is shown in the
diagram below:

.. image:: figures/bdalg-ullft.png
:width: 480
:align: center

To construct this example we define a plant `P`, controller `K` and
uncertainty block `Delta`:

.. code::

P = ss(..., inputs=["u_delta", "w", "u"], outputs=["y_delta", "z", "v"])
K = ss(..., input="v", output="u")
Delta = ss(..., input="y_delta", output="u_delta")

The lower LFT `PK` and upper LFT `PDelta` can then be made using:

.. code::

PK = ct.lft(P, K)
PDelta = ct.lft(Delta, P)

It is important to enter the systems in this order as `ct.lft(P, Delta)` would
result in a lower LFT of `P` and `Delta` with `u` and `v` being connected to
the `Delta` block as opposed to `u_delta` and `y_delta`.

All block diagram algebra functions allow the name of the system and
labels for signals to be specified using the usual `name`, `inputs`,
and `outputs` keywords, as described in the :class:`InputOutputSystem`
Expand Down