fprintf('Generating synthetic data...\n');
Generating synthetic data...
true_params = [3.0, 25.0, 0.5, -0.8];
x_data = linspace(0.1, 100, 100)';
y_true = true_params(1) * exp(-(x_data/true_params(2)).^true_params(3)) + true_params(4);
y_data = y_true + noise_level * randn(size(y_true));
figure('Position', [100, 100, 800, 400]);
plot(x_data, y_data, 'ko', 'MarkerSize', 4, 'MarkerFaceColor', [0.7, 0.7, 0.7]);
plot(x_data, y_true, 'r-', 'LineWidth', 2);
legend('Noisy data', 'True model', 'Location', 'best');
fprintf(' True parameters: n=%.2f, tau=%.2f, m=%.2f, c=%.2f\n', ...
true_params(1), true_params(2), true_params(3), true_params(4));
True parameters: n=3.00, tau=25.00, m=0.50, c=-0.80
initialguess = [3, 25.5013, 0.5, -0.8793];
ub = [inf, 2000, 0.5, 0];
options = optimoptions('lsqnonlin', 'Algorithm', 'levenberg-marquardt', ...
objective = @(params) compute_residuals(params, x_data, y_data);
[x_opt, resnorm, residual, exitflag, output, lambda, jacobian] = ...
lsqnonlin(objective, initialguess, lb, ub, options);
fprintf('\nOptimal parameters:\n');
fprintf('n = %.4f (true: %.2f)\n', x_opt(1), true_params(1));
fprintf('tau = %.4f (true: %.2f)\n', x_opt(2), true_params(2));
tau = 27.0510 (true: 25.00)
fprintf('m = %.4f (true: %.2f)\n', x_opt(3), true_params(3));
fprintf('c = %.4f (true: %.2f)\n', x_opt(4), true_params(4));
c = -0.8577 (true: -0.80)
y_fitted = compute_model(x_opt, x_data);
plot(x_data, y_data, 'ko', 'MarkerSize', 4, 'MarkerFaceColor', [0.7, 0.7, 0.7]);
plot(x_data, y_fitted, 'b-', 'LineWidth', 2);
plot(x_data, y_true, 'r--', 'LineWidth', 1.5);
legend('Data', 'Fitted model', 'True model', 'Location', 'best');
active_lower = abs(x_opt - lb) < tolerance;
active_upper = abs(x_opt - ub) < tolerance;
constraints_active = any(active_lower) || any(active_upper);
fprintf('\nConstraint Status:\n');
param_names = {'n', 'tau', 'm', 'c'};
fprintf('%s is at LOWER bound (%.4f)\n', param_names{i}, lb(i));
fprintf('%s is at UPPER bound (%.4f)\n', param_names{i}, ub(i));
fprintf('%s is FREE (%.4f)\n', param_names{i}, x_opt(i));
end
n is FREE (3.0485)
tau is FREE (27.0510)
m is at LOWER bound (0.5000)
fprintf('\n=== METHOD 1: Standard Error Approach ===\n');
fprintf('(Valid only because no constraints are active)\n\n');
n_data = length(residual);
n_params = length(x_opt);
sigma2 = resnorm / (n_data - n_params);
JtJ = jacobian' * jacobian;
covar = sigma2 * inv(JtJ);
t_crit = tinv(1 - alpha/2, df);
ci_lower = x_opt - t_crit * se;
ci_upper = x_opt + t_crit * se;
fprintf('Parameter Estimates with 95%% CI:\n');
fprintf('%5s: %.4f [%.4f, %.4f] ± %.4f\n', ...
param_names{i}, x_opt(i), ci_lower(i), ci_upper(i), se(i));
fprintf('\nWARNING: These CIs may extend beyond constraint boundaries!\n');
fprintf('\n=== METHOD 1: Not applicable (constraints are active) ===\n');
end
=== METHOD 1: Not applicable (constraints are active) ===
fprintf('\n=== METHOD 2: Residual Bootstrap ===\n');
=== METHOD 2: Residual Bootstrap ===
fprintf('(Works with constraints, more reliable)\n\n');
(Works with constraints, more reliable)
bootstrap_params = zeros(n_bootstrap, length(x_opt));
y_fitted = compute_model(x_opt, x_data);
fprintf('Running bootstrap (this may take a minute)...\n');
Running bootstrap (this may take a minute)...
boot_options = optimoptions('lsqnonlin', 'Algorithm', 'levenberg-marquardt', ...
'Display', 'off', 'MaxIterations', 100);
boot_indices = randi(length(residual), length(residual), 1);
boot_residuals = residual(boot_indices);
y_boot = y_fitted + boot_residuals;
boot_objective = @(params) compute_residuals(params, x_data, y_boot);
x_boot = lsqnonlin(boot_objective, x_opt, lb, ub, boot_options);
bootstrap_params(b, :) = x_boot;
bootstrap_params(b, :) = x_opt;
fprintf(' Completed %d/%d iterations\n', b, n_bootstrap);
end
Completed 100/1000 iterations
Completed 200/1000 iterations
Completed 300/1000 iterations
Completed 400/1000 iterations
Completed 500/1000 iterations
Completed 600/1000 iterations
Completed 700/1000 iterations
Completed 800/1000 iterations
Completed 900/1000 iterations
Completed 1000/1000 iterations
fprintf('Bootstrap completed in %.1f seconds\n\n', elapsed);
Bootstrap completed in 0.9 seconds
fprintf('Bootstrap 95%% Confidence Intervals:\n');
Bootstrap 95% Confidence Intervals:
fprintf('%-5s %10s %12s %12s %10s\n', 'Param', 'Estimate', 'CI Lower', 'CI Upper', 'Std Error');
Param Estimate CI Lower CI Upper Std Error
fprintf('%s\n', repmat('-', 1, 60));
------------------------------------------------------------
ci_lower_boot = prctile(bootstrap_params(:, i), alpha/2 * 100);
ci_upper_boot = prctile(bootstrap_params(:, i), (1 - alpha/2) * 100);
se_boot = std(bootstrap_params(:, i));
fprintf('%-5s %10.4f [%10.4f, %10.4f] ± %.4f\n', ...
param_names{i}, x_opt(i), ci_lower_boot, ci_upper_boot, se_boot);
end
n 3.0485 [ 2.9221, 3.1828] ± 0.0679
tau 27.0510 [ 20.4260, 35.1022] ± 4.0409
m 0.5000 [ 0.5000, 0.5000] ± 0.0000
c -0.8577 [ -1.0000, -0.7161] ± 0.0833
figure('Position', [100, 100, 1200, 800]);
histogram(bootstrap_params(:, i), 50, 'Normalization', 'pdf', ...
'FaceColor', [0.3, 0.5, 0.8], 'EdgeColor', 'none', 'FaceAlpha', 0.7);
plot([x_opt(i), x_opt(i)], yl, 'r-', 'LineWidth', 2);
ci_low = prctile(bootstrap_params(:, i), alpha/2 * 100);
ci_high = prctile(bootstrap_params(:, i), (1 - alpha/2) * 100);
plot([ci_low, ci_low], yl, 'r--', 'LineWidth', 1.5);
plot([ci_high, ci_high], yl, 'r--', 'LineWidth', 1.5);
plot([lb(i), lb(i)], yl, 'k-', 'LineWidth', 2);
text(lb(i), yl(2)*0.9, 'LB', 'HorizontalAlignment', 'center');
plot([ub(i), ub(i)], yl, 'k-', 'LineWidth', 2);
text(ub(i), yl(2)*0.9, 'UB', 'HorizontalAlignment', 'center');
title(sprintf('%s: %.4f [%.4f, %.4f]', ...
param_names{i}, x_opt(i), ci_low, ci_high));
sgtitle('Bootstrap Parameter Distributions with 95% Confidence Intervals');
function residuals = compute_residuals(params, x_data, y_data)
y_pred = compute_model(params, x_data);
residuals = y_data - y_pred;
function y_pred = compute_model(params, x_data)
y_pred = n * exp(-(x_data/tau).^m) + c;
fprintf('\n=== IMPORTANT NOTES ===\n');
fprintf('1. The bootstrap approach respects constraints automatically\n');
1. The bootstrap approach respects constraints automatically
fprintf('2. Bootstrap CIs are valid even when parameters are at boundaries\n');
2. Bootstrap CIs are valid even when parameters are at boundaries
fprintf('3. This uses RESIDUAL BOOTSTRAP - resampling residuals from thefit\n');
3. This uses RESIDUAL BOOTSTRAP - resampling residuals from thefit
fprintf('4. Increase n_bootstrap to 5000-10000 for publication-qualityresults\n');
4. Increase n_bootstrap to 5000-10000 for publication-qualityresults
fprintf('5. If parameters consistently hit boundaries, consider whether your\n');
5. If parameters consistently hit boundaries, consider whether your
fprintf(' model/constraints are appropriate for the data\n');
model/constraints are appropriate for the data
fprintf('\n=== HOW TO ADAPT THIS CODE ===\n');
=== HOW TO ADAPT THIS CODE ===
fprintf('1. Replace the data generation section with your actual data\n');
1. Replace the data generation section with your actual data
fprintf('2. Modify compute_model() to match your specific model equation\n');
2. Modify compute_model() to match your specific model equation
fprintf('3. Adjust the parameter bounds (lb, ub) as needed\n');
3. Adjust the parameter bounds (lb, ub) as needed
fprintf('4. Keep the bootstrap resampling logic - it works for any model!\n')
4. Keep the bootstrap resampling logic - it works for any model!