Skip to content

Commit d983a29

Browse files
author
Rik
committed
Overhaul qr function for Matlab compatibility (bug #66488)
* qr.cc (Fqr): Update documentation to warn that calling form with argument '0' is not recommended and may be removed. Update input validation to catch a non-numeric argument B, and to catch being called with more than 3 output arguments. Remove input validation code for invalid 'B' argument that is unreachable. Return a matrix P when "econ" argument is given rather than a vector. Always return a row vector when "vector" or "0" flag given for Matlab compatibility. Update BIST tests. Add BIST test for bug #66488.
1 parent 006e395 commit d983a29

File tree

1 file changed

+71
-50
lines changed

1 file changed

+71
-50
lines changed

libinterp/corefcn/qr.cc

+71-50
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,10 @@ DEFUN (qr, args, nargout,
115115
@deftypefnx {} {@var{R} =} qr (@var{A}) # sparse A
116116
@deftypefnx {} {@var{X} =} qr (@var{A}, @var{B}) # sparse A
117117
@deftypefnx {} {[@var{C}, @var{R}] =} qr (@var{A}, @var{B})
118-
@deftypefnx {} {[@dots{}] =} qr (@dots{}, 0)
119118
@deftypefnx {} {[@dots{}] =} qr (@dots{}, "econ")
120119
@deftypefnx {} {[@dots{}] =} qr (@dots{}, "vector")
121120
@deftypefnx {} {[@dots{}] =} qr (@dots{}, "matrix")
121+
@deftypefnx {} {[@dots{}] =} qr (@dots{}, 0)
122122
@cindex QR factorization
123123
Compute the QR@tie{}factorization of @var{A}, using standard @sc{lapack}
124124
subroutines.
@@ -238,7 +238,7 @@ recommended to request only one return value @var{R}. In that case, the
238238
computation avoids the construction of @var{Q} and returns a sparse @var{R}
239239
such that @code{@var{R} = chol (@var{A}' * @var{A})}.
240240
241-
If @var{A} is dense, an additional matrix @var{B} is supplied and two
241+
If @var{A} is dense, an additional input matrix @var{B} is supplied, and two
242242
return values are requested, then @code{qr} returns @var{C}, where
243243
@code{@var{C} = @var{Q}' * @var{B}}. This allows the least squares
244244
approximation of @code{@var{A} \ @var{B}} to be calculated as
@@ -271,14 +271,17 @@ matrix. In this case, the defining relationship is:
271271
The default, however, is to return a permutation matrix and this may be
272272
explicitly specified by using a final argument of @qcode{"matrix"}.
273273
274-
If the final argument is the scalar 0 or the string @qcode{"econ"}, an economy
274+
When the optional argument is the string @qcode{"econ"}, an economy
275275
factorization is returned. If the original matrix @var{A} has size
276276
@nospell{MxN} and M > N, then the economy factorization will calculate just N
277277
rows in @var{R} and N columns in @var{Q} and omit the zeros in @var{R}. If M
278278
@leq{} N, there is no difference between the economy and standard
279-
factorizations. When calculating an economy factorization and @var{A} is
280-
dense, the output @var{P} is always a vector rather than a matrix. If @var{A}
281-
is sparse, output @var{P} is a sparse permutation matrix.
279+
factorizations.
280+
281+
If the optional argument is the numeric value 0 then @code{qr} acts as if
282+
the @qcode{"econ"} and @qcode{"vector"} arguments were both given.
283+
@strong{Warning:} This syntax is accepted, but no longer recommended and may
284+
be removed in the future. Use @qcode{"econ"} instead.
282285
283286
Background: The QR factorization has applications in the solution of least
284287
squares problems
@@ -315,6 +318,9 @@ orthogonal basis of @code{span (A)}.
315318
if (nargin < 1 || nargin > 3)
316319
print_usage ();
317320

321+
if (nargout > 3)
322+
error ("qr: too many output arguments");
323+
318324
octave_value_list retval;
319325

320326
octave_value arg = args(0);
@@ -335,6 +341,7 @@ orthogonal basis of @code{span (A)}.
335341
if (val == 0)
336342
{
337343
economy = true;
344+
vector_p = true;
338345
have_b = (nargin > 2);
339346
}
340347
else if (nargin == 3) // argument 3 should be 0 or a string
@@ -343,32 +350,28 @@ orthogonal basis of @code{span (A)}.
343350
else if (args(nargin-1).is_string ())
344351
{
345352
std::string str = args(nargin-1).string_value ();
346-
if (str == "vector")
353+
if (str == "econ")
354+
economy = true;
355+
else if (str == "vector")
347356
vector_p = true;
348-
else if (str == "econ")
349-
{
350-
economy = true;
351-
have_b = (nargin > 2);
352-
}
353357
else if (str != "matrix")
354-
error ("qr: option string must be 'econ' or 'matrix' or " \
355-
"'vector', not \"%s\"", str.c_str ());
358+
error ("qr: option string must be 'econ' or 'matrix' or 'vector', not \"%s\"", str.c_str ());
356359
have_b = (nargin > 2);
357360
}
358-
else if (! args(nargin-1).is_matrix_type ())
361+
else if (! args(nargin-1).isnumeric ())
359362
err_wrong_type_arg ("qr", args(nargin-1));
360363
else if (nargin == 3) // should be caught by is_scalar_type or is_string
361364
print_usage ();
362365

366+
if (have_b && ! args(1).isnumeric ())
367+
print_usage ();
368+
363369
if (have_b && args(1).iscomplex ())
364370
is_cmplx = true;
365371
}
366372

367373
if (arg.issparse ())
368374
{
369-
if (nargout > 3)
370-
error ("qr: too many output arguments");
371-
372375
if (is_cmplx)
373376
{
374377
if (have_b && nargout == 1)
@@ -399,8 +402,6 @@ orthogonal basis of @code{span (A)}.
399402
<SparseMatrix, SparseComplexMatrix>
400403
(arg.sparse_complex_matrix_value (),
401404
args(1).sparse_matrix_value (), info));
402-
else
403-
error ("qr: b is not valid");
404405
}
405406
else if (have_b && nargout == 2)
406407
{
@@ -415,7 +416,7 @@ orthogonal basis of @code{span (A)}.
415416
q (arg.sparse_complex_matrix_value ());
416417
if (vector_p)
417418
retval = ovl (q.C (args(1).complex_matrix_value (), economy),
418-
q.R (economy), q.E ());
419+
q.R (economy), q.E ().transpose ());
419420
else
420421
retval = ovl (q.C (args(1).complex_matrix_value (), economy),
421422
q.R (economy), q.E_MAT ());
@@ -427,7 +428,8 @@ orthogonal basis of @code{span (A)}.
427428
math::sparse_qr<SparseComplexMatrix>
428429
q (arg.sparse_complex_matrix_value ());
429430
if (vector_p)
430-
retval = ovl (q.Q (economy), q.R (economy), q.E ());
431+
retval = ovl (q.Q (economy), q.R (economy),
432+
q.E ().transpose ());
431433
else
432434
retval = ovl (q.Q (economy), q.R (economy),
433435
q.E_MAT ());
@@ -472,8 +474,6 @@ orthogonal basis of @code{span (A)}.
472474
(arg.sparse_matrix_value (),
473475
args(1).sparse_complex_matrix_value (),
474476
info));
475-
else
476-
error ("qr: b is not valid");
477477
}
478478
else if (have_b && nargout == 2)
479479
{
@@ -488,7 +488,7 @@ orthogonal basis of @code{span (A)}.
488488
q (arg.sparse_matrix_value ());
489489
if (vector_p)
490490
retval = ovl (q.C (args(1).matrix_value (), economy),
491-
q.R (economy), q.E ());
491+
q.R (economy), q.E ().transpose ());
492492
else
493493
retval = ovl (q.C (args(1).matrix_value (), economy),
494494
q.R (economy), q.E_MAT ());
@@ -501,7 +501,8 @@ orthogonal basis of @code{span (A)}.
501501
math::sparse_qr<SparseMatrix>
502502
q (arg.sparse_matrix_value ());
503503
if (vector_p)
504-
retval = ovl (q.Q (economy), q.R (economy), q.E ());
504+
retval = ovl (q.Q (economy), q.R (economy),
505+
q.E ().transpose ());
505506
else
506507
retval = ovl (q.Q (economy), q.R (economy),
507508
q.E_MAT ());
@@ -524,7 +525,7 @@ orthogonal basis of @code{span (A)}.
524525
else
525526
{
526527
if (have_b && nargout > 2)
527-
error ("qr: too many output arguments for dense A with B");
528+
error ("qr: too many output arguments when called with A and B");
528529

529530
if (arg.is_single_type ())
530531
{
@@ -565,7 +566,7 @@ orthogonal basis of @code{span (A)}.
565566
{
566567
math::qrp<FloatMatrix> fact (m, type);
567568

568-
if (economy || vector_p)
569+
if (vector_p)
569570
retval = ovl (fact.Q (), get_qr_r (fact), fact.Pvec ());
570571
else
571572
retval = ovl (fact.Q (), get_qr_r (fact), fact.P ());
@@ -603,7 +604,7 @@ orthogonal basis of @code{span (A)}.
603604
default:
604605
{
605606
math::qrp<FloatComplexMatrix> fact (m, type);
606-
if (economy || vector_p)
607+
if (vector_p)
607608
retval = ovl (fact.Q (), get_qr_r (fact), fact.Pvec ());
608609
else
609610
retval = ovl (fact.Q (), get_qr_r (fact), fact.P ());
@@ -616,8 +617,7 @@ orthogonal basis of @code{span (A)}.
616617
{
617618
if (arg.isreal ())
618619
{
619-
math::qr<Matrix>::type type
620-
= qr_type<Matrix> (nargout, economy);
620+
math::qr<Matrix>::type type = qr_type<Matrix> (nargout, economy);
621621

622622
Matrix m = arg.matrix_value ();
623623

@@ -650,7 +650,7 @@ orthogonal basis of @code{span (A)}.
650650
default:
651651
{
652652
math::qrp<Matrix> fact (m, type);
653-
if (economy || vector_p)
653+
if (vector_p)
654654
retval = ovl (fact.Q (), get_qr_r (fact), fact.Pvec ());
655655
else
656656
retval = ovl (fact.Q (), get_qr_r (fact), fact.P ());
@@ -688,7 +688,7 @@ orthogonal basis of @code{span (A)}.
688688
default:
689689
{
690690
math::qrp<ComplexMatrix> fact (m, type);
691-
if (economy || vector_p)
691+
if (vector_p)
692692
retval = ovl (fact.Q (), get_qr_r (fact), fact.Pvec ());
693693
else
694694
retval = ovl (fact.Q (), get_qr_r (fact), fact.P ());
@@ -709,8 +709,8 @@ orthogonal basis of @code{span (A)}.
709709
%! a = [0, 2, 1; 2, 1, 2];
710710
%!
711711
%! [q, r] = qr (a);
712-
%! [qe, re] = qr (a, 0);
713-
%! [qe2, re2] = qr (a, "econ");
712+
%! [qe, re] = qr (a, "econ");
713+
%! [qe2, re2] = qr (a, 0);
714714
%!
715715
%! assert (q * r, a, sqrt (eps));
716716
%! assert (qe * re, a, sqrt (eps));
@@ -720,19 +720,19 @@ orthogonal basis of @code{span (A)}.
720720
%! a = [0, 2, 1; 2, 1, 2];
721721
%!
722722
%! [q, r, p] = qr (a); # FIXME: not giving right dimensions.
723-
%! [qe, re, pe] = qr (a, 0);
724-
%! [qe2, re2, pe2] = qr (a, "econ");
723+
%! [qe, re, pe] = qr (a, "econ");
724+
%! [qe2, re2, pe2] = qr (a, 0);
725725
%!
726726
%! assert (q * r, a * p, sqrt (eps));
727-
%! assert (qe * re, a(:, pe), sqrt (eps));
727+
%! assert (qe * re, a * pe, sqrt (eps));
728728
%! assert (qe2 * re2, a(:, pe2), sqrt (eps));
729729
730730
%!test
731731
%! a = [0, 2; 2, 1; 1, 2];
732732
%!
733733
%! [q, r] = qr (a);
734-
%! [qe, re] = qr (a, 0);
735-
%! [qe2, re2] = qr (a, "econ");
734+
%! [qe, re] = qr (a, "econ");
735+
%! [qe2, re2] = qr (a, 0);
736736
%!
737737
%! assert (q * r, a, sqrt (eps));
738738
%! assert (qe * re, a, sqrt (eps));
@@ -742,11 +742,11 @@ orthogonal basis of @code{span (A)}.
742742
%! a = [0, 2; 2, 1; 1, 2];
743743
%!
744744
%! [q, r, p] = qr (a);
745-
%! [qe, re, pe] = qr (a, 0);
746-
%! [qe2, re2, pe2] = qr (a, "econ");
745+
%! [qe, re, pe] = qr (a, "econ");
746+
%! [qe2, re2, pe2] = qr (a, 0);
747747
%!
748748
%! assert (q * r, a * p, sqrt (eps));
749-
%! assert (qe * re, a(:, pe), sqrt (eps));
749+
%! assert (qe * re, a * pe, sqrt (eps));
750750
%! assert (qe2 * re2, a(:, pe2), sqrt (eps));
751751
752752
%!test
@@ -800,14 +800,35 @@ orthogonal basis of @code{span (A)}.
800800
%! assert (qr (sparse (1, 0)), sparse (1, 0))
801801
%! assert (qr (sparse (0, 1)), sparse (0, 1))
802802
803-
%!error qr ()
804-
%!error qr ([1, 2; 3, 4], 0, 2)
803+
%!test <*66488>
804+
%! ## Orientation of 'p' output
805+
%! [q, r, p] = qr (eye (3));
806+
%! assert (size (p), [3, 3]);
807+
%! [q, r, p] = qr (speye (3));
808+
%! assert (size (p), [3, 3]);
809+
%! [q, r, p] = qr (eye (3), 'vector');
810+
%! assert (size (p), [1, 3]);
811+
%! [q, r, p] = qr (speye (3), 'vector');
812+
%! assert (size (p), [1, 3]);
813+
%! [q, r, p] = qr (eye (3), 'econ');
814+
%! assert (size (p), [3, 3]);
815+
%! [q, r, p] = qr (speye (3), 'econ');
816+
%! assert (size (p), [3, 3]);
817+
%! [q, r, p] = qr (eye (3), 0);
818+
%! assert (size (p), [1, 3]);
819+
%! [q, r, p] = qr (speye (3), 0);
820+
%! assert (size (p), [1, 3]);
821+
822+
## Test input validation
823+
%!error <Invalid call> qr ()
824+
%!error <Invalid call> qr (1,2,3,4)
825+
%!error <too many output arguments> [a,b,c,d] = qr (1)
805826
%!error <option string must be .*, not "foo"> qr (magic (3), "foo")
806-
%!error <option string must be .*, not "foo"> qr (magic (3), rand (3, 1), "foo")
807-
%!error <too many output arguments for dense A with B>
808-
%! [q, r, p] = qr (rand (3, 2), rand (3, 1));
809-
%!error <too many output arguments for dense A with B>
810-
%! [q, r, p] = qr (rand (3, 2), rand (3, 1), 0);
827+
%!error <option string must be .*, not "foo"> qr (magic (3), ones (3, 1), "foo")
828+
%!error <too many output arguments when called with A and B>
829+
%! [q, r, p] = qr (ones (3, 2), ones (3, 1));
830+
%!error <too many output arguments when called with A and B>
831+
%! [q, r, p] = qr (ones (3, 2), ones (3, 1), 0);
811832
812833
%!function retval = __testqr (q, r, a, p)
813834
%! tol = 100* eps (class (q));

0 commit comments

Comments
 (0)