From 856794997ddc6932cfa6828b1b206dd18ae766bd Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Fri, 31 Jan 2025 11:57:05 +0100 Subject: [PATCH 1/4] compress printing for very large statespace models --- lib/ControlSystemsBase/src/types/StateSpace.jl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/ControlSystemsBase/src/types/StateSpace.jl b/lib/ControlSystemsBase/src/types/StateSpace.jl index 2cd66eb09..db762fbbd 100644 --- a/lib/ControlSystemsBase/src/types/StateSpace.jl +++ b/lib/ControlSystemsBase/src/types/StateSpace.jl @@ -564,18 +564,18 @@ end Base.print(io::IO, sys::AbstractStateSpace) = show(io, sys) function Base.show(io::IO, sys::AbstractStateSpace) - # Compose the name vectors - #inputs = format_names(s.inputnames, "u", "?") - #outputs = format_names(s.outputnames, "y", "?") - #println(io, "StateSpace:") println(io, typeof(sys)) + A, B, C, D = ssdata(sys) if nstates(sys) > 0 - #states = format_names(s.statenames, "x", "?") - println(io, "A = \n", _string_mat_with_headers(sys.A)) - println(io, "B = \n", _string_mat_with_headers(sys.B)) - println(io, "C = \n", _string_mat_with_headers(sys.C)) + length(A) < 200 ? println(io, "A = \n", _string_mat_with_headers(A)) : + println(io, "A = $(typeof(A))$(size(A))") + length(B) < 200 ? println(io, "B = \n", _string_mat_with_headers(B)) : + println(io, "B = $(typeof(B))$(size(B))") + length(C) < 200 ? println(io, "C = \n", _string_mat_with_headers(C)) : + println(io, "C = $(typeof(C))$(size(C))") + length(D) < 200 ? println(io, "D = \n", _string_mat_with_headers(D)) : + println(io, "D = ", iszero(D) ? " 0" : "$(typeof(D))$(size(D))") end - println(io, "D = \n", _string_mat_with_headers(sys.D), "\n") # Print sample time if isdiscrete(sys) println(io, "Sample Time: ", sys.Ts, " (seconds)") From ef893cdacf6384b3afe888d334329ff6a5d67309 Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Thu, 14 May 2026 15:16:00 +0200 Subject: [PATCH 2/4] Tests for compressed StateSpace printing; fix D for 0-state systems Move the D-matrix println back outside `if nstates(sys) > 0` so pure feedthrough systems (state dimension 0) still show D. Add tests covering the new compressed format for large A/B/C/D, the zero-D shortcut, and the mixed case (large A with small B/C/D). Update the four existing print test strings to drop the trailing blank line that the previous `println(..., "\n")` produced. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/types/StateSpace.jl | 4 ++-- .../test/test_statespace.jl | 22 +++++++++++++++---- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/lib/ControlSystemsBase/src/types/StateSpace.jl b/lib/ControlSystemsBase/src/types/StateSpace.jl index db762fbbd..54d03dd38 100644 --- a/lib/ControlSystemsBase/src/types/StateSpace.jl +++ b/lib/ControlSystemsBase/src/types/StateSpace.jl @@ -573,9 +573,9 @@ function Base.show(io::IO, sys::AbstractStateSpace) println(io, "B = $(typeof(B))$(size(B))") length(C) < 200 ? println(io, "C = \n", _string_mat_with_headers(C)) : println(io, "C = $(typeof(C))$(size(C))") - length(D) < 200 ? println(io, "D = \n", _string_mat_with_headers(D)) : - println(io, "D = ", iszero(D) ? " 0" : "$(typeof(D))$(size(D))") end + length(D) < 200 ? println(io, "D = \n", _string_mat_with_headers(D)) : + println(io, "D = ", iszero(D) ? " 0" : "$(typeof(D))$(size(D))") # Print sample time if isdiscrete(sys) println(io, "Sample Time: ", sys.Ts, " (seconds)") diff --git a/lib/ControlSystemsBase/test/test_statespace.jl b/lib/ControlSystemsBase/test/test_statespace.jl index 0de7ed37e..602f78f10 100644 --- a/lib/ControlSystemsBase/test/test_statespace.jl +++ b/lib/ControlSystemsBase/test/test_statespace.jl @@ -217,10 +217,24 @@ # Printing if SS <: StateSpace - @test sprint(show, C_222) == "StateSpace{Continuous, Int64}\nA = \n -5 -3\n 2 -9\nB = \n 1 0\n 0 2\nC = \n 1 0\n 0 1\nD = \n 0 0\n 0 0\n\nContinuous-time state-space model" - @test sprint(show, C_022) == "StateSpace{Continuous, Float64}\nD = \n 4.0 0.0\n 0.0 4.0\n\nContinuous-time state-space model" - @test sprint(show, D_022) == "StateSpace{Discrete{Float64}, Float64}\nD = \n 4.0 0.0\n 0.0 4.0\n\nSample Time: 0.005 (seconds)\nDiscrete-time state-space model" - @test sprint(show, D_222) == "StateSpace{Discrete{Float64}, Float64}\nA = \n 0.2 -0.8\n -0.8 0.07\nB = \n 1.0 0.0\n 0.0 2.0\nC = \n 1.0 0.0\n 0.0 1.0\nD = \n 0.0 0.0\n 0.0 0.0\n\nSample Time: 0.005 (seconds)\nDiscrete-time state-space model" + @test sprint(show, C_222) == "StateSpace{Continuous, Int64}\nA = \n -5 -3\n 2 -9\nB = \n 1 0\n 0 2\nC = \n 1 0\n 0 1\nD = \n 0 0\n 0 0\nContinuous-time state-space model" + @test sprint(show, C_022) == "StateSpace{Continuous, Float64}\nD = \n 4.0 0.0\n 0.0 4.0\nContinuous-time state-space model" + @test sprint(show, D_022) == "StateSpace{Discrete{Float64}, Float64}\nD = \n 4.0 0.0\n 0.0 4.0\nSample Time: 0.005 (seconds)\nDiscrete-time state-space model" + @test sprint(show, D_222) == "StateSpace{Discrete{Float64}, Float64}\nA = \n 0.2 -0.8\n -0.8 0.07\nB = \n 1.0 0.0\n 0.0 2.0\nC = \n 1.0 0.0\n 0.0 1.0\nD = \n 0.0 0.0\n 0.0 0.0\nSample Time: 0.005 (seconds)\nDiscrete-time state-space model" + + # Large systems use a compressed format + G_large_zeroD = ss(zeros(15,15), zeros(15,15), zeros(15,15), zeros(15,15)) + @test sprint(show, G_large_zeroD) == "StateSpace{Continuous, Float64}\nA = Matrix{Float64}(15, 15)\nB = Matrix{Float64}(15, 15)\nC = Matrix{Float64}(15, 15)\nD = 0\nContinuous-time state-space model" + + G_large_nonzeroD = ss(zeros(15,15), zeros(15,15), zeros(15,15), ones(15,15)) + @test sprint(show, G_large_nonzeroD) == "StateSpace{Continuous, Float64}\nA = Matrix{Float64}(15, 15)\nB = Matrix{Float64}(15, 15)\nC = Matrix{Float64}(15, 15)\nD = Matrix{Float64}(15, 15)\nContinuous-time state-space model" + + G_large_A = ss(zeros(15,15), zeros(15,1), zeros(1,15), 0) + out_large_A = sprint(show, G_large_A) + @test occursin("A = Matrix{Float64}(15, 15)", out_large_A) + @test occursin("B = \n", out_large_A) + @test occursin("C = \n", out_large_A) + @test occursin("D = \n", out_large_A) end G = ssrand(1,2,3) From cdb341878d13e7a842b4a2caa0f33fcfc3cb77d1 Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Thu, 14 May 2026 15:58:11 +0200 Subject: [PATCH 3/4] Update DelayLti/HammersteinWiener print tests for new D format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same trailing-blank-line drop as the existing StateSpace print tests — these composite types embed the StateSpace show output. Co-Authored-By: Claude Opus 4.7 (1M context) --- lib/ControlSystemsBase/test/test_delayed_systems.jl | 2 +- lib/ControlSystemsBase/test/test_hammerstein_wiener.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ControlSystemsBase/test/test_delayed_systems.jl b/lib/ControlSystemsBase/test/test_delayed_systems.jl index 2213fa8fe..396eb6ecf 100644 --- a/lib/ControlSystemsBase/test/test_delayed_systems.jl +++ b/lib/ControlSystemsBase/test/test_delayed_systems.jl @@ -14,7 +14,7 @@ using ControlSystemsBase @test_throws ErrorException delay(1, 0.3) @test delay(1, 1) == ss(zeros(1,1), ones(1,1), ones(1,1), zeros(1,1), 1) -@test sprint(show, ss(1,1,1,1)*delay(1.0)) == "DelayLtiSystem{Float64, Float64}\n\nP: StateSpace{Continuous, Float64}\nA = \n 1.0\nB = \n 0.0 1.0\nC = \n 1.0\n 0.0\nD = \n 0.0 1.0\n 1.0 0.0\n\nContinuous-time state-space model\n\nDelays: [1.0]" +@test sprint(show, ss(1,1,1,1)*delay(1.0)) == "DelayLtiSystem{Float64, Float64}\n\nP: StateSpace{Continuous, Float64}\nA = \n 1.0\nB = \n 0.0 1.0\nC = \n 1.0\n 0.0\nD = \n 0.0 1.0\n 1.0 0.0\nContinuous-time state-space model\n\nDelays: [1.0]" # Extremely basic tests @test freqresp(delay(1), ω) ≈ reshape(exp.(-im*ω), 1, 1, length(ω)) rtol=1e-15 diff --git a/lib/ControlSystemsBase/test/test_hammerstein_wiener.jl b/lib/ControlSystemsBase/test/test_hammerstein_wiener.jl index 035e74056..c562cd6ab 100644 --- a/lib/ControlSystemsBase/test/test_hammerstein_wiener.jl +++ b/lib/ControlSystemsBase/test/test_hammerstein_wiener.jl @@ -14,7 +14,7 @@ using Test @test typeof(promote(nonlinearity(abs2), ss(1))[1]) == HammersteinWienerSystem{Float64} @test sprint(show, ss(1, 1, 1, 1) * nonlinearity(abs2)) == - "HammersteinWienerSystem{Float64}\n\nP: StateSpace{Continuous, Float64}\nA = \n 1.0\nB = \n 0.0 1.0\nC = \n 1.0\n 0.0\nD = \n 0.0 1.0\n 1.0 0.0\n\nContinuous-time state-space model\n\nNonlinearities: Function[abs2]" + "HammersteinWienerSystem{Float64}\n\nP: StateSpace{Continuous, Float64}\nA = \n 1.0\nB = \n 0.0 1.0\nC = \n 1.0\n 0.0\nD = \n 0.0 1.0\n 1.0 0.0\nContinuous-time state-space model\n\nNonlinearities: Function[abs2]" P1 = HammersteinWienerSystem(ss(-1.0, 1, 1, 0)) From a9803985f52de4049f521b185c033ec90eecf75d Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Thu, 14 May 2026 16:10:07 +0200 Subject: [PATCH 4/4] Retain trailing blank line after D for backwards compatibility Restore the trailing "\n" argument on the D println so the blank line between D and the time-domain line is preserved (downstream packages may depend on this output format). Both the small-matrix and the new compressed branches now emit the blank line. Test expectations for the 4 existing StateSpace tests, the 2 composite-type tests, and the 2 new compressed-matrix tests updated accordingly. Co-Authored-By: Claude Opus 4.7 (1M context) --- lib/ControlSystemsBase/src/types/StateSpace.jl | 4 ++-- lib/ControlSystemsBase/test/test_delayed_systems.jl | 2 +- .../test/test_hammerstein_wiener.jl | 2 +- lib/ControlSystemsBase/test/test_statespace.jl | 12 ++++++------ 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/ControlSystemsBase/src/types/StateSpace.jl b/lib/ControlSystemsBase/src/types/StateSpace.jl index 54d03dd38..45fae041a 100644 --- a/lib/ControlSystemsBase/src/types/StateSpace.jl +++ b/lib/ControlSystemsBase/src/types/StateSpace.jl @@ -574,8 +574,8 @@ function Base.show(io::IO, sys::AbstractStateSpace) length(C) < 200 ? println(io, "C = \n", _string_mat_with_headers(C)) : println(io, "C = $(typeof(C))$(size(C))") end - length(D) < 200 ? println(io, "D = \n", _string_mat_with_headers(D)) : - println(io, "D = ", iszero(D) ? " 0" : "$(typeof(D))$(size(D))") + length(D) < 200 ? println(io, "D = \n", _string_mat_with_headers(D), "\n") : + println(io, "D = ", iszero(D) ? " 0" : "$(typeof(D))$(size(D))", "\n") # Print sample time if isdiscrete(sys) println(io, "Sample Time: ", sys.Ts, " (seconds)") diff --git a/lib/ControlSystemsBase/test/test_delayed_systems.jl b/lib/ControlSystemsBase/test/test_delayed_systems.jl index 396eb6ecf..2213fa8fe 100644 --- a/lib/ControlSystemsBase/test/test_delayed_systems.jl +++ b/lib/ControlSystemsBase/test/test_delayed_systems.jl @@ -14,7 +14,7 @@ using ControlSystemsBase @test_throws ErrorException delay(1, 0.3) @test delay(1, 1) == ss(zeros(1,1), ones(1,1), ones(1,1), zeros(1,1), 1) -@test sprint(show, ss(1,1,1,1)*delay(1.0)) == "DelayLtiSystem{Float64, Float64}\n\nP: StateSpace{Continuous, Float64}\nA = \n 1.0\nB = \n 0.0 1.0\nC = \n 1.0\n 0.0\nD = \n 0.0 1.0\n 1.0 0.0\nContinuous-time state-space model\n\nDelays: [1.0]" +@test sprint(show, ss(1,1,1,1)*delay(1.0)) == "DelayLtiSystem{Float64, Float64}\n\nP: StateSpace{Continuous, Float64}\nA = \n 1.0\nB = \n 0.0 1.0\nC = \n 1.0\n 0.0\nD = \n 0.0 1.0\n 1.0 0.0\n\nContinuous-time state-space model\n\nDelays: [1.0]" # Extremely basic tests @test freqresp(delay(1), ω) ≈ reshape(exp.(-im*ω), 1, 1, length(ω)) rtol=1e-15 diff --git a/lib/ControlSystemsBase/test/test_hammerstein_wiener.jl b/lib/ControlSystemsBase/test/test_hammerstein_wiener.jl index c562cd6ab..035e74056 100644 --- a/lib/ControlSystemsBase/test/test_hammerstein_wiener.jl +++ b/lib/ControlSystemsBase/test/test_hammerstein_wiener.jl @@ -14,7 +14,7 @@ using Test @test typeof(promote(nonlinearity(abs2), ss(1))[1]) == HammersteinWienerSystem{Float64} @test sprint(show, ss(1, 1, 1, 1) * nonlinearity(abs2)) == - "HammersteinWienerSystem{Float64}\n\nP: StateSpace{Continuous, Float64}\nA = \n 1.0\nB = \n 0.0 1.0\nC = \n 1.0\n 0.0\nD = \n 0.0 1.0\n 1.0 0.0\nContinuous-time state-space model\n\nNonlinearities: Function[abs2]" + "HammersteinWienerSystem{Float64}\n\nP: StateSpace{Continuous, Float64}\nA = \n 1.0\nB = \n 0.0 1.0\nC = \n 1.0\n 0.0\nD = \n 0.0 1.0\n 1.0 0.0\n\nContinuous-time state-space model\n\nNonlinearities: Function[abs2]" P1 = HammersteinWienerSystem(ss(-1.0, 1, 1, 0)) diff --git a/lib/ControlSystemsBase/test/test_statespace.jl b/lib/ControlSystemsBase/test/test_statespace.jl index 602f78f10..4938923f0 100644 --- a/lib/ControlSystemsBase/test/test_statespace.jl +++ b/lib/ControlSystemsBase/test/test_statespace.jl @@ -217,17 +217,17 @@ # Printing if SS <: StateSpace - @test sprint(show, C_222) == "StateSpace{Continuous, Int64}\nA = \n -5 -3\n 2 -9\nB = \n 1 0\n 0 2\nC = \n 1 0\n 0 1\nD = \n 0 0\n 0 0\nContinuous-time state-space model" - @test sprint(show, C_022) == "StateSpace{Continuous, Float64}\nD = \n 4.0 0.0\n 0.0 4.0\nContinuous-time state-space model" - @test sprint(show, D_022) == "StateSpace{Discrete{Float64}, Float64}\nD = \n 4.0 0.0\n 0.0 4.0\nSample Time: 0.005 (seconds)\nDiscrete-time state-space model" - @test sprint(show, D_222) == "StateSpace{Discrete{Float64}, Float64}\nA = \n 0.2 -0.8\n -0.8 0.07\nB = \n 1.0 0.0\n 0.0 2.0\nC = \n 1.0 0.0\n 0.0 1.0\nD = \n 0.0 0.0\n 0.0 0.0\nSample Time: 0.005 (seconds)\nDiscrete-time state-space model" + @test sprint(show, C_222) == "StateSpace{Continuous, Int64}\nA = \n -5 -3\n 2 -9\nB = \n 1 0\n 0 2\nC = \n 1 0\n 0 1\nD = \n 0 0\n 0 0\n\nContinuous-time state-space model" + @test sprint(show, C_022) == "StateSpace{Continuous, Float64}\nD = \n 4.0 0.0\n 0.0 4.0\n\nContinuous-time state-space model" + @test sprint(show, D_022) == "StateSpace{Discrete{Float64}, Float64}\nD = \n 4.0 0.0\n 0.0 4.0\n\nSample Time: 0.005 (seconds)\nDiscrete-time state-space model" + @test sprint(show, D_222) == "StateSpace{Discrete{Float64}, Float64}\nA = \n 0.2 -0.8\n -0.8 0.07\nB = \n 1.0 0.0\n 0.0 2.0\nC = \n 1.0 0.0\n 0.0 1.0\nD = \n 0.0 0.0\n 0.0 0.0\n\nSample Time: 0.005 (seconds)\nDiscrete-time state-space model" # Large systems use a compressed format G_large_zeroD = ss(zeros(15,15), zeros(15,15), zeros(15,15), zeros(15,15)) - @test sprint(show, G_large_zeroD) == "StateSpace{Continuous, Float64}\nA = Matrix{Float64}(15, 15)\nB = Matrix{Float64}(15, 15)\nC = Matrix{Float64}(15, 15)\nD = 0\nContinuous-time state-space model" + @test sprint(show, G_large_zeroD) == "StateSpace{Continuous, Float64}\nA = Matrix{Float64}(15, 15)\nB = Matrix{Float64}(15, 15)\nC = Matrix{Float64}(15, 15)\nD = 0\n\nContinuous-time state-space model" G_large_nonzeroD = ss(zeros(15,15), zeros(15,15), zeros(15,15), ones(15,15)) - @test sprint(show, G_large_nonzeroD) == "StateSpace{Continuous, Float64}\nA = Matrix{Float64}(15, 15)\nB = Matrix{Float64}(15, 15)\nC = Matrix{Float64}(15, 15)\nD = Matrix{Float64}(15, 15)\nContinuous-time state-space model" + @test sprint(show, G_large_nonzeroD) == "StateSpace{Continuous, Float64}\nA = Matrix{Float64}(15, 15)\nB = Matrix{Float64}(15, 15)\nC = Matrix{Float64}(15, 15)\nD = Matrix{Float64}(15, 15)\n\nContinuous-time state-space model" G_large_A = ss(zeros(15,15), zeros(15,1), zeros(1,15), 0) out_large_A = sprint(show, G_large_A)