02D. Stability
Mingyang Lu
01/07/2024
Stability of steady states
Consider
. We first plot the first and second terms of the derivative function separately. % All X values to be sampled
plot(X_all, ones(size(X_all)) * g, 'k', 'LineWidth', 1.5, 'DisplayName', 'g');
plot(X_all, k * X_all, 'r', 'LineWidth', 1.5, 'DisplayName', 'kX');
% Set axis labels and limits
legend('Location', 'southeast');
The intersect of the two curves represents the steady state. It's a stable steady state, when
. Now, let us consider the circuit with a self-inhibiting gene, whose dynamics is governed by
,where
.
is the inhibitory Hill function with expression
.% Inhibitory Hill function
% X_th: Hill threshold, n: Hill coefficient
hill_inh = @(X) 1 ./ (1 + (X / X_th).^n);
% All X values to be sampled
% Plot the first term g(X)
plot(X_all, g_0 + g_1 * hill_inh(X_all), 'k', 'LineWidth', 1.5, 'DisplayName', 'g(X)');
% Plot the second term kX
plot(X_all, k * X_all, 'r', 'LineWidth', 1.5, 'DisplayName', 'kX');
% Set axis labels and limits
legend('Location', 'northeast');
Let us also compute
. Note that we use vector operations for high efficiency. % All X values to be sampled
dfdX = cal_dfdx(X_all, dt, @f_1g_self_inh, 10, 60, 200, 4, 0.1);
plot(X_all, dfdX, 'k', 'LineWidth', 1.5, 'DisplayName', 'df/dX');
% Add horizontal line at y=0
plot([0, 600], [0, 0], '--r', 'LineWidth', 1.5, 'DisplayName', '0');
% Set axis labels and limits
legend('Location', 'Best');
In this case,
at the steady state X (around 250 nM). Thus, the steady state is stable. Multi-stability
The third example is the circuit with a self-activating gene, whose dynamics is governed by
,where
.
is the excitatory Hill function with expression Again, we plot
and
. In the plot below, we vary the value of k and fix the rest of the parameters. % Excitatory Hill function
% X_th: Hill threshold, n: Hill coefficient
hill_ex = @(X, X_th, n) (X / X_th).^n ./ (1 + (X / X_th).^n);
% All X values to be sampled
% Plot the first term g(X)
plot(X_all, g0 + g1 * hill_ex(X_all, X0, n), 'k', 'LineWidth', 1.5, 'DisplayName', 'g(X)');
% Plot the second term kX for different values of k
plot(X_all, 0.1 * X_all, 'r', 'LineWidth', 1.5, 'DisplayName', 'kX (k = 0.1)');
plot(X_all, 0.15 * X_all, 'g', 'LineWidth', 1.5, 'DisplayName', 'kX (k = 0.15)');
plot(X_all, 0.2 * X_all, 'b', 'LineWidth', 1.5, 'DisplayName', 'kX (k = 0.2)');
% Set axis labels and limits
legend('Location', 'northwest');
When
or
, the circuit has only one steady state. When
, the circuit has three steady states. Two of these steady states are stable, and the other one is unstable. This is called bistability. We can simulate the ODE with different initial conditions to see that time trajectories converge to two steady states. % Time sequence for which output is needed
% All initial conditions, each leads to a different simulation
% ODE simulation per initial condition
legend_text = cell(1, numel(X0_all));
[t, y] = ode45(@(t, y) dy_deSolve_1g_self_act(t, y, g0, g1, X_th, n, k), [0, 80], X0);
plot(t, y, 'DisplayName', ['X0 = ', num2str(X0)]);
legend_text{i} = ['X0 = ', num2str(X0)];
legend('Location', 'northeast');
lgd.Title.String = 'Initial condition';
Again, we can also learn the stability of steady states from the
curve. % All X values to be sampled
dfdX = cal_dfdx(X_all, dt, @(X) f_1g_self_act(X, g0, g1, X_th, n, k));
plot(X_all, dfdX, 'k', 'LineWidth', 1, 'DisplayName', 'df/dX');
yline(0, '--', 'Color', 'red', 'DisplayName', '0');
% Set axis labels and limits
legend('show', 'Location', 'best');
Effective potential
For an ODE
, the potential function can be defined as We set
. A common numerical method to integrate a function is by the trapezoidal rule. We got
.% Calculate effective potential
results_1 = cal_int(0, 500, 0.1, @(X) f_1g_self_act(X, g0, g1, X_th, n, k));
% Plot effective potential
plot(results_1(:, 1), results_1(:, 2), 'k', 'LineWidth', 1);
ylabel('Effective Potential');
Same numerical integration, but this time with vectorization.
% Calculate effective potential with vectorized approach
results_2 = cal_int_vector(0, 500, 0.1, @(X) f_1g_self_act(X, g0, g1, X_th, n, k));
% Plot effective potential
plot(results_2(:, 1), results_2(:, 2), 'k', 'LineWidth', 1);
ylabel('Effective Potential');
Benchmarking:
cal_int(0, 500, 0.1, @f_1g_self_act, 10, 45, 200, 4, 0.15);
toc;
Elapsed time is 0.005923 seconds.
% Benchmarking cal_int_vector
cal_int_vector(0, 500, 0.1, @f_1g_self_act, 10, 45, 200, 4, 0.15);
toc;
Elapsed time is 0.002737 seconds.
Finally, we show the effective potential for
. % Calculate effective potential with vectorized approach
results_2 = cal_int_vector(0, 500, 0.1, @(X) f_1g_self_act(X, g0, g1, X_th, n, k));
% Plot effective potential
plot(results_2(:, 1), results_2(:, 2), 'k', 'LineWidth', 1);
ylabel('Effective Potential');
And the effective potential for
. % Calculate effective potential with vectorized approach
results_2 = cal_int_vector(0, 500, 0.1, @(X) f_1g_self_act(X, g0, g1, X_th, n, k));
% Plot effective potential
plot(results_2(:, 1), results_2(:, 2), 'k', 'LineWidth', 1);
ylabel('Effective Potential');
Define MATLAB functions
1. Caculate f(x) for the case of one gene with self-inhibition.
function result = f_1g_self_inh(X, g0, g1, X_th, n, k)
hill_inh = 1 ./ (1 + (X / X_th).^n);
result = g0 + g1 * hill_inh - k * X;
2. Calculate df(X)/dt.
function dfdX = cal_dfdx(X_all, dt, derivs, varargin)
% X_all is a vector of all X values to be sampled
% derivs takes the name of the derivatives function
% varargin to take model parameters
% f(X) for all X values in a vector
f_all = derivs(X_all, varargin{:})';
% f(X+dx), achieved by shifting the vector by one to the left
f_all_shift_by_plus_one = [f_all(2:end); NaN];
% f(X-dx), achieved by shifting the vector by one to the right
f_all_shift_by_minus_one = [NaN; f_all(1:end-1)];
% df/dX = (f(X+dx) - f(X-dx))/2/dt
dfdX = (f_all_shift_by_plus_one - f_all_shift_by_minus_one) / (2 * dt);
3. Modified derivative function for MATLAB ode solver
function dydt = dy_deSolve_1g_self_act(t, y, g0, g1, X_th, n, k)
hill_ex = (y / X_th).^n ./ (1 + (y / X_th).^n);
dydt = g0 + g1 * hill_ex - k * y;
4. Caculate f(x) for the case of one gene with self-activation.
function result = f_1g_self_act(X, g0, g1, X_th, n, k)
hill_inh = 1 ./ (1 + (X / X_th).^n);
result = g0 + g1 * (1-hill_inh) - k * X;
5. Integrate f(X) with the trapezoidal rule
function results = cal_int(Xmin, Xmax, dX, f, varargin)
% varargin to pass all model parameters
% Initialize a vector of potential U
U_all(i + 1) = U_all(i) - (f(X, varargin{:}) + f(X + dX, varargin{:})) / 2 * dX;
results = [X_all' U_all'];
6. Integrate f(X) with the vectorized approach
function results = cal_int_vector(Xmin, Xmax, dX, f, varargin)
% varargin to pass all model parameters
f_all = f(X_all, varargin{:}); % generate all f(X)
% another array containing f(X+dx), except for the last point
f_all_shift_by_plus_one = [f_all(2:end), NaN];
f_all = (f_all + f_all_shift_by_plus_one) * dX / 2; % each integrated part
results = [X_all' U_all'];