DirectSD-Python 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. directsd_python-0.1.0/DirectSD_Python.egg-info/PKG-INFO +450 -0
  2. directsd_python-0.1.0/DirectSD_Python.egg-info/SOURCES.txt +44 -0
  3. directsd_python-0.1.0/DirectSD_Python.egg-info/dependency_links.txt +1 -0
  4. directsd_python-0.1.0/DirectSD_Python.egg-info/requires.txt +20 -0
  5. directsd_python-0.1.0/DirectSD_Python.egg-info/top_level.txt +1 -0
  6. directsd_python-0.1.0/LICENSE +36 -0
  7. directsd_python-0.1.0/PKG-INFO +450 -0
  8. directsd_python-0.1.0/README.md +408 -0
  9. directsd_python-0.1.0/directsd/__init__.py +141 -0
  10. directsd_python-0.1.0/directsd/analysis/__init__.py +4 -0
  11. directsd_python-0.1.0/directsd/analysis/charpol.py +89 -0
  12. directsd_python-0.1.0/directsd/analysis/errors.py +185 -0
  13. directsd_python-0.1.0/directsd/analysis/norms.py +630 -0
  14. directsd_python-0.1.0/directsd/design/__init__.py +9 -0
  15. directsd_python-0.1.0/directsd/design/convex.py +620 -0
  16. directsd_python-0.1.0/directsd/design/lifting.py +403 -0
  17. directsd_python-0.1.0/directsd/design/polynomial.py +5925 -0
  18. directsd_python-0.1.0/directsd/examples/__init__.py +0 -0
  19. directsd_python-0.1.0/directsd/examples/_common.py +36 -0
  20. directsd_python-0.1.0/directsd/examples/demos.py +1161 -0
  21. directsd_python-0.1.0/directsd/examples/examples.py +296 -0
  22. directsd_python-0.1.0/directsd/examples/help_examples.py +1043 -0
  23. directsd_python-0.1.0/directsd/glopt/__init__.py +4 -0
  24. directsd_python-0.1.0/directsd/glopt/advanced.py +1658 -0
  25. directsd_python-0.1.0/directsd/glopt/optimize.py +420 -0
  26. directsd_python-0.1.0/directsd/linalg/__init__.py +3 -0
  27. directsd_python-0.1.0/directsd/linalg/linsys.py +66 -0
  28. directsd_python-0.1.0/directsd/linalg/matrices.py +156 -0
  29. directsd_python-0.1.0/directsd/linalg/minreal.py +425 -0
  30. directsd_python-0.1.0/directsd/linalg/riccati.py +197 -0
  31. directsd_python-0.1.0/directsd/polynomial/__init__.py +11 -0
  32. directsd_python-0.1.0/directsd/polynomial/diophantine.py +426 -0
  33. directsd_python-0.1.0/directsd/polynomial/operations.py +247 -0
  34. directsd_python-0.1.0/directsd/polynomial/poln.py +742 -0
  35. directsd_python-0.1.0/directsd/polynomial/spectral.py +368 -0
  36. directsd_python-0.1.0/directsd/polynomial/transforms.py +124 -0
  37. directsd_python-0.1.0/directsd/polynomial/utils.py +449 -0
  38. directsd_python-0.1.0/directsd/sspace/__init__.py +4 -0
  39. directsd_python-0.1.0/directsd/sspace/design.py +1083 -0
  40. directsd_python-0.1.0/directsd/sspace/plant.py +198 -0
  41. directsd_python-0.1.0/directsd/tf/__init__.py +9 -0
  42. directsd_python-0.1.0/directsd/tf/interconnect.py +105 -0
  43. directsd_python-0.1.0/directsd/zpk/__init__.py +1 -0
  44. directsd_python-0.1.0/directsd/zpk/zpk.py +400 -0
  45. directsd_python-0.1.0/pyproject.toml +59 -0
  46. directsd_python-0.1.0/setup.cfg +4 -0
