Skip to content

Releases: jamestjsp/controlsys

v0.8.0 — Reduce/equalize bug fixes

15 Apr 23:37

Choose a tag to compare

Bug-fix release addressing three issues in Reduce / MinimalRealization / balancing.

Fixed

  • #26Reduce now short-circuits to a cleanly empty System when the controllability or observability staircase collapses to zero states. Previously it fell through to extractSubmatrix(A, 0, 0, 0, 0) and got back a phantom 1×1 zero A (via #28), producing a 1-state "reduced" system that IsStable reported as marginally unstable.

  • #27equalize() (the Parlett-Reinsch state-space balancing used when Reduce is called with Equalize: true) has been rewritten to match LAPACK DGEBAL with the SLICOT TB01ID state-space extension that includes B rows and C columns in the row/column 1-norms. The previous implementation diverged to Inf on the issue #26 closed loop (A[0,1] = -Inf, B[0,0] = 1e+186 after one pass). Fixed bugs: per-step update was c *= sclfac² instead of linear; r was never updated in the loops; convergence check divided by f; scaling application direction was inverted. Added overflow guards matching the LAPACK sfmin2/sfmax2 bounds.

  • #28extractBlock now returns a true 0×0 &mat.Dense{} for requests with both dimensions zero, instead of the max(rows,1)/max(cols,1) phantom fallback. This eliminates the underlying trigger of #26. Mixed-zero requests (0×N, N×0) still use the old fallback pending a caller audit.

Tests added

  • TestReduceIssue26PIPlantLoop — exact reproducer from #26 (PI + first-order plant closed loop).
  • TestReduceZeroControllableReturnsEmpty / TestReduceZeroObservableReturnsEmpty — B=0 / C=0 edge cases.
  • TestEqualizeImbalanced2x2 — eigenvalue preservation and norm-ratio improvement on a skewed 2×2.
  • TestEqualizeIssue26SystemFiniteReduce(Equalize: true) on the issue #26 system produces finite, stable output.
  • TestExtractBlockZeroDim — 0×0 request returns a 0×0 dense.

Full diff: v0.7.1...v0.8.0

v0.7.1 — HinfSyn fix & numerical hardening

05 Apr 00:17
e8e23de

Choose a tag to compare

Bug Fixes

  • HinfSyn: negate Bk in controller construction (was returning wrong sign)
  • Canon: modal form falls back to Schur for ill-conditioned eigenvectors
  • Input validation: reject improper transfer functions, harden descriptor Riccati and Q PSD checks

Tests

  • 105 new hardening tests from python-control audit

v0.7.0 — FRD performance, CST fixes, and README sync

03 Apr 15:55
50c8f82

Choose a tag to compare

Highlights

  • optimize the FRD stack with contiguous FRD storage, reusable SVD workspace, and sharply lower allocation counts in FRD feedback/series/parallel paths
  • fix canonical-form conjugate-pair handling and include the recent FRDMargin/Lsim correctness fixes that landed after v0.6.0
  • sync the README with the current public API surface, including PID/PID2/Pidtune, FRD workflows, and corrected exported names

v0.6.0 — New synthesis, estimation, and analysis APIs

02 Apr 23:14
048d7f3

Choose a tag to compare

New APIs

System synthesis

Symbol Description
H2Syn(P, nmeas, ncont)*H2SynResult H₂ optimal controller synthesis
HinfSyn(P, nmeas, ncont)*HinfSynResult H∞ controller synthesis
Lqg(sys, Q, R, Qn, Rn, opts)*LqgResult LQG controller (LQR + Kalman filter)
SmithPredictor(controller, model, delay, padeOrder)*System Smith predictor for time-delay systems

State estimation

Symbol Description
NewEKF(model, x0, P0)*EKF Extended Kalman filter
type EKFModel Nonlinear model definition (F, H, FJac, HJac, Q, R)

System identification

Symbol Description
ERA(markov, order, dt)*ERAResult Eigensystem Realization Algorithm

Analysis

Symbol Description
RootLocus(sys, gains)*RootLocusResult Root locus computation
IsStabilizable(A, B, continuous) Stabilizability test
IsDetectable(A, C, continuous) Detectability test
Linearize(model, x0, u0)*System Jacobian linearization of nonlinear models

Lyapunov equations (signature change)

Lyap and DLyap now accept an optional *LyapunovOpts argument (pass nil for default behavior — backward compatible at the call site if opts was not used).

Symbol Description
Lyap(A, Q, opts) Continuous Lyapunov equation
DLyap(A, Q, opts) Discrete Lyapunov equation
NewLyapunovWorkspace(n)*LyapunovWorkspace Pre-allocate solver workspace for repeated calls
type LyapunovOpts Solver options (workspace reuse)

New error sentinels

ErrNotStabilizable, ErrNotDetectable, ErrInvalidPartition, ErrNoFiniteH2Norm, ErrH2DirectFeedthrough, ErrGammaNotAchievable, ErrDescriptorSingular


