LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
ode_eliminate_immediate.m
1function [all_jumps_red, rateBase_red, eventIdx_red, state_map] = ...
2 ode_eliminate_immediate(all_jumps, rateBase, eventIdx, sn, options)
3% [ALL_JUMPS_RED, RATEBASE_RED, EVENTIDX_RED, STATE_MAP] = ...
4% ODE_ELIMINATE_IMMEDIATE(ALL_JUMPS, RATEBASE, EVENTIDX, SN, OPTIONS)
5%
6% Eliminate immediate transitions from Fluid ODE system using stochastic
7% complementation to reduce stiffness
8%
9% Input:
10% all_jumps: [n_states x n_transitions] matrix of state changes
11% rateBase: [n_transitions x 1] vector of base rates
12% eventIdx: [n_transitions x 1] vector of state indices for rate gating
13% sn: Model structure
14% options: Solver options
15%
16% Output:
17% all_jumps_red: Reduced jump matrix
18% rateBase_red: Reduced rate vector
19% eventIdx_red: Reduced event index vector
20% state_map: Mapping from reduced to original state indices
21%
22% Copyright (c) 2012-2026, Imperial College London
23% All rights reserved.
24
25% Get immediate detection threshold using GlobalConstants.Immediate with tolerance
26% A transition is considered immediate if its rate is within 1% of GlobalConstants.Immediate
27imm_ref = GlobalConstants.Immediate;
28rel_tol = 0.01;
29imm_tol = imm_ref * (1 - rel_tol);
30
31% Identify immediate transitions (rates close to or above GlobalConstants.Immediate)
32imm_idx = find(rateBase >= imm_tol);
33
34% If no immediate transitions, return unchanged
35if isempty(imm_idx)
36 all_jumps_red = all_jumps;
37 rateBase_red = rateBase;
38 eventIdx_red = eventIdx;
39 state_map = 1:size(all_jumps, 1);
40 return;
41end
42
43% Report detected immediate transitions
44if isfield(options, 'verbose') && options.verbose > 0
45 line_printf(sprintf('Eliminating %d immediate transitions (out of %d total)\n', ...
46 length(imm_idx), length(rateBase)));
47end
48
49try
50 % Apply stochastic complement elimination
51 [all_jumps_red, rateBase_red, eventIdx_red, state_map] = ...
52 eliminate_via_stochcomp(all_jumps, rateBase, eventIdx, imm_idx, options);
53
54catch ME
55 % Elimination failed - fall back to original system
56 warning('LINE:Fluid:EliminationFailed', ...
57 'Immediate transition elimination failed: %s. Using original system.', ME.message);
58 all_jumps_red = all_jumps;
59 rateBase_red = rateBase;
60 eventIdx_red = eventIdx;
61 state_map = 1:size(all_jumps, 1);
62end
63
64end % ode_eliminate_immediate
65
66
67function [all_jumps_red, rateBase_red, eventIdx_red, state_map] = ...
68 eliminate_via_stochcomp(all_jumps, rateBase, eventIdx, imm_idx, options)
69% Eliminate immediate transitions using stochastic complementation
70
71n_states = size(all_jumps, 1);
72n_transitions = length(rateBase);
73
74% Step 1: Construct infinitesimal generator matrix from jumps and rates
75W = sparse(n_states, n_states);
76
77for t = 1:n_transitions
78 src_state = eventIdx(t);
79 jump = all_jumps(:, t);
80
81 % Find destination state (where jump > 0)
82 dst_states = find(jump > 0);
83
84 if ~isempty(dst_states)
85 for d = 1:length(dst_states)
86 dst_state = dst_states(d);
87 % Add transition rate from src to dst
88 W(src_state, dst_state) = W(src_state, dst_state) + rateBase(t) * jump(dst_state);
89 end
90 end
91end
92
93% Make W a proper generator (row sums = 0)
94W = W - diag(sum(W, 2));
95
96% Step 2: Identify immediate vs timed states
97% Immediate states are those that have outgoing immediate transitions
98imm_states = unique(eventIdx(imm_idx));
99timed_states = setdiff(1:n_states, imm_states);
100
101% Check that we have enough timed states remaining
102if isempty(timed_states) || length(timed_states) <= 1
103 % Too few timed states - elimination would produce a trivial system.
104 % Fall back to the original system without elimination.
105 all_jumps_red = all_jumps;
106 rateBase_red = rateBase;
107 eventIdx_red = eventIdx;
108 state_map = 1:n_states;
109 return;
110end
111
112% Step 3: Apply stochastic complement
113[W_red, ~, ~, ~, Q22, ~] = ctmc_stochcomp(W, timed_states);
114
115% Check conditioning of Q22
116if rcond(full(Q22)) < 1e-12
117 warning('LINE:Fluid:IllConditioned', ...
118 'Immediate transition matrix ill-conditioned (rcond = %g)', rcond(full(Q22)));
119end
120
121% Step 4: Convert reduced generator back to jump/rate representation
122[all_jumps_red_local, rateBase_red, eventIdx_red_local] = generator_to_jumps(W_red);
123
124% Step 5: Set up state mapping and expand to original state space
125state_map = timed_states;
126
127% Expand jump matrix and eventIdx to original state space dimension
128% This ensures compatibility with ode_rates_closing which uses original q_indices
129all_jumps_red = zeros(n_states, length(rateBase_red));
130for t = 1:length(rateBase_red)
131 jump_local = all_jumps_red_local(:, t);
132 % Map local indices to original indices
133 for s = 1:length(state_map)
134 if jump_local(s) ~= 0
135 all_jumps_red(state_map(s), t) = jump_local(s);
136 end
137 end
138end
139
140% Map eventIdx from reduced to original state space
141eventIdx_red = state_map(eventIdx_red_local);
142
143% Report reduction
144if isfield(options, 'verbose') && options.verbose > 0
145 reduction_pct = 100 * (1 - length(timed_states)/n_states);
146 line_printf(sprintf('State space reduced from %d to %d states (%.1f%% reduction)\n', ...
147 n_states, length(timed_states), reduction_pct));
148end
149
150end % eliminate_via_stochcomp
151
152
153function x_full = ode_expand_state(x_reduced, state_map, n_original)
154% X_FULL = ODE_EXPAND_STATE(X_REDUCED, STATE_MAP, N_ORIGINAL)
155%
156% Expand reduced state vector back to original dimension
157%
158% Input:
159% x_reduced: [n_reduced x 1] reduced state vector
160% state_map: [n_reduced x 1] mapping from reduced to original indices
161% n_original: original state space dimension
162%
163% Output:
164% x_full: [n_original x 1] full state vector
165% Immediate states (not in state_map) are set to 0
166
167x_full = zeros(n_original, 1);
168x_full(state_map) = x_reduced;
169
170end % ode_expand_state