Why the optimization results of lsqnonlin are different in R2026a and R2025a?
이 질문을 팔로우합니다.
- 팔로우하는 게시물 피드에서 업데이트를 확인할 수 있습니다.
- 정보 수신 기본 설정에 따라 이메일을 받을 수 있습니다.
오류 발생
페이지가 변경되었기 때문에 동작을 완료할 수 없습니다. 업데이트된 상태를 보려면 페이지를 다시 불러오십시오.
이전 댓글 표시
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
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.
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
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...
채택된 답변
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
The bad logic appears to be in this section:
if ishermitian(A) || ishermitian(A, 'skew')
[Q, d] = eig(full(A), 'vector');
expd = exp(d);
F = (Q.*expd.')*Q';
if isreal(expd)
F = (F+F')/2;
end
return;
end
I think it should really be,
if ishermitian(A) || ishermitian(A, 'skew')
[Q, d] = eig(full(A), 'vector');
expd = exp(d);
F = real( (Q.*expd.')*Q' );
return;
end
What if A is complex ?
A = [1 3-1i 4;3+1i -2 -6+1i;4 -6-1i 5];
ishermitian(A)
ans = logical
1
[Q, d] = eig(full(A), 'vector');
d
d = 3×1
-7.9622
2.7363
9.2259
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
expd = exp(d);
F = real( (Q.*expd.')*Q' );
F
F = 3×3
1.0e+03 *
0.9612 -1.0241 2.5403
-1.0241 1.7128 -3.4018
2.5403 -3.4018 7.4982
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
●
expm(A)
ans =
1.0e+03 *
0.9612 + 0.0000i -1.0241 - 0.7486i 2.5403 + 0.8175i
-1.0241 + 0.7486i 1.7128 + 0.0000i -3.4018 + 1.1120i
2.5403 - 0.8175i -3.4018 - 1.1120i 7.4982 + 0.0000i
True. So, I guess it should be...
if ishermitian(A) || ishermitian(A, 'skew')
[Q, d] = eig(full(A), 'vector');
expd = exp(d);
F = (Q.*expd.')*Q';
if isreal(A)
F = real(F);
end
return;
end
This fixes the lsqnonlin result. Of course, I don't know how much you're inclined to trust the rest of the pre-release with the built-in expm out of whack...
function [ err ] = residual_KR_robust( X1, X2, imsize1, imsize2, paras, sigma )
k1 = paras(1);
k2 = paras(2);
theta = paras(3:5);
ctr1=imsize1/2; %NOTE: These are the wrong expressions for the center of a 1-based
ctr2=imsize2/2; % image coordinate grid. Should be ctr = (imsize+1)/2
K1 = [k1, 0, ctr1(2);
0, k1, ctr1(1);
0, 0, 1];
K2 = [k2, 0, ctr2(2);
0, k2, ctr2(1);
0, 0, 1];
theta_m = [0 -theta(3) theta(2)
theta(3) 0 -theta(1)
-theta(2) theta(1) 0];
[V,D]=eig(theta_m); %<-----Matt J
R=real(V*diag(exp(diag(D)))/V); %<-----Matt J
err = residual_H(X1, X2, K1, K2, R);
outlier = (abs(err) > sigma);
err(outlier) = sign(err(outlier)) .* (sigma + sigma * log( abs(err(outlier)) / sigma ) ) ;
end
Solver stopped prematurely.
lsqnonlin stopped because it exceeded the function evaluation limit,
options.MaxFunctionEvaluations = 5.000000e+02.
x =
1.0e+04 *
2.3371 2.4754 0.0000 -0.0000 0.0000
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: []
@Matt J Thank you for your keen insight!
Great catch. I really hope that error gets fixed before the 2026A is formally released into the wild. Nevertheless, seems strange and, frankly, quite disappointing that it made it even into a pre-release.
@Paul I updated it, but even format long isn't enough to reveal the non-zeroness of the imaginary part.
Now I'm confused. If I run the bad logic here I get the expected (real) result, but the same code in 2026a yields a complex result?
A=zeros(3); A(3,2)=1; A(2,3)=-1;
[Q, d] = eig(full(A), 'vector');
expd = exp(d);
F = (Q.*expd.')*Q'
F = 3×3
1.0000 0 0
0 0.5403 -0.8415
0 0.8415 0.5403
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
expm(A)
ans = 3×3
1.0000 0 0
0 0.5403 -0.8415
0 0.8415 0.5403
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
If I run the bad logic here I get the expected (real) result, but the same code in 2026a yields a complex result?
On my computer, in both R2024b and R2026a,:
F =
1.0000 + 0.0000i 0.0000 + 0.0000i 0.0000 + 0.0000i
0.0000 + 0.0000i 0.5403 - 0.0000i -0.8415 - 0.0000i
0.0000 + 0.0000i 0.8415 + 0.0000i 0.5403 - 0.0000i
However, I do not have R2025a installed, so I cannot compare, but a real-valued result is theoretically unstable numerically, when the eigenvalues are complex, as they are here. The differences we see would have to be floating point noise -- possibly a CPU-dpendent thing, or version-dependent differences in multithreading.
I'm running 2024a and your A matrix causes expm to go down a different path because in 2024a expm checks ishermitian(A) but does not check for ishermitian(A,'skew').
In 2024a we have:
if ishermitian(A)
[Q, d] = eig(full(A), 'vector');
F = (Q.*exp(d).')*Q';
F = (F+F')/2;
return;
end
from which I gather that if A is Hermitian then F must be conjugate symmetric. But I don't think your latest proposal here would ensure that.
I assume that the skew check and other mods were added in 2024b, which is why you're seeing the complex result? I don't have 2024b handy so can't check myself.
But @郦歌 reports no issues in 2025b and prior, albeit for different input matrices than we're playing with here.
F = (Q.*exp(d).')*Q' is the exact formula for exp(A) so any failure of F to have requisite properties like realness or Hermitian-ness would have to be due to floating point noise. You can make sure the floating point representation of the final result is ideally Hermitian by modifying as follows.
isherm=ishermitian(A);
if isherm || ishermitian(A, 'skew')
[Q, d] = eig(full(A), 'vector');
expd = exp(d);
F = (Q.*expd.')*Q';
if isreal(A)
F = real(F);
end
if isherm
F=(F+F')/2;
end
return;
end
According to Wikipedia, the properties that hold are:
"If X is symmetric then eX is also symmetric, and if X is skew-symmetric then eX is orthogonal. If X is Hermitian then eX is also Hermitian, and if X is skew-Hermitian then eX is unitary."
I don't think the else case above is correct. And the real(F) should be predicated on A being real.
For the cases where F is supposed to be orthogonal or unitary, what would be the best way to ensure that?
I think that's what I now have.
For the cases where F is supposed to be orthogonal or unitary, what would be the best way to ensure that?
I don't think you can. Floating point Hermitian-ness is something that can be ensured (as above), but probably not orthogonality. F*F' = I will always give some floating point error.
One more sublety that I just realized. The isreal check will fail if the imaginary part of a complex matrix zero.
A = [0,0,0;0,0,-1;0,1,0];
A = complex(A,0*A);
ishermitian(A,'skew')
ans = logical
1
isreal(A)
ans = logical
0
Perhaps need to change to
if ~any(imag(A),'all')
F = real(F);
end
Unrecognized function or variable 'F'.
"The isreal check will fail if the imaginary part of a complex matrix zero."
Yes, isreal only checks the data class of the variable; it doesn't care what the values are.
And then you're back into the problem of how much is sufficient that an array is intended to be real but has floating point rounding in the imaginary part making it complex? Just as the earlier comment that (almost) always the ==1 will fail for checking unity. Floating point is a b@!xx! when it comes to such details. Numerical computing and math rarely overlap neatly.
@dpb I think @Paul was suggesting that if A has an imaginary part, whether explicit or implicit, that is perfectly zero then theoretically so should the output of exp(A). So, you might do something like this,
isherm=ishermitian(A);
if isherm || ishermitian(A, 'skew')
[Q, d] = eig(full(A), 'vector');
F = (Q.*exp(d).')*Q';
if isreal(A)
F = real(F);
elseif ~any(imag(A(:))) %real, but of complex type
F=complex(real(F), imag(A));
end
if isherm
F=(F+F')/2;
end
return;
end
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?
Tech support has informed me that they are aware of the issue, and that it will be fixed in the R2026a general release.
추가 답변 (0개)
카테고리
도움말 센터 및 File Exchange에서 Choose a Solver에 대해 자세히 알아보기
참고 항목
웹사이트 선택
번역된 콘텐츠를 보고 지역별 이벤트와 혜택을 살펴보려면 웹사이트를 선택하십시오. 현재 계신 지역에 따라 다음 웹사이트를 권장합니다:
또한 다음 목록에서 웹사이트를 선택하실 수도 있습니다.
사이트 성능 최적화 방법
최고의 사이트 성능을 위해 중국 사이트(중국어 또는 영어)를 선택하십시오. 현재 계신 지역에서는 다른 국가의 MathWorks 사이트 방문이 최적화되지 않았습니다.
미주
- América Latina (Español)
- Canada (English)
- United States (English)
유럽
- Belgium (English)
- Denmark (English)
- Deutschland (Deutsch)
- España (Español)
- Finland (English)
- France (Français)
- Ireland (English)
- Italia (Italiano)
- Luxembourg (English)
- Netherlands (English)
- Norway (English)
- Österreich (Deutsch)
- Portugal (English)
- Sweden (English)
- Switzerland
- United Kingdom (English)
