comparison scripts/general/cplxpair.m @ 20508:4c2e76cbdc7d

cplxpair.m: Use tolerance that depends on Z to evaluate pairing (bug #45810). * cplxpair.m: Update docstring to explain how tolerance input is calculated. Validate TOL input is a positive scalar. Use standard code to find the first singleton dimension. When determing whether a conjugate pair exists, use a tolerance of TOL*eps (abs (z(i))). Use Octave coding convestions in BIST tests. Add BIST tests for tolerance input. Add BIST tests for input validation.
author Rik <rik@octave.org>
date Tue, 01 Sep 2015 08:07:57 -0700
parents 7503499a252b
children
comparison
equal deleted inserted replaced
20507:2d415c68213f 20508:4c2e76cbdc7d
18 18
19 ## -*- texinfo -*- 19 ## -*- texinfo -*-
20 ## @deftypefn {Function File} {} cplxpair (@var{z}) 20 ## @deftypefn {Function File} {} cplxpair (@var{z})
21 ## @deftypefnx {Function File} {} cplxpair (@var{z}, @var{tol}) 21 ## @deftypefnx {Function File} {} cplxpair (@var{z}, @var{tol})
22 ## @deftypefnx {Function File} {} cplxpair (@var{z}, @var{tol}, @var{dim}) 22 ## @deftypefnx {Function File} {} cplxpair (@var{z}, @var{tol}, @var{dim})
23 ## Sort the numbers @var{z} into complex conjugate pairs ordered by 23 ## Sort the numbers @var{z} into complex conjugate pairs ordered by increasing
24 ## increasing real part. 24 ## real part.
25 ## 25 ##
26 ## The negative imaginary complex numbers are placed first within each pair. 26 ## The negative imaginary complex numbers are placed first within each pair.
27 ## All real numbers (those with 27 ## All real numbers (those with
28 ## @code{abs (imag (@var{z}) / @var{z}) < @var{tol}}) are placed after the 28 ## @code{abs (imag (@var{z}) / @var{z}) < @var{tol}}) are placed after the
29 ## complex pairs. 29 ## complex pairs.
30 ## 30 ##
31 ## If @var{tol} is unspecified the default value is 100*@code{eps}. 31 ## @var{tol} is a weighting factor which determines the tolerance of matching.
32 ## The default value is 100 and the resulting tolerance for a given complex
33 ## pair is @code{100 * eps (abs (@var{z}(i))}.
32 ## 34 ##
33 ## By default the complex pairs are sorted along the first non-singleton 35 ## By default the complex pairs are sorted along the first non-singleton
34 ## dimension of @var{z}. If @var{dim} is specified, then the complex pairs are 36 ## dimension of @var{z}. If @var{dim} is specified, then the complex pairs are
35 ## sorted along this dimension. 37 ## sorted along this dimension.
36 ## 38 ##
44 ## cplxpair (exp(2i*pi*[0:4]'/5)) == exp(2i*pi*[3; 2; 4; 1; 0]/5) 46 ## cplxpair (exp(2i*pi*[0:4]'/5)) == exp(2i*pi*[3; 2; 4; 1; 0]/5)
45 ## @end smallexample 47 ## @end smallexample
46 ## @end deftypefn 48 ## @end deftypefn
47 49
48 ## FIXME: subsort returned pairs by imaginary magnitude 50 ## FIXME: subsort returned pairs by imaginary magnitude
49 ## FIXME: Why doesn't exp (2i*pi*[0:4]'/5) produce exact conjugates. Does 51 ## FIXME: Why doesn't exp (2i*pi*[0:4]'/5) produce exact conjugates? Does
50 ## FIXME: it in Matlab? The reason is that complex pairs are supposed 52 ## FIXME: it in Matlab? The reason is that complex pairs are supposed
51 ## FIXME: to be exact conjugates, and not rely on a tolerance test. 53 ## FIXME: to be exact conjugates, and not rely on a tolerance test.
52 54
53 ## 2006-05-12 David Bateman - Modified for NDArrays 55 ## 2006-05-12 David Bateman - Modified for NDArrays
54 56
56 58
57 if (nargin < 1 || nargin > 3) 59 if (nargin < 1 || nargin > 3)
58 print_usage (); 60 print_usage ();
59 endif 61 endif
60 62
61 if (length (z) == 0) 63 if (isempty (z))
62 y = zeros (size (z)); 64 y = zeros (size (z));
63 return; 65 return;
64 endif 66 endif
65 67
66 if (nargin < 2 || isempty (tol)) 68 if (nargin < 2 || isempty (tol))
67 if (isa (z, "single")) 69 tol = 100;
68 tol = 100 * eps("single"); 70 elseif (! isscalar (tol) || tol < 0)
69 else 71 error ("cplxpair: TOL must be a positive scalar number")
70 tol = 100*eps; 72 endif
73
74 nd = ndims (z);
75 if (nargin < 3)
76 ## Find the first singleton dimension.
77 sz = size (z);
78 (dim = find (sz > 1, 1)) || (dim = 1);
79 else
80 dim = floor (dim);
81 if (dim < 1 || dim > nd)
82 error ("cplxpair: invalid dimension DIM");
71 endif 83 endif
72 endif 84 endif
73 85
74 nd = ndims (z); 86 ## Move dimension to treat to first position, and convert to a 2-D matrix.
75 orig_dims = size (z);
76 if (nargin < 3)
77 ## Find the first singleton dimension.
78 dim = 0;
79 while (dim < nd && orig_dims(dim+1) == 1)
80 dim++;
81 endwhile
82 dim++;
83 if (dim > nd)
84 dim = 1;
85 endif
86 else
87 dim = floor (dim);
88 if (dim < 1 || dim > nd)
89 error ("cplxpair: invalid dimension along which to sort");
90 endif
91 endif
92
93 ## Move dimension to treat first, and convert to a 2-D matrix.
94 perm = [dim:nd, 1:dim-1]; 87 perm = [dim:nd, 1:dim-1];
95 z = permute (z, perm); 88 z = permute (z, perm);
96 sz = size (z); 89 sz = size (z);
97 n = sz(1); 90 n = sz(1);
98 m = prod (sz) / n; 91 m = prod (sz) / n;
101 ## Sort the sequence in terms of increasing real values. 94 ## Sort the sequence in terms of increasing real values.
102 [q, idx] = sort (real (z), 1); 95 [q, idx] = sort (real (z), 1);
103 z = z(idx + n * ones (n, 1) * [0:m-1]); 96 z = z(idx + n * ones (n, 1) * [0:m-1]);
104 97
105 ## Put the purely real values at the end of the returned list. 98 ## Put the purely real values at the end of the returned list.
106 cls = "double"; 99 cls = ifelse (isa (z, "single"), "single", "double");
107 if (isa (z, "single")) 100 [idxi, idxj] = find (abs (imag (z)) ./ (abs (z) + realmin (cls)) ...
108 cls = "single"; 101 < tol*eps (abs (z)));
109 endif
110 [idxi, idxj] = find (abs (imag (z)) ./ (abs (z) + realmin (cls)) < tol);
111 q = sparse (idxi, idxj, 1, n, m); 102 q = sparse (idxi, idxj, 1, n, m);
112 nr = sum (q, 1); 103 nr = sum (q, 1);
113 [q, idx] = sort (q, 1); 104 [q, idx] = sort (q, 1);
114 z = z(idx); 105 z = z(idx);
115 y = z; 106 y = z;
116 107
117 ## For each remaining z, place the value and its conjugate at the 108 ## For each remaining z, place the value and its conjugate at the start of
118 ## start of the returned list, and remove them from further 109 ## the returned list, and remove them from further consideration.
119 ## consideration.
120 for j = 1:m 110 for j = 1:m
121 p = n - nr(j); 111 p = n - nr(j);
122 for i = 1:2:p 112 for i = 1:2:p
123 if (i+1 > p) 113 if (i+1 > p)
124 error ("cplxpair: could not pair all complex numbers"); 114 error ("cplxpair: could not pair all complex numbers");
125 endif 115 endif
126 [v, idx] = min (abs (z(i+1:p) - conj (z(i)))); 116 [v, idx] = min (abs (z(i+1:p) - conj (z(i))));
127 if (v > tol) 117 if (v > tol*eps (abs (z(i))))
128 error ("cplxpair: could not pair all complex numbers"); 118 error ("cplxpair: could not pair all complex numbers");
129 endif 119 endif
130 if (imag (z(i)) < 0) 120 if (imag (z(i)) < 0)
131 y([i, i+1]) = z([i, idx+i]); 121 y([i, i+1]) = z([i, idx+i]);
132 else 122 else
157 %!shared z 147 %!shared z
158 %! z = exp (2i*pi*[4; 3; 5; 2; 6; 1; 0]/7); 148 %! z = exp (2i*pi*[4; 3; 5; 2; 6; 1; 0]/7);
159 %!assert (cplxpair (z(randperm (7))), z) 149 %!assert (cplxpair (z(randperm (7))), z)
160 %!assert (cplxpair (z(randperm (7))), z) 150 %!assert (cplxpair (z(randperm (7))), z)
161 %!assert (cplxpair (z(randperm (7))), z) 151 %!assert (cplxpair (z(randperm (7))), z)
162 %!assert (cplxpair ([z(randperm(7)),z(randperm(7))]), [z,z]) 152 %!assert (cplxpair ([z(randperm (7)), z(randperm (7))]), [z,z])
163 %!assert (cplxpair ([z(randperm(7)),z(randperm(7))],[],1), [z,z]) 153 %!assert (cplxpair ([z(randperm (7)), z(randperm (7))],[],1), [z,z])
164 %!assert (cplxpair ([z(randperm(7)).';z(randperm(7)).'],[],2), [z.';z.']) 154 %!assert (cplxpair ([z(randperm (7)).'; z(randperm (7)).'],[],2), [z.';z.'])
165 155
166 ## tolerance test 156 ## Test tolerance
167 %!assert (cplxpair ([1i, -1i, 1+(1i*eps)],2*eps), [-1i, 1i, 1+(1i*eps)]) 157 %!assert (cplxpair ([2000 * (1+eps) + 4j; 2000 * (1-eps) - 4j]), ...
158 %! [(2000 - 4j); (2000 + 4j)], 100*eps(200))
159 %!error <could not pair> cplxpair ([2000 * (1+eps) + 4j; 2000 * (1-eps) - 4j], 0);
168 160
161 %!error <could not pair> cplxpair ([2e6 + j; 2e6 - j; 1e-9 * (1 + j); 1e-9 * (1 - 2j)]);
162
163 ## Test input validation
164 %!error cplxpair ()
165 %!error cplxpair (1,2,3,4)
166 %!error <TOL must be .* positive> cplxpair (1, -1)
167 %!error <TOL must be .* scalar number> cplxpair (1, ones (2,2))
168 %!error <invalid dimension DIM> cplxpair (1, [], 3)
169