@@ -0,0 +1,450 @@
1
+ Metadata-Version: 2.4
2
+ Name: DirectSD-Python
3
+ Version: 0.1.0
4
+ Summary: Direct Sampled-Data Control Systems Toolbox — Python port of MATLAB DirectSD v3.0
5
+ Author-email: Roozbeh Dargahi <roozbehdargahi.0x7b4@gmail.com>
6
+ License: BSD-3-Clause
7
+ Project-URL: Homepage, https://github.com/RooDK0x7b4/DirectSD-Python
8
+ Project-URL: Repository, https://github.com/RooDK0x7b4/DirectSD-Python
9
+ Project-URL: Issues, https://github.com/RooDK0x7b4/DirectSD-Python/issues
10
+ Keywords: control-systems,sampled-data,h2-control,h-infinity,diophantine-equations,polynomial-methods,digital-control
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: License :: OSI Approved :: BSD License
14
+ Classifier: Topic :: Scientific/Engineering
15
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Operating System :: OS Independent
23
+ Requires-Python: >=3.8
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: numpy>=1.21
27
+ Requires-Dist: scipy>=1.7
28
+ Provides-Extra: control
29
+ Requires-Dist: control>=0.9; extra == "control"
30
+ Provides-Extra: plot
31
+ Requires-Dist: matplotlib>=3.4; extra == "plot"
32
+ Provides-Extra: convex
33
+ Requires-Dist: cvxpy>=1.3; extra == "convex"
34
+ Provides-Extra: dev
35
+ Requires-Dist: pytest>=7.0; extra == "dev"
36
+ Requires-Dist: matplotlib>=3.4; extra == "dev"
37
+ Provides-Extra: all
38
+ Requires-Dist: control>=0.9; extra == "all"
39
+ Requires-Dist: matplotlib>=3.4; extra == "all"
40
+ Requires-Dist: cvxpy>=1.3; extra == "all"
41
+ Dynamic: license-file
42
+
43
+ # DirectSD — Python Package
44
+
45
+ [github.com/RooDK0x7b4/DirectSD-Python](https://github.com/RooDK0x7b4/DirectSD-Python)
46
+
47
+ Python port of the **DirectSD MATLAB Toolbox v3.0** by Konstantin Polyakov (1995–2006).
48
+
49
+ DirectSD is a toolbox for analysis and design of **sampled-data (hybrid) control systems** using:
50
+ - Polynomial (Diophantine-equation) methods
51
+ - Lifting / FR-operator approach (exact inter-sample modelling)
52
+ - State-space (LQR / Kalman / Riccati ARE) methods
53
+ - H2 and H∞ optimal control
54
+ - Global optimization (Strongin information algorithm, SA simplex, Hilbert-curve search)
55
+ - **L1 / L∞ convex synthesis** via Youla parameterisation (requires `cvxpy`)
56
+
57
+ ---
58
+
59
+ ## Installation
60
+
61
+ ```bash
62
+ pip install numpy scipy # runtime dependencies
63
+ pip install -e . # development install from source
64
+ ```
65
+
66
+ Or with optional extras:
67
+
68
+ ```bash
69
+ pip install -e ".[dev]" # adds pytest + matplotlib
70
+ pip install -e ".[convex]" # adds cvxpy for L1/L∞ convex synthesis
71
+ pip install -e ".[control]" # adds python-control interoperability
72
+ pip install -e ".[all]" # installs all optional dependencies
73
+ ```
74
+
75
+ ---
76
+
77
+ ## Package Structure
78
+
79
+ ```
80
+ directsd/
81
+ ├── __init__.py # flat public API — import everything from here
82
+
83
+ ├── polynomial/ # Poln class, operations, spectral factorisation, transforms
84
+ │ ├── poln.py # Poln — the @poln OOP class equivalent
85
+ │ ├── operations.py # compat, deg, gcd, coprime, triple, factor,
86
+ │ │ # striplz, recip, vec, dioph, dioph2
87
+ │ ├── spectral.py # sfactor, sfactfft
88
+ │ ├── transforms.py # dtfm (ZOH discrete Laplace), ztrm (modified Z)
89
+ │ └── utils.py # bilintr, improper, diophsys, tf2nd,
90
+ │ # separtf, sumzpk, bilinss
91
+
92
+ ├── zpk/ # Zpk class (root-list zero-pole-gain representation)
93
+ │ └── zpk.py
94
+
95
+ ├── linalg/ # Linear algebra utilities
96
+ │ ├── matrices.py # toep, hank, givens, house, lyap, dlyap
97
+ │ └── linsys.py # linsys (QR / SVD with iterative refinement)
98
+
99
+ ├── analysis/ # Sampled-data system analysis
100
+ │ ├── norms.py # sdh2norm, sdhinorm, dinfnorm, dahinorm,
101
+ │ │ # h2norm_ct, hinfnorm_ct
102
+ │ │ # ── all via Lyapunov / Hamiltonian ARE,
103
+ │ │ # no frequency gridding
104
+ │ ├── charpol.py # charpol, sdmargin
105
+ │ └── errors.py # quaderr, sdl2err, sd2doferr
106
+
107
+ ├── design/ # Controller design
108
+ │ ├── lifting.py # lift_h2, lift_l2, compute_gamma
109
+ │ │ # ── Van Loan exact inter-sample lifting
110
+ │ │ # (FR-operator / Chen-Francis method)
111
+ │ ├── polynomial.py # ch2, sdh2, sdl2, dhinf, sd2dof, polquad
112
+ │ └── convex.py # sdl1_reg, sd_mixed_h2_l1, sd_constrained,
113
+ │ # sdl1norm, youla_basis [requires cvxpy]
114
+
115
+ ├── sspace/ # State-space design
116
+ │ ├── plant.py # GeneralizedPlant class (augmented plant representation)
117
+ │ └── design.py # h2reg, hinfreg, sdh2reg, sdhinfreg,
118
+ │ # sdfast, separss
119
+
120
+ ├── tf/ # Block-diagram arithmetic
121
+ │ ├── interconnect.py # mul, neg, add, feedback, nd, to_lti
122
+ │ └── __init__.py # re-exports all interconnect functions
123
+
124
+ ├── glopt/ # Global optimization
125
+ │ ├── optimize.py # neldermead, simanneal, randsearch,
126
+ │ │ # crandsearch, dual_annealing, sector, banana
127
+ │ └── advanced.py # Full dsdglopt port: updateopt, uniproj, u2range,
128
+ │ # randbeta, randgamma, sa_testfun,
129
+ │ # val2bin, bin2val, coord2hilb, hilb2coord,
130
+ │ # r2range, r1range, admproj, par2cp, cp2par,
131
+ │ # guesspoles, k2ksi, go_par2k,
132
+ │ # f_sdh2p, f_sdl2p, go_sdh2p, go_sdl2p,
133
+ │ # sasimplex, arandsearch,
134
+ │ # infglob, infglobc, optglob, optglobc
135
+
136
+ ├── examples/ # demos.py (25 MATLAB demo ports), help_examples.py
137
+ │ # (26 dsd_help.chm worked examples), examples.py
138
+ │ # (11 core-utility examples), _common.py (shared helpers)
139
+
140
+ └── tests/
141
+ └── test_directsd.py # 228 pytest tests (209 core + 19 convex,
142
+ # convex skipped automatically without cvxpy)
143
+ ```
144
+
145
+ ---
146
+
147
+ ## Quick Start
148
+
149
+ ### Polynomial arithmetic
150
+
151
+ ```python
152
+ from directsd import Poln, s_var, gcd, sfactor, dioph
153
+
154
+ s = s_var()
155
+ p = Poln([1, 3, 2], 's') # s² + 3s + 2
156
+ q = Poln([1, 1], 's') # s + 1
157
+
158
+ print(p * q) # s³ + 4s² + 5s + 2
159
+ print(p.roots) # [-2. -1.]
160
+ print(gcd(p, q)) # s + 1
161
+
162
+ # Spectral factorization: R(s) = N(s)·N(-s) → find N
163
+ R = Poln([-1, 0, 1], 's') # 1 − s² (Hermitian)
164
+ fs, _ = sfactor(R)
165
+ print(fs) # s + 1
166
+
167
+ # Diophantine equation: X·A + Y·B = C
168
+ x, y, err, cond = dioph(Poln([1,1],'s'), Poln([1,0],'s'), Poln([1],'s'))
169
+ ```
170
+
171
+ ### Lifting → H2 design (recommended workflow)
172
+
173
+ ```python
174
+ import scipy.signal as sig
175
+ from directsd import lift_h2, h2reg, compute_gamma
176
+ import numpy as np
177
+
178
+ # Continuous-time generalized plant (standard 2×2 form)
179
+ plant = sig.StateSpace(
180
+ np.array([[-1., 0.5], [0., -2.]]),
181
+ np.array([[1., 0.], [0., 1.]]),
182
+ np.array([[1., 0.], [0., 1.]]),
183
+ np.array([[0., 1.], [1., 0.]])
184
+ )
185
+
186
+ T = 0.1 # sampling period
187
+
188
+ # Step 1 — lift to exact discrete equivalent (Van Loan / FR-operator)
189
+ P_lifted, gamma, _ = lift_h2(plant, T)
190
+ print(f"Lifted plant: {P_lifted.A.shape}, γ = {gamma:.4f}")
191
+
192
+ # Step 2 — H2-optimal controller on lifted plant
193
+ K, h2n = h2reg(P_lifted, n_meas=1, n_ctrl=1)
194
+
195
+ # Step 3 — exact total H2 cost
196
+ total_cost = np.sqrt(h2n**2 + gamma)
197
+ print(f"Optimal H2 cost: {total_cost:.4f}")
198
+ ```
199
+
200
+ ### Norms (algebraic, no frequency gridding)
201
+
202
+ ```python
203
+ from directsd import sdh2norm, sdhinorm, h2norm_ct, hinfnorm_ct, dinfnorm
204
+ import scipy.signal as sig
205
+
206
+ plant = sig.lti([1], [1, 1])
207
+
208
+ # Continuous-time norms
209
+ print(h2norm_ct(plant)) # 0.7071 (= 1/√2)
210
+ gamma, w = hinfnorm_ct(plant) # 1.0 at ω=0
211
+ print(gamma, w)
212
+
213
+ # Sampled-data norms
214
+ K = ([0.5], [1.0])
215
+ print(sdh2norm(plant, K, T=0.1))
216
+ gamma, w = sdhinorm(plant, K, T=0.1)
217
+ print(gamma, w)
218
+ ```
219
+
220
+ ### Stability analysis
221
+
222
+ ```python
223
+ from directsd import charpol, sdmargin
224
+ import scipy.signal as sig
225
+
226
+ plant = sig.lti([1], [1, 1])
227
+ K = ([1.0], [1.0])
228
+ T = 0.1
229
+
230
+ delta = charpol(plant, K, T) # characteristic polynomial coefficients
231
+ margin, poles = sdmargin(plant, K, T)
232
+ print(f"Stability margin: {margin:.4f}") # positive = stable
233
+ print(f"Closed-loop poles: {poles}")
234
+ ```
235
+
236
+ ### Global optimization
237
+
238
+ ```python
239
+ from directsd import neldermead, dual_annealing, simanneal, sector, banana
240
+
241
+ # Nelder-Mead (deterministic)
242
+ x, f, n = neldermead(banana, [-1.2, 1.0], tol=1e-8)
243
+ print(f"Banana min: x={x}, f={f:.2e}")
244
+
245
+ # Dual annealing (scipy hybrid, faster and more robust than simanneal)
246
+ x, f, res = dual_annealing(banana, [(-3,3),(-3,3)], seed=42)
247
+ print(f"Dual SA min: x={x}, f={f:.2e}, evals={res.nfev}")
248
+
249
+ # Stability sector
250
+ alpha, beta = sector([-1+2j, -1-2j, -3+0j])
251
+ print(f"Degree of stability α={alpha}, oscillation β={beta}")
252
+ ```
253
+
254
+ ### Advanced global optimization (dsdglopt full port)
255
+
256
+ ```python
257
+ from directsd import (
258
+ infglob, optglob, sasimplex, arandsearch,
259
+ par2cp, cp2par, admproj, guesspoles,
260
+ coord2hilb, hilb2coord, sa_testfun, randbeta, randgamma,
261
+ )
262
+
263
+ # Strongin information algorithm — 1D Lipschitz minimisation
264
+ f = lambda x: (x - 0.3)**2 + 0.1*np.sin(20*x)
265
+ x_best, z_best, n_iter, trace = infglob(f, {'maxIter': 300, 'r': 2.5})
266
+
267
+ # Multi-run with zooming
268
+ x_best, z_best, coef, loops, trace = optglob(f, {'maxLoop': 10, 'maxIter': 100})
269
+
270
+ # SA + Nelder-Mead hybrid
271
+ x, y, nevals = sasimplex(sa_testfun, [0.5, 0.5], {'startTemp': 5.0, 'maxFunEvals': 5000})
272
+
273
+ # Hilbert space-filling curve (N-D ↔ 1D)
274
+ coord = np.array([0.3, 0.7, 0.5])
275
+ x1d = coord2hilb(coord, precision=8) # 3D → scalar in [0,1]
276
+ back = hilb2coord(x1d, dimensions=3, precision=8)
277
+
278
+ # Stability-sector polynomial parameterisation
279
+ Delta, DeltaZ, poles = par2cp([0.4, 0.6, 0.5], alpha=0.2, beta=np.inf)
280
+ rho = cp2par(DeltaZ, alpha=0.2, beta=np.inf) # inverse
281
+ ```
282
+
283
+ ### Convex synthesis — L1 and mixed H2/L1 (requires `pip install cvxpy`)
284
+
285
+ The Youla (Q) parameterisation turns all stabilising controllers into an
286
+ affine family, making L1 and mixed-norm problems convex.
287
+
288
+ ```python
289
+ import numpy as np
290
+ import scipy.signal as sig
291
+ from directsd import lift_h2, sdl1_reg, sd_mixed_h2_l1, sd_constrained, sdl1norm
292
+
293
+ # Step 1 — continuous-time generalised plant (standard 2×2 form)
294
+ ct_plant = sig.StateSpace(
295
+ np.array([[-1.]]),
296
+ np.array([[1., 1.]]),
297
+ np.array([[1.], [1.]]),
298
+ np.array([[0., 1.], [1., 0.]])
299
+ )
300
+ T = 0.1
301
+
302
+ # Step 2 — lift to exact discrete equivalent
303
+ P_lifted, _, _ = lift_h2(ct_plant, T)
304
+
305
+ # L1-optimal controller (minimises peak output amplitude)
306
+ K_l1, l1_norm, Q_fir, info = sdl1_reg(P_lifted, N_fir=20)
307
+ print(f"L1-norm (peak-to-peak gain): {l1_norm:.4f} [{info['status']}]")
308
+
309
+ # Mixed H2/L1: minimise H2 subject to L1 ≤ bound
310
+ K_mix, cost, _, _ = sd_mixed_h2_l1(P_lifted, N_fir=20, l1_bound=5.0)
311
+ print(f"H2 cost: {cost['h2']:.4f}, L1: {cost['l1']:.4f}")
312
+
313
+ # Hard envelope constraint
314
+ lo = -1.5 * np.ones((15, 1))
315
+ hi = 1.5 * np.ones((15, 1))
316
+ K_con, achieved, _, _ = sd_constrained(
317
+ P_lifted, N_fir=20, objective='h2', envelope=(lo, hi)
318
+ )
319
+ print(f"Constrained H2: {achieved['h2']:.4f}, linf: {achieved['linf']:.4f}")
320
+
321
+ # L1-norm analysis of any existing closed loop (no cvxpy needed)
322
+ l1, h = sdl1norm(sig.lti([1], [1, 2, 1]), ([0.5], [1.0]), T=0.1)
323
+ print(f"Closed-loop L1-norm: {l1:.4f}")
324
+ ```
325
+
326
+ ---
327
+
328
+ ## MATLAB → Python mapping
329
+
330
+ | MATLAB | Python |
331
+ |--------|--------|
332
+ | `poln(a,'s')` | `Poln(a, 's')` |
333
+ | `s`, `z`, `p`, `q`, `d` | `s_var()`, `z_var()`, … |
334
+ | `gcd(a,b)` | `gcd(a, b)` |
335
+ | `coprime(a,b)` | `coprime(a, b)` |
336
+ | `factor(p,'s')` | `factor(p, 's')` |
337
+ | `sfactor(p)` | `sfactor(p)` |
338
+ | `sfactfft(p)` | `sfactfft(p)` |
339
+ | `dioph(a,b,c)` | `dioph(a, b, c)` |
340
+ | `dtfm(G,T,0)` | `dtfm(G, T)` |
341
+ | `ztrm(G,T,mu)` | `ztrm(G, T, mu)` |
342
+ | `bilintr(F,'tustin',T)` | `bilintr(F, 'tustin', T)` |
343
+ | `improper(R)` | `improper(R)` |
344
+ | `toep(a,r,c)` | `toep(a, r, c)` |
345
+ | `linsys(A,B,'svd')` | `linsys(A, B, method='svd')` |
346
+ | `sdgh2mod(sys,T)` | `lift_h2(plant_ss, T)` |
347
+ | `sdh2simple(sys,T)` | `lift_l2(plant_ss, T)` |
348
+ | `charpol(sys,K)` | `charpol(plant, K, T)` |
349
+ | `sdmargin(sys,K)` | `sdmargin(plant, K, T)` |
350
+ | `sdh2norm(sys,K)` | `sdh2norm(plant, K, T)` |
351
+ | `sdhinorm(sys,K)` | `sdhinorm(plant, K, T)` |
352
+ | `dinfnorm(sys)` | `dinfnorm(sys)` |
353
+ | `quaderr(A,B,E,M)` | `quaderr(plant_cl, T)` |
354
+ | `sdl2err(sys,K)` | `sdl2err(plant, K, T)` |
355
+ | `ch2(sys)` | `ch2(plant)` |
356
+ | `sdh2(sys,T)` | `sdh2(plant, T)` |
357
+ | `sdl2(sys,T)` | `sdl2(plant, T)` |
358
+ | `dhinf(plant_d)` | `dhinf(plant_d)` |
359
+ | `sd2dof(sys,K,T)` | `sd2dof(plant, K_fb, T)` |
360
+ | `h2reg(sys,1,1)` | `h2reg(sys, n_meas=1, n_ctrl=1)` |
361
+ | `hinfreg(sys,1,1)` | `hinfreg(sys, n_meas=1, n_ctrl=1)` |
362
+ | `sdh2reg(sys,T)` | `sdh2reg(plant, T)` |
363
+ | `sdhinfreg(sys,T)` | `sdhinfreg(plant, T)` |
364
+ | `sdfast(sys,T)` | `sdfast(plant, T)` |
365
+ | `neldermead(f,x0)` | `neldermead(f, x0)` |
366
+ | `simanneal(f,x0,opt)` | `simanneal(f, x0, options=opt)` |
367
+ | `sector(poles)` | `sector(poles)` |
368
+ | `sasimplex(f,x0,opt)` | `sasimplex(f, x0, options=opt)` |
369
+ | `arandsearch(f,x,opt)` | `arandsearch(f, x0, options=opt)` |
370
+ | `infglob(f,opt)` | `infglob(f, options=opt)` |
371
+ | `infglobc(f,opt)` | `infglobc(f, options=opt)` |
372
+ | `optglob(f,opt)` | `optglob(f, options=opt)` |
373
+ | `optglobc(f,opt)` | `optglobc(f, options=opt)` |
374
+ | `par2cp(rho,alpha,beta)` | `par2cp(rho, alpha, beta)` |
375
+ | `cp2par(DeltaZ,alpha,beta)` | `cp2par(DeltaZ, alpha, beta)` |
376
+ | `admproj(p,alpha,beta,dom,T)` | `admproj(p, alpha, beta, dom, T)` |
377
+ | `guesspoles(poles,n)` | `guesspoles(poles, n_poles)` |
378
+ | `r1range(r2,Ea,beta,sh)` | `r1range(r2, Ea, beta, shifted)` |
379
+ | `r2range(Ea,Eb,sh)` | `r2range(Ea, Eb, shifted)` |
380
+ | `coord2hilb(c,p)` | `coord2hilb(coord, precision)` |
381
+ | `hilb2coord(x,d,p)` | `hilb2coord(x, dimensions, precision)` |
382
+ | `val2bin(x,nf)` | `val2bin(x, nfrac)` |
383
+ | `bin2val(bi,bf)` | `bin2val(bint, bfrac)` |
384
+ | `k2ksi(sys,K,dK0)` | `k2ksi(plant, K, dK0, T)` |
385
+ | `go_par2k(coef)` | `go_par2k(coef, ctx)` |
386
+ | `f_sdh2p(coef)` | `f_sdh2p(coef, ctx)` |
387
+ | `f_sdl2p(coef)` | `f_sdl2p(coef, ctx)` |
388
+ | `go_sdh2p(x)` | `go_sdh2p(x, ctx)` |
389
+ | `go_sdl2p(x)` | `go_sdl2p(x, ctx)` |
390
+ | `randbeta(a,b,r,c)` | `randbeta(a, b, rows, cols)` |
391
+ | `randgamma(a,r,c)` | `randgamma(a, rows, cols)` |
392
+ | `sa_testfun(x)` | `sa_testfun(x)` |
393
+ | `updateopt(opt,new)` | `updateopt(options, opt)` |
394
+ | `uniproj(x)` | `uniproj(x)` |
395
+ | `u2range(u,lo,hi)` | `u2range(u, lo, hi)` |
396
+ | `sdnorm(sys,K,'l1')` | `sdl1norm(plant, K, T)` — L1/peak-to-peak norm analysis |
397
+ | *(no MATLAB equivalent)* | `sdl1_reg(P_lifted, N_fir)` — L1-optimal synthesis |
398
+ | *(no MATLAB equivalent)* | `sd_mixed_h2_l1(P_lifted, N_fir, l1_bound=…)` — mixed H2/L1 |
399
+ | *(no MATLAB equivalent)* | `sd_constrained(P_lifted, N_fir, envelope=(lo,hi))` — hard constraints |
400
+ | *(no MATLAB equivalent)* | `youla_basis(P_lifted, K0, N_fir)` — raw Youla maps for custom problems |
401
+
402
+ ---
403
+
404
+ ## Key design decisions
405
+
406
+ **Lifting is first-class.** `design/lifting.py` implements the exact Van Loan
407
+ matrix-exponential method (Hagiwara-Araki 1995, Chen-Francis 1995).
408
+ Unlike simple ZOH discretization, the lifted plant accounts for inter-sample
409
+ signal energy — the `gamma` term captures what a continuous-time H2 design
410
+ recovers that a purely discrete design misses.
411
+
412
+ **No frequency gridding for norms.** All norm functions in `analysis/norms.py`
413
+ use Lyapunov equations or Hamiltonian bisection. This eliminates
414
+ `BadCoefficients` warnings from ill-conditioned transfer functions and gives
415
+ machine-precision results even for systems with integrators or sharp resonances.
416
+
417
+ **SVD-based Diophantine solver.** `dioph()` uses `scipy.linalg.lstsq` instead
418
+ of a custom QR solver, making it robust to rank-deficient Sylvester matrices
419
+ that arise when the plant has repeated or near-common poles.
420
+
421
+ **Convex synthesis goes beyond the MATLAB toolbox.** `design/convex.py`
422
+ implements L1 (peak-to-peak) optimal control and mixed H2/L1 design via the
423
+ Youla Q-parameterisation — a capability not present in the original MATLAB
424
+ DirectSD toolbox. All stabilising controllers are parameterised as
425
+ `K = K0 + Δ(Q)` where Q is a stable FIR sequence, reducing the synthesis
426
+ to a Linear Programme solved by CVXPY. This is an optional dependency:
427
+ the rest of the package works without it.
428
+
429
+ ---
430
+
431
+ ## Notes
432
+
433
+ - Simulink `.mdl` files (`dsdmodel/`) have no Python equivalent; use `scipy.signal.lsim` for simulation.
434
+ - The `@lti`, `@tf`, `@zpk` MATLAB overrides are replaced throughout by `scipy.signal`.
435
+ - All functions accept `scipy.signal.lti`, `scipy.signal.dlti`, `(num, den)` tuples, and `scipy.signal.StateSpace` objects interchangeably.
436
+
437
+ ---
438
+
439
+ ## How to Cite
440
+
441
+ If you use this package in your research, please cite it — see
442
+ [`CITATION.cff`](CITATION.cff), or use GitHub's "Cite this repository" button
443
+ on the repo page.
444
+
445
+ ## License
446
+
447
+ This Python port is licensed under the [BSD 3-Clause License](LICENSE) ©
448
+ 2026 Roozbeh Dargahi. The original MATLAB toolbox is © 1995–2006 Konstantin
449
+ Polyakov; this license covers only the Python implementation in this
450
+ repository, not the original MATLAB source.
@@ -0,0 +1,44 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ DirectSD_Python.egg-info/PKG-INFO
5
+ DirectSD_Python.egg-info/SOURCES.txt
6
+ DirectSD_Python.egg-info/dependency_links.txt
7
+ DirectSD_Python.egg-info/requires.txt
8
+ DirectSD_Python.egg-info/top_level.txt
9
+ directsd/__init__.py
10
+ directsd/analysis/__init__.py
11
+ directsd/analysis/charpol.py
12
+ directsd/analysis/errors.py
13
+ directsd/analysis/norms.py
14
+ directsd/design/__init__.py
15
+ directsd/design/convex.py
16
+ directsd/design/lifting.py
17
+ directsd/design/polynomial.py
18
+ directsd/examples/__init__.py
19
+ directsd/examples/_common.py
20
+ directsd/examples/demos.py
21
+ directsd/examples/examples.py
22
+ directsd/examples/help_examples.py
23
+ directsd/glopt/__init__.py
24
+ directsd/glopt/advanced.py
25
+ directsd/glopt/optimize.py
26
+ directsd/linalg/__init__.py
27
+ directsd/linalg/linsys.py
28
+ directsd/linalg/matrices.py
29
+ directsd/linalg/minreal.py
30
+ directsd/linalg/riccati.py
31
+ directsd/polynomial/__init__.py
32
+ directsd/polynomial/diophantine.py
33
+ directsd/polynomial/operations.py
34
+ directsd/polynomial/poln.py
35
+ directsd/polynomial/spectral.py
36
+ directsd/polynomial/transforms.py
37
+ directsd/polynomial/utils.py
38
+ directsd/sspace/__init__.py
39
+ directsd/sspace/design.py
40
+ directsd/sspace/plant.py
41
+ directsd/tf/__init__.py
42
+ directsd/tf/interconnect.py
43
+ directsd/zpk/__init__.py
44
+ directsd/zpk/zpk.py
@@ -0,0 +1,20 @@
1
+ numpy>=1.21
2
+ scipy>=1.7
3
+
4
+ [all]
5
+ control>=0.9
6
+ matplotlib>=3.4
7
+ cvxpy>=1.3
8
+
9
+ [control]
10
+ control>=0.9
11
+
12
+ [convex]
13
+ cvxpy>=1.3
14
+
15
+ [dev]
16
+ pytest>=7.0
17
+ matplotlib>=3.4
18
+
19
+ [plot]
20
+ matplotlib>=3.4
@@ -0,0 +1,36 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Roozbeh Dargahi
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ ---
31
+
32
+ This is a Python port of algorithms and function structure from the DirectSD
33
+ MATLAB Toolbox v3.0 (Copyright (c) 1995-2006 Konstantin Polyakov). The
34
+ original MATLAB source is not distributed with this package and is not
35
+ covered by the license above; this license applies only to the Python
36
+ implementation in this repository.