Generate lifted form matrices for discrete state space system
70 ビュー (過去 30 日間)
古いコメントを表示
I have a discrete recursive state space equation
x_{k+1}=A_dx_k+B_du_k
y_k=Cx_k+Du_k
where x0 is nx x 1, y is M x ny, u is N x nu.
I want to work with a single equation where y =f([g(theta) = A B C D] , x0 , u). explicit input-output form.
The program that I wrote for this already works and leverages a few tricks, but I'm sure that this is a very well studied problem in Control theory. I want to have a function that:
- Names everything correctly (like Toeplitz matrix, Markov Parameter, Observability, convolution kernel)
- Is as fast as possible
- Can work as a function handle to preserve memory (sometimes my input data N is 100k long and that is not kind to my computer when Btheta can just be a matvec)
Ultimately I understand this program very well but not well enough that I think this is a good implementation. There must be some toolbox or app I am missing.
function [B_theta_c, y_theta_c, uc, I, A_theta, Hd, B_theta, yt] = toeplitzBtheta_FOCUSS_PreCalcs(y, theta, u, x0, sys_desc, choice, dat)
% Extract dimensions
[M, ny] = size(y); % M = number of output samples, ny = number of outputs
[N, nu] = size(u); % N = number of input samples, nu = number of inputs
nx = length(x0); % nx = number of states
[Ad, Bd, Cd, Dd, d] = StateSpace(theta, sys_desc, dat, nx, ny, nu);
% Pre-indexing for downsampling
indices = calculateDownsamplingIndices(N, M);
CAk = powers_CAk(Ad, Cd, N+1); % C*A^k powers are common to all three precalculations
CAk_needed = CAk(:, :, indices);
A_theta = reshape(permute(pagemtimes(CAk_needed, x0), [3 1 2]), M, ny);
Hd_step = reshape(permute(pagemtimes(CAk_needed, d), [3 1 2]), M, ny);
Hd = cumsum(Hd_step, 1);
B_theta_col = pagemtimes(CAk, Bd); % ny x nu x N+1
B_theta = zeros(ny*M, nu*N);
% Build B_theta row by row
% Fill in each row at once by dropping in the impulse array slice with the right delay
for i = 1:M
ii = indices(i); % downsampled time for this output row
BT_row_idx = (0:ny-1)*M + i; % rows for all ny outputs at time i
% --- Valid-lag mask (skip zeros): determine which input columns can affect output at time ii
t_vec = ii - (1:N); % 1×N
j_valid = find((t_vec > 0) & (t_vec <= N));
tj = t_vec(j_valid); % 1×K, K can be 0
% Gather the (ny×nu)×K impulse slices for this row; empty is fine
H_sel = B_theta_col(:, :, tj); % ny × nu × K
% --- Drop the entire ny×K block into place for each input channel (vectorized write)
for c = 1:nu
BT_col_idx = (c-1)*N + j_valid;
vals = reshape(H_sel(:, c, :), ny, []); % ny × K (or ny×0)
B_theta(BT_row_idx, BT_col_idx) = vals;
end
% % --- Drop the entire ny×K block into place for each input channel (vectorized write)
% row_block = zeros(ny, nu*N);
% cols = zeros(1, nu*numel(j_valid));
% p = 1;
% for k = 1:numel(j_valid)
% cols(p:p+nu-1) = (o:nu-1)*N + j_valid(k);
% p = p + nu;
% end
% row_block(:, cols) = H_sel(:,:);
% B_theta(BT_row_idx, :) = row_block; % assign the whole row block at once
% end
end
% Get the corresponding B_theta matrix for the chosen component
start_col = (choice-1)*N + 1;
end_col = choice*N;
B_theta_c = B_theta(:, start_col:end_col);
%Calculate the contribution from all OTHER components
uc = u(:, choice); % extract chosen signal
% uc = u(choice, :)';
B_theta_u_others = B_theta * u(:) - B_theta_c * u(:, choice);
yt = y - A_theta - Hd;
y_theta_c = yt(:) - B_theta_u_others;
I = speye(size(B_theta_c, 1));
end
function CAk = powers_CAk(Ad, Cd, N)
% powers_CAk - Precompute C*A^k matrices efficiently
%
% This function precomputes C*A^k for k = 0 to N-1 using efficient algorithms.
% For 2x2 matrices, it uses the Cayley-Hamilton recurrence relation.
% For larger matrices, it uses direct matrix multiplication.
% Get matrix dimensions
[nx, ~] = size(Ad);
ny = size(Cd, 1);
% Initialize output matrix
CAk = zeros(ny, nx, N);
% Set first matrix: C*A^0 = C
CAk(:, :, 1) = Cd;
CAk(:, :, 2) = Cd * Ad;
% For 2x2 matrices, use Cayley-Hamilton recurrence for efficiency
% This avoids computing matrix powers directly
if nx == 2
% Get trace and determinant for recurrence relation
s1 = trace(Ad); % s1 = λ1 + λ2
s2 = det(Ad); % s2 = λ1 * λ2
% Use recurrence: A^k = s1*A^(k-1) - s2*A^(k-2)
for k = 3:N
CAk(:, :, k) = s1 * CAk(:, :, k-1) - s2 * CAk(:, :, k-2);
end
else
% For larger matrices, use direct multiplication
% A^k = A^(k-1) * A
for k = 3:N
CAk(:, :, k) = CAk(:, :, k-1) * Ad;
end
end
end
10 件のコメント
Torsten
2025 年 10 月 9 日 20:25
編集済み: Torsten
2025 年 10 月 9 日 20:48
Here is the computation of A_theta and B_theta for the example from above:
rng("default")
A = diag([0.7,0.5]); B = rand(2,2); C = rand(2,2); D = zeros(2,2);
x0 = [1;2];
u = rand(2,5); % five output samples
C5 = repmat({C},1,5);
D5 = repmat({D},1,5);
A_theta = blkdiag(C5{:})*[eye(size(A));A;A^2;A^3;A^4];
B_theta = blkdiag(C5{:})*[zeros(2,10);[B,zeros(2,8)];[A^1*B,B,zeros(2,6)];[A^2*B,A^1*B,B,zeros(2,4)];[A^3*B,A^2*B,A^1*B,B,zeros(2,2)]] + ...
blkdiag(D5{:});
y = A_theta*x0 + B_theta*u(:)
Do you see the pattern and can you transfer it to the general case ?
Can you deduce an efficient code to compute the matrices involved (A,A^2,...,A^4,A*B,A^2*B,A^3*B)?
回答 (0 件)
参考
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!