Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
9c53287
Implement object-oriented uncertainty block structure in source code
timeverettadams May 13, 2025
dd48ded
Implement object-oriented uncertainty blocks in tests and examples
timeverettadams May 14, 2025
8f47d5f
Add UncertaintyBlock parent class
timeverettadams May 14, 2025
d59e62c
Add MATLAB block structure to OOP block structure conversion and bloc…
timeverettadams May 27, 2025
de98ddc
Fix current doctests
timeverettadams May 27, 2025
fce826e
Update Sphinx documentation and bump version number and date
timeverettadams May 27, 2025
89070c9
Implement UncertaintyBlock as an abstract class
timeverettadams Jun 25, 2025
bbd8f43
Create an UncertaintyBlockStructure class to create common interface …
timeverettadams Jun 26, 2025
a90009c
Add architecture overview documentation and update examples with new …
timeverettadams Jul 3, 2025
916fbb5
Remove flaky doctest
sdahdah Jul 9, 2025
64cba3f
Merge branch 'main' into 36-implement-oop-block_structure-approach-an…
sdahdah Jul 9, 2025
e16e181
Revert to list of UncertaintyBlock and MATLAB format for uncertainty …
timeverettadams Jul 16, 2025
c47802a
Update unit tests as control.append returns same object type as inputs
timeverettadams Jul 16, 2025
dfbb9e7
Add name to conf.py
timeverettadams Jul 16, 2025
6e6eed4
Substitute utilities._tf_combine and utilities._tf_split for python-c…
timeverettadams Jul 16, 2025
848baa9
Tweak doctests
timeverettadams Jul 16, 2025
e9ddca2
Decrease numerical precision of doctests
timeverettadams Jul 21, 2025
2347d6b
Add np.ndarray in block_structure type hints and remove transfer func…
timeverettadams Jul 24, 2025
58475bf
Update uncertainty structure description in README.rst example
timeverettadams Jul 24, 2025
7cd241e
Update version numbers
timeverettadams Jul 24, 2025
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
4 changes: 4 additions & 0 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ authors:
given-names: "Steven"
orcid: "https://orcid.org/0000-0003-4930-9634"
affiliation: "McGill University"
- family-names: "Adams"
given-names: "Timothy Everett"
orcid: "https://orcid.org/0009-0008-5291-0686"
affiliation: "McGill University"
- family-names: "Forbes"
given-names: "James Richard"
orcid: "https://orcid.org/0000-0002-1987-9268"
Expand Down
6 changes: 3 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,12 @@ If you use this software in your research, please cite it as below or see

.. code-block:: bibtex

