Mercurial > octave
changeset 31269:72744e659510
maint: Merge stable to default
author | Arun Giridhar <arungiridhar@gmail.com> |
---|---|
date | Fri, 07 Oct 2022 06:23:07 -0400 |
parents | 3c504176e546 (current diff) 44a68ac1a22f (diff) |
children | 6cf7dab21e9b |
files | |
diffstat | 2 files changed, 75 insertions(+), 18 deletions(-) [+] |
line wrap: on
line diff
--- a/scripts/optimization/lsqnonneg.m Thu Oct 06 19:56:02 2022 -0400 +++ b/scripts/optimization/lsqnonneg.m Fri Oct 07 06:23:07 2022 -0400 @@ -69,9 +69,11 @@ ## @end itemize ## ## @item lambda -## Conjugate gradient at the converged point. Zero elements are usually -## abutting coordinate planes. Negative elements are stable to small -## perturbations. +## Lagrange multipliers. If these are non-zero, the corresponding @var{x} +## values should be zero, indicating the solution is pressed up against a +## coordinate plane. The magnitude indicates how much the residual would +## improve if the @code{@var{x} >= 0} constraints were relaxed in that +## direction. ## ## @end table ## @seealso{pqpnonneg, lscov, optimset} @@ -220,12 +222,7 @@ endif if (isargout (6)) lambda = zeros (size (x)); - lambda(p) = w; - ## FIXME: The above line errors when the solution is NOT constrained - ## by non-negativity! That case happens when the lsqnonneg solution - ## is the same as the fminunc solution. Ideally the user would not - ## be using lsqnonneg if nonnegativity constraints are not active, but we - ## should handle that more gracefully. + lambda (setdiff (1:numel(x), p)) = w; endif endfunction @@ -242,6 +239,40 @@ %! xnew = [0;0.6929]; %! assert (lsqnonneg (C, d), xnew, 0.0001); +## Test Lagrange multiplier duality: x .* lambda == 0 + +%!test +%! [x, resn, resid, ~, ~, lambda] = lsqnonneg ([1 0; 0 1; 2 1], [1 1 3]'); +%! assert (x, [1 1]', 10*eps); +%! assert (resn, 0, 10*eps); +%! assert (resid, [0 0 0]', 10*eps); +%! assert (lambda, [0 0]', 10*eps); +%! assert (x .* lambda, [0 0]') + +%!test +%! [x, resn, resid, ~, ~, lambda] = lsqnonneg ([1 0; 0 1; 2 1], [1 -1 1]'); +%! assert (x, [0.6 0]', 10*eps); +%! assert (resn, 1.2, 10*eps); +%! assert (resid, [0.4 -1 -0.2]', 10*eps); +%! assert (lambda, [0 -1.2]', 10*eps); +%! assert (x .* lambda, [0 0]') + +%!test +%! [x, resn, resid, ~, ~, lambda] = lsqnonneg ([1 0; 0 1; 2 1], [-1 1 -1]'); +%! assert (x, [0 0]', 10*eps); +%! assert (resn, 3, 10*eps); +%! assert (resid, [-1 1 -1]', 10*eps); +%! assert (lambda, [-3 0]', 10*eps); +%! assert (x .* lambda, [0 0]') + +%!test +%! [x, resn, resid, ~, ~, lambda] = lsqnonneg ([1 0; 0 1; 2 1], [-1 -1 -3]'); +%! assert (x, [0 0]', 10*eps); +%! assert (resn, 11, 20*eps); +%! assert (resid, [-1 -1 -3]', 10*eps); +%! assert (lambda, [-7 -4]', 10*eps); +%! assert (x .* lambda, [0 0]') + ## Test input validation %!error <Invalid call> lsqnonneg () %!error <Invalid call> lsqnonneg (1)
--- a/scripts/optimization/pqpnonneg.m Thu Oct 06 19:56:02 2022 -0400 +++ b/scripts/optimization/pqpnonneg.m Fri Oct 07 06:23:07 2022 -0400 @@ -71,9 +71,11 @@ ## @end itemize ## ## @item lambda -## Conjugate gradient at the converged point. Zero elements are usually -## abutting coordinate planes. Negative elements are stable to small -## perturbations. +## Lagrange multipliers. If these are non-zero, the corresponding @var{x} +## values should be zero, indicating the solution is pressed up against a +## coordinate plane. The magnitude indicates how much the residual would +## improve if the @code{@var{x} >= 0} constraints were relaxed in that +## direction. ## ## @end table ## @seealso{lsqnonneg, qp, optimset} @@ -224,12 +226,7 @@ endif if (isargout (5)) lambda = zeros (size (x)); - lambda(p) = w; - ## FIXME: The above line errors when the solution is NOT constrained - ## by non-negativity! That case happens when the lsqnonneg solution - ## is the same as the fminunc solution. Ideally the user would not - ## be using lsqnonneg if nonnegativity constraints are not active, but we - ## should handle that more gracefully. + lambda (setdiff (1:numel(x), p)) = w; endif endfunction @@ -246,6 +243,35 @@ %! d = rand (20, 1); %! assert (pqpnonneg (C'*C, -C'*d), lsqnonneg (C, d), 100*eps); +## Test Lagrange multiplier duality: lambda .* x == 0 +%!test +%! [x, resid, ~, ~, lambda] = pqpnonneg ([3 2; 2 2], [6; 5]); +%! assert (x, [0 0]', eps); +%! assert (resid, 0, eps); +%! assert (lambda, [-6 -5]', eps); +%! assert (x .* lambda, [0 0]') + +%!test +%! [x, resid, ~, ~, lambda] = pqpnonneg ([3 2; 2 2], [6; -5]); +%! assert (x, [0 2.5]', eps); +%! assert (resid, -6.25, eps); +%! assert (lambda, [-11 0]', eps); +%! assert (x .* lambda, [0 0]') + +%!test +%! [x, resid, ~, ~, lambda] = pqpnonneg ([3 2; 2 2], [-6; 5]); +%! assert (x, [2 0]', eps); +%! assert (resid, -6, eps); +%! assert (lambda, [0 -9]', eps); +%! assert (x .* lambda, [0 0]') + +%!test +%! [x, resid, ~, ~, lambda] = pqpnonneg ([3 2; 2 2], [-6; -5]); +%! assert (x, [1 1.5]', 10*eps); +%! assert (resid, -6.75, eps); +%! assert (lambda, [0 0]', eps); +%! assert (x .* lambda, [0 0]') + ## Test input validation %!error <Invalid call> pqpnonneg () %!error <Invalid call> pqpnonneg (1)