Why the optimization results of lsqnonlin are different in R2026a and R2025a?

183 views (last 30 days)
I used the following code to solve a nonlinear optimization problem. All random seeds and optimization options are the same, but the result in the R2026a pre-release is complex-valued, whereas in R2025a or older releases the result is real-valued. All code and data are attached.
% R2025b or earier is real-base solution
% R2026a is complex-base solution?
R2025b:
load temp.mat
rng default;
options = optimoptions('lsqnonlin', 'Algorithm','levenberg-marquardt', 'Display','final',...
'MaxFunEvals',2000, 'MaxIter',1e3, 'TolFun',1e-6, 'TolX',1e-6, 'Jacobian','off');
[x,resnorm,~,exitflag,output] = lsqnonlin(@(p)residual_KR_robust(double(X1_ok), double(X2_ok), imsize1, imsize2, p, 1000), [1000 1000 0 0 0],...
[],[])
Solver stopped prematurely. lsqnonlin stopped because it exceeded the function evaluation limit, options.MaxFunctionEvaluations = 5.000000e+02.
x = 1×5
1.0e+04 * 2.3371 2.4754 0.0000 -0.0000 0.0000
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
resnorm = 2.8483e+07
exitflag = 0
output = struct with fields:
firstorderopt: 1.6500e+07 iterations: 83 funcCount: 504 cgiterations: 0 algorithm: 'trust-region-reflective' stepsize: 1.2800e+03 message: 'Solver stopped prematurely.↵↵lsqnonlin stopped because it exceeded the function evaluation limit,↵options.MaxFunctionEvaluations = 5.000000e+02.' bestfeasible: [] constrviolation: []
R2026a:
  2 Comments
Torsten
Torsten on 10 Nov 2025 at 15:44
Starting from real-valued initial guesses for the parameters, do you have a line in your code that could produce complex results for an element of "err" ? I cannot find one - thus I have no explanation why you could end up with complex-valued parameters at all.
dpb
dpb on 10 Nov 2025 at 16:14
Edited: dpb on 10 Nov 2025 at 16:36
type residual_KR_robust
function [ err ] = residual_KR_robust( X1, X2, imsize1, imsize2, paras, sigma ) % parameretes sigma indicates the distance scope for inliers with homopraphy matrix, in pixels k1 = paras(1); k2 = paras(2); theta = paras(3:5); % yaw = paras(3); % pitch = paras(4); % roll = paras(5); K1 = [k1, 0, imsize1(2)/2; 0, k1, imsize1(1)/2; 0, 0, 1]; K2 = [k2, 0, imsize2(2)/2; 0, k2, imsize2(1)/2; 0, 0, 1]; theta_m = [0 -theta(3) theta(2) theta(3) 0 -theta(1) -theta(2) theta(1) 0]; R = expm(theta_m); % Ry = [cos(yaw), 0, -sin(yaw); % 0, 1, 0; % sin(yaw), 0, cos(yaw)] ; % Rp = [1, 0 0; % 0, cos(pitch), sin(pitch); % 0, -sin(pitch), cos(pitch)] ; % Rr = [cos(roll), sin(roll), 0; % -sin(roll), cos(roll), 0; % 0, 0, 1] ; % R = Rr * Rp * Ry; err = residual_H(X1, X2, K1, K2, R); outlier = (abs(err) > sigma); err(outlier) = sign(err(outlier)) .* (sigma + sigma * log(abs(err(outlier))/sigma)); % err(outlier) = sign(err(outlier)) .* sqrt(2*sigma*abs(err(outlier)) - sigma*sigma); end
type residual_H
function [ errH ] = residual_H( X1, X2, K1, K2, R) % homopraphy matrix H = K2*R/K1; X1_p2 = H * X1; X1_p2(1,:) = X1_p2(1,:) ./ X1_p2(3,:) ; X1_p2(2,:) = X1_p2(2,:) ./ X1_p2(3,:) ; errH1_2 = X2 - X1_p2; X2_p1 = H \ X2; X2_p1(1,:) = X2_p1(1,:) ./ X2_p1(3,:) ; X2_p1(2,:) = X2_p1(2,:) ./ X2_p1(3,:) ; errH2_1 = X1 - X2_p1; errH = [errH1_2(1,:)', errH1_2(2,:)', errH2_1(1,:)', errH2_1(2,:)']; end
@Torsten asked "do you have a line in your code that could produce complex results...?"
The backslash solve might under some circumstances, couldn't it?
Setting a breakpoint on condition ~isreal(errH) would be able to discover when it first occurs and let figure out what might have happened and (maybe then) why.
I don't suppose there's anything about lsqnonlin in the release notes...

Sign in to comment.

Accepted Answer

Matt J
Matt J on 10 Nov 2025 at 20:31
Edited: Matt J on 11 Nov 2025 at 2:53
There appears to be a new (and buggy) implementation of expm.m in R2026a, resulting in incorrectly complex results for skew symmetric matrices:
>> A=zeros(3); A(3,2)=1; A(2,3)=-1
A =
0 0 0
0 0 -1
0 1 0
>> B=expm(A)
B =
1.000000000000000 + 0.000000000000000i 0.000000000000000 + 0.000000000000000i 0.000000000000000 + 0.000000000000000i
0.000000000000000 + 0.000000000000000i 0.540302305868140 - 0.000000000000000i -0.841470984807896 - 0.000000000000000i
0.000000000000000 + 0.000000000000000i 0.841470984807896 + 0.000000000000000i 0.540302305868140 - 0.000000000000000i
>> imag(B)==0
ans =
3×3 logical array
1 1 1
1 0 0
1 0 0
I have submitted a bug report.
  19 Comments
dpb
dpb on 11 Nov 2025 at 18:55
I was just commenting back on @Paul wrote as
if ~any(imag(A),'all')
will only be true if all(imag(A))==0 identically; even the LSB in one element will fail such that if A got converted from intended real to complex owing to some computational gaff like under discussion here, should it really be complex with rounding error complex part or real?
Matt J
Matt J on 13 Nov 2025 at 22:35
Tech support has informed me that they are aware of the issue, and that it will be fixed in the R2026a general release.

Sign in to comment.

More Answers (0)

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!