@software{dahdah_dkpy_2024,
@software{dahdah_dkpy_2025,
title={{decargroup/dkpy}},
doi={10.5281/zenodo.14511244},
url={https://github.com/decargroup/dkpy},
publisher={Zenodo},
author={Steven Dahdah and James Richard Forbes},
author={Steven Dahdah and Timothy Everett Adams and James Richard Forbes},
version = {{v0.1.9}},
year={2024},
year={2025},
}
4 changes: 2 additions & 2 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information

project = "dkpy"
copyright = "2024, Steven Dahdah and James Richard Forbes"
author = "Steven Dahdah and James Richard Forbes"
copyright = "2024, Steven Dahdah, Timothy Everett Adams and James Richard Forbes"
author = "Steven Dahdah, Timothy Everett Adams and James Richard Forbes"
version = "0.1.9"
release = "0.1.9"

Expand Down
59 changes: 50 additions & 9 deletions doc/dkpy.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,38 @@
``dkpy`` architecture overview
==============================

``dkpy`` implements each step in the D-K iteration algorithm as an abstract
class. This ensures that each step satisfies the interface required by the
algorithm while leaving the numerical implementation up to the user. Sensible
implementations of each of these steps are provided in ``dkpy``. However, the
user can create custom implementations via the abstract classes. Therefore,
anyone aiming to extend or customize ``dkpy`` should familiarize themselves
with them. The abstract classes are provided below.

.. autosummary::
:toctree: _autosummary/

dkpy.ControllerSynthesis
dkpy.StructuredSingularValue
dkpy.DScaleFit
dkpy.DkIteration

The steps of the D-K iteration algorithm are as follows [SP06]_.
1. Controller synthesis (:class:`ControllerSynthesis`): Synthesize an
H-infinity controller for the scaled problem with fixed fitted D-scales.
2. Structured singular value and D-scale computation
(:class:`StructuredSingularValue`): Compute the structured singular value
and the D-scales over a discrete grid of frequencies with a fixed
controller.
3. D-scale fit (:class:`DScaleFit`): Fit the magnitude of each D-scale to a
stable minimum-phase LTI system.

The D-K iteration algorithm, specified by a :class:`DkIteration` object, loops
through these three steps until the robustness criteria are satisfied.
Therefore, the algorithm is specified using implementations of each of the
abstract classes :class:`ControllerSynthesis`,
:class:`StructuredSingularValue`, and :class:`DScaleFit`.

D-K iteration methods
=====================

Expand Down Expand Up @@ -69,17 +104,23 @@ selecting the order in :func:`DScaleFit.fit`.

dkpy.DScaleFitSlicot

Extending ``dkpy``
==================
Uncertainty block structure
===========================

The abstract classes defining the structure of ``dkpy`` are presented below.
Anyone aiming to extend or customize ``dkpy`` should familiarize themselves
with them.
The uncertainty block structure is specified via an
:class:`UncertaintyBlockStructure` object, which encodes the block diagonal
uncertainty structure. The :class:`UncertaintyBlockStructure` object is
composed of individual uncertainty blocks that satisfy the interface in
:class:`UncertaintyBlock`, which are provided below.

.. autosummary::
:toctree: _autosummary/

dkpy.DkIteration
dkpy.ControllerSynthesis
dkpy.StructuredSingularValue
dkpy.DScaleFit
dkpy.RealDiagonalBlock
dkpy.ComplexDiagonalBlock
dkpy.ComplexFullBlock

The :class:`UncertaintyBlockStructure` object can also be specified using the
MATLAB two-column array format (see `MATLAB documentation
<https://www.mathworks.com/help/robust/ref/mussv.html>`) for users that are
more comfortable with this notation.
14 changes: 12 additions & 2 deletions examples/1_example_dk_iter_fixed_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,23 @@ def example_dk_iter_fixed_order():
)

omega = np.logspace(-3, 3, 61)
block_structure = np.array([[1, 1], [1, 1], [2, 2]])
# Alternative MATLAB descr.
# uncertainty_structure = dkpy.UncertaintyBlockStructure(
# [[1, 1], [1, 1], [2, 2]]
# )
uncertainty_structure = dkpy.UncertaintyBlockStructure(
[
dkpy.ComplexFullBlock(1, 1),
dkpy.ComplexFullBlock(1, 1),
dkpy.ComplexFullBlock(2, 2),
]
)
K, N, mu, iter_results, info = dk_iter.synthesize(
eg["P"],
eg["n_y"],
eg["n_u"],
omega,
block_structure,
uncertainty_structure,
)

print(f"mu={mu}")
Expand Down
14 changes: 12 additions & 2 deletions examples/2_example_dk_iter_list_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,23 @@ def example_dk_iter_list_order():
)

omega = np.logspace(-3, 3, 61)
block_structure = np.array([[1, 1], [1, 1], [2, 2]])
# Alternative MATLAB descr.
# uncertainty_structure = dkpy.UncertaintyBlockStructure(
# [[1, 1], [1, 1], [2, 2]]
# )
uncertainty_structure = dkpy.UncertaintyBlockStructure(
[
dkpy.ComplexFullBlock(1, 1),
dkpy.ComplexFullBlock(1, 1),
dkpy.ComplexFullBlock(2, 2),
]
)
K, N, mu, iter_results, info = dk_iter.synthesize(
eg["P"],
eg["n_y"],
eg["n_u"],
omega,
block_structure,
uncertainty_structure,
)

print(f"mu={mu}")
Expand Down
14 changes: 12 additions & 2 deletions examples/3_example_dk_iter_auto_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,23 @@ def example_dk_iter_auto_order():
)