Bug fixes

  • ERA odd-length panicr := (len(markov)+1)/2 caused out-of-bounds on minimum-valid odd-length input; fixed to len(markov)/2
  • H2Syn nonzero D22 — old path silently produced an incorrect controller; now returns ErrH2DirectFeedthrough before any computation
  • EvalFr ignoring sys.Delay — per-channel delay matrix was not applied; fixed via shared applyIODelayAtS helper
  • LU singularity detection — all 8 lu.Det()==0 guards replaced with lu.Cond()*ε ≥ 1 (LAPACK dgecon), which correctly catches ill-conditioned transforms

Performance

  • FreqResponse / EvalFr — single-point evaluation computes C(sI-A)⁻¹B+D directly; no TransferFunction construction
  • EKF — all scratch matrices pre-allocated in NewEKF, reused across every Predict/Update call
  • Discrete Lyapunov solverblocks, blockStart, buf slices reused via workspace threading

v0.5.0

23 Mar 03:59

Choose a tag to compare

Breaking Changes

  • Lqe, Kalman, Kalmd now accept *RiccatiOpts as final parameter for workspace reuse

Performance Optimizations

Riccati Solvers (Care/Dare)

  • RiccatiWorkspace struct pools ~25 arrays, reducing GC churn by ~96% (1.95 MB/call eliminated)
  • Care_N10: 54,640 → 994 B/op, Dare_N10: 63,784 → 906 B/op

HinfNorm

  • Replace 50x per-frequency EvalFr+SVD loop with single vectorized Sigma() call
  • 43% faster, 95% less memory, 83% fewer allocs

Transfer Functions

  • Pool vPolyRecurrence allocations: 25% fewer allocs per matrix transformation

FeedbackLFT

  • Scoped BLAS workspace eliminates ~48 intermediate allocations (321 → 273 allocs/op)

Balreal / Balred

  • Single unified buffer replaces 17 make() calls (104 → 88 allocs/op)

ControllabilityStaircase

  • Static blockBuf eliminates per-iteration mat.NewDense (126 → 109 allocs/op)

Raw Stride Indexing

  • Replace At()/Set() with direct RawMatrix().Data indexing in transfer.go, frequency.go, feedback_lft.go hot paths

Lqr/Kalman Workspace Propagation

  • Lqe/Kalman/Kalmd proxy RiccatiOpts to inner Care/Dare calls
  • Kalman_N100: 2,213 Ki → 309 Ki B/op (86% reduction)

v0.4.0

22 Mar 21:37

Choose a tag to compare

Breaking Changes

  • System.InternalDelay, System.B2, System.C2, System.D12, System.D21, System.D22 fields replaced by System.LFT *LFTDelay nested struct
    • sys.InternalDelaysys.LFT.Tau
    • sys.B2sys.LFT.B2 (same for C2, D12, D21, D22)
    • nil check: sys.LFT == nil replaces individual field checks

Improvements

  • Add copyStrided/copyBlock matrix copy helpers, eliminating ~100 duplicate inline copy loops
  • Unify duplicate extractBlock/extractSubmatrix functions
  • -3.5% benchmark geomean improvement across 101 benchmarks (up to -17% on delay operations)
  • Net -51 lines of code

Tests & Coverage

  • Add MATLAB delay system tests (Pade, Thiran, c2d, feedback, margins)
  • Add exact MATLAB/python-control expected values to all tests
  • Tighten test tolerances
  • Coverage 83.9% → 85.7%

Other

  • Update README with full API coverage

v0.3.0

22 Mar 00:36

Choose a tag to compare

Performance & Robustness

Simulation hot-path optimizations

  • Replace gonum MulVec/AddVec wrappers with direct blas64.Gemv calls in simulateNoDelay (-22%) and simulateWithInternalDelay (-74%)
  • Fuse A*x + B*u into Gemv(beta=1) accumulation, eliminating intermediate buffers

Frequency response allocation reduction

  • Add TransferFunc.evalInto for allocation-free transfer function evaluation
  • FreqResponse allocs -71% (560→160), Bode allocs -81% (992→192)

Numerical robustness (#347510c)

  • Harden numerical robustness and close test coverage gaps

Simulation performance (#4429669)

  • Batch delayed MIMO simulation to remove per-input replay overhead (#2)

New benchmarks

  • 7 MATLAB-inspired benchmarks: DC motor, Boeing 747 lateral/longitudinal, mass-spring-damper chain, large system (50 states), feedback+simulate pipeline, large MIMO Bode

v0.2.0

20 Mar 14:05

Choose a tag to compare

Bug Fixes

  • Fix DiscretizeZOH silently dropping InternalDelay (#1)

    DiscretizeZOH and Discretize now properly handle systems with internal delays (LFT structure produced by Feedback with delays in the feedback path).

    Two underlying bugs fixed:

    • DiscretizeZOH/Discretize silently discarded the LFT delay structure (B2, C2, D12, D21, D22, InternalDelay), reducing the system to its delay-free rational part — causing S ≈ 1 and T ≈ 0 instead of correct sensitivity functions.
    • discretizeWithInternalDelay copied the continuous-time B2 unchanged instead of computing Bd2 = Γ·B2 via the augmented matrix exponential, making the delayed feedback gain ~20× too large.

v0.1.0

19 Mar 08:08

Choose a tag to compare

Initial release: Go library for linear state-space control systems.