omega = np.logspace(-3, 3, 61)
block_structure = np.array([[1, 1], [1, 1], [2, 2]])
# Alternative MATLAB descr.
# uncertainty_structure = dkpy.UncertaintyBlockStructure(
# [[1, 1], [1, 1], [2, 2]]
# )
uncertainty_structure = dkpy.UncertaintyBlockStructure(
[
dkpy.ComplexFullBlock(1, 1),
dkpy.ComplexFullBlock(1, 1),
dkpy.ComplexFullBlock(2, 2),
]
)
K, N, mu, iter_results, info = dk_iter.synthesize(
eg["P"],
eg["n_y"],
eg["n_u"],
omega,
block_structure,
uncertainty_structure,
)

print(f"mu={mu}")
Expand Down
14 changes: 12 additions & 2 deletions examples/4_example_dk_iter_interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,23 @@ def example_dk_iter_interactive():
)

omega = np.logspace(-3, 3, 61)
block_structure = np.array([[1, 1], [1, 1], [2, 2]])
# Alternative MATLAB descr.
# uncertainty_structure = dkpy.UncertaintyBlockStructure(
# [[1, 1], [1, 1], [2, 2]]
# )
uncertainty_structure = dkpy.UncertaintyBlockStructure(
[
dkpy.ComplexFullBlock(1, 1),
dkpy.ComplexFullBlock(1, 1),
dkpy.ComplexFullBlock(2, 2),
]
)
K, N, mu, iter_results, info = dk_iter.synthesize(
eg["P"],
eg["n_y"],
eg["n_u"],
omega,
block_structure,
uncertainty_structure,
)

print(f"mu={mu}")
Expand Down
16 changes: 13 additions & 3 deletions examples/5_example_dk_iteration_custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def _get_fit_order(
D_omega,
P,
K,
block_structure,
uncertainty_structure,
):
print(f"Iteration {self.my_iter_count} with mu of {np.max(mu_omega)}")
if self.my_iter_count < 3:
Expand Down Expand Up @@ -70,13 +70,23 @@ def example_dk_iter_custom():
)

omega = np.logspace(-3, 3, 61)
block_structure = np.array([[1, 1], [1, 1], [2, 2]])
# Alternative MATLAB descr.
# uncertainty_structure = dkpy.UncertaintyBlockStructure(
# [[1, 1], [1, 1], [2, 2]]
# )
uncertainty_structure = dkpy.UncertaintyBlockStructure(
[
dkpy.ComplexFullBlock(1, 1),
dkpy.ComplexFullBlock(1, 1),
dkpy.ComplexFullBlock(2, 2),
]
)
K, N, mu, iter_results, info = dk_iter.synthesize(
eg["P"],
eg["n_y"],
eg["n_u"],
omega,
block_structure,
uncertainty_structure,
)

print(f"mu={mu}")
Expand Down
1 change: 1 addition & 0 deletions src/dkpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
from .dk_iteration import *
from .d_scale_fit import *
from .structured_singular_value import *
from .uncertainty_structure import *
from .utilities import *
6 changes: 3 additions & 3 deletions src/dkpy/controller_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class HinfSynSlicot(ControllerSynthesis):
>>> P, n_y, n_u = example_scherer1997_p907
>>> K, N, gamma, info = dkpy.HinfSynSlicot().synthesize(P, n_y, n_u)
>>> gamma
9.5080
9.51
"""

def __init__(self):
Expand Down Expand Up @@ -107,7 +107,7 @@ class HinfSynLmi(ControllerSynthesis):
>>> P, n_y, n_u = example_scherer1997_p907
>>> K, N, gamma, info = dkpy.HinfSynLmi().synthesize(P, n_y, n_u)
>>> gamma
9.5081
9.51

H-infinity controller synthesis with CLARABEL

Expand Down Expand Up @@ -407,7 +407,7 @@ class HinfSynLmiBisection(ControllerSynthesis):
... },
... ).synthesize(P, n_y, n_u)
>>> gamma
9.5093
9.51
"""

def __init__(
Expand Down
Loading
Loading