comparison scripts/linear-algebra/cross.m @ 33279:308060bc6b27 stable

cross: Improve input validation and expand BISTs. (bug #65544) * cross.m: Add input validation check for input dim to ensure it's a real integer valued numeric scalar. Change vector check from columns and rows count to iscolumn and isrow check to avoid passing 1-by-n-by-m and n-by-1-by-m arrays through to transpose operator. Move size and ndims check earlier to reduce repeated function calls. Reduce large BISTs tolerances on simple tests from 2e-8 to eps. Change docstring mention of matrices to arrays, add note about error if sz(dim) != 3, and add additional example. Add BISTs to verify dim input validation and to catch vector transpose check. Add BISTs for all error messages and warnings and remove FIXME note requesting the same. * NEWS.9.md: Note change to dim input validation.
author Nicholas R. Jankowski <jankowski.nicholas@gmail.com>
date Sun, 31 Mar 2024 22:48:01 -0400
parents 2e484f9f1f18
children 2aea823a9973
comparison
equal deleted inserted replaced
33275:32669ebfd773 33279:308060bc6b27
27 ## @deftypefn {} {@var{z} =} cross (@var{x}, @var{y}) 27 ## @deftypefn {} {@var{z} =} cross (@var{x}, @var{y})
28 ## @deftypefnx {} {@var{z} =} cross (@var{x}, @var{y}, @var{dim}) 28 ## @deftypefnx {} {@var{z} =} cross (@var{x}, @var{y}, @var{dim})
29 ## Compute the vector cross product of two 3-dimensional vectors @var{x} and 29 ## Compute the vector cross product of two 3-dimensional vectors @var{x} and
30 ## @var{y}. 30 ## @var{y}.
31 ## 31 ##
32 ## If @var{x} and @var{y} are matrices, the cross product is applied along the 32 ## If @var{x} and @var{y} are arrays, the cross product is applied along the
33 ## first dimension with three elements. 33 ## first dimension with three elements.
34 ## 34 ##
35 ## The optional argument @var{dim} forces the cross product to be calculated 35 ## The optional argument @var{dim} forces the cross product to be calculated
36 ## along the specified dimension. 36 ## along the specified dimension. An error will be produced if the specified
37 ## dimension is not three elements in size.
37 ## 38 ##
38 ## Example Code: 39 ## Example Code:
39 ## 40 ##
40 ## @example 41 ## @example
41 ## @group 42 ## @group
42 ## cross ([1, 1, 0], [0, 1, 1]) 43 ## cross ([1, 1, 0], [0, 1, 1])
43 ## @result{} 44 ## @result{}
44 ## 1 -1 1 45 ## 1 -1 1
46 ## @end group
47 ## @end example
48 ##
49 ## @example
50 ## @group
51 ## cross (magic (3), eye (3), 2)
52 ## @result{}
53 ## 0 6 -1
54 ## -7 0 3
55 ## 9 -4 0
45 ## @end group 56 ## @end group
46 ## @end example 57 ## @end example
47 ## 58 ##
48 ## @seealso{dot, curl, divergence} 59 ## @seealso{dot, curl, divergence}
49 ## @end deftypefn 60 ## @end deftypefn
52 63
53 if (nargin < 2) 64 if (nargin < 2)
54 print_usage (); 65 print_usage ();
55 endif 66 endif
56 67
57 if (ndims (x) < 3 && ndims (y) < 3 && nargin < 3) 68 nd = ndims (x);
69
70 if (nd < 3 && ndims (y) < 3 && nargin < 3)
58 ## COMPATIBILITY -- opposite behavior for cross(row,col) 71 ## COMPATIBILITY -- opposite behavior for cross(row,col)
59 ## Swap x and y in the assignments below to get the matlab behavior. 72 ## Swap x and y in the assignments below to get the matlab behavior.
60 ## Better yet, fix the calling code so that it uses conformant vectors. 73 ## Better yet, fix the calling code so that it uses conformant vectors.
61 if (columns (x) == 1 && rows (y) == 1) 74 if (iscolumn (x) && isrow (y))
62 warning ("cross: taking cross product of column by row"); 75 warning ("cross: taking cross product of column by row");
63 y = y.'; 76 y = y.';
64 elseif (rows (x) == 1 && columns (y) == 1) 77 elseif (isrow (x) && iscolumn (y))
65 warning ("cross: taking cross product of row by column"); 78 warning ("cross: taking cross product of row by column");
66 x = x.'; 79 x = x.';
67 endif 80 endif
68 endif 81 endif
69 82
83 sz = size (x);
84
70 if (nargin == 2) 85 if (nargin == 2)
71 dim = find (size (x) == 3, 1); 86 dim = find (sz == 3, 1);
72 if (isempty (dim)) 87 if (isempty (dim))
73 error ("cross: must have at least one dimension with 3 elements"); 88 error ("cross: must have at least one dimension with 3 elements");
74 endif 89 endif
75 else 90 else
76 if (size (x, dim) != 3) 91 if (! (isnumeric (dim) && dim > 0 && isreal (dim) && ...
77 error ("cross: dimension DIM must have 3 elements"); 92 isscalar (dim) && dim == fix (dim)))
78 endif 93 error ("cross: DIM must be a positive scalar whole number");
94 endif
95
96 if (dim > nd || sz(dim) != 3)
97 error ("cross: must have three elements in dimension DIM");
98 endif
79 endif 99 endif
80 100
81 nd = ndims (x);
82 sz = size (x);
83 idx2 = idx3 = idx1 = {':'}(ones (1, nd)); 101 idx2 = idx3 = idx1 = {':'}(ones (1, nd));
84 idx1(dim) = 1; 102 idx1(dim) = 1;
85 idx2(dim) = 2; 103 idx2(dim) = 2;
86 idx3(dim) = 3; 104 idx3(dim) = 3;
87 105
99 117
100 endfunction 118 endfunction
101 119
102 120
103 %!test 121 %!test
104 %! x = [1 0 0]; 122 %! x = [1, 0, 0];
105 %! y = [0 1 0]; 123 %! y = [0, 1, 0];
106 %! r = [0 0 1]; 124 %! r = [0, 0, 1];
107 %! assert (cross (x, y), r, 2e-8); 125 %! assert (cross (x, y), r, eps);
108 126
109 %!test 127 %!test
110 %! x = [1 2 3]; 128 %! x = [1, 2, 3];
111 %! y = [4 5 6]; 129 %! y = [4, 5, 6];
112 %! r = [(2*6-3*5) (3*4-1*6) (1*5-2*4)]; 130 %! r = [(2*6-3*5), (3*4-1*6), (1*5-2*4)];
113 %! assert (cross (x, y), r, 2e-8); 131 %! assert (cross (x, y), r, eps);
114 132
115 %!test 133 %!test
116 %! x = [1 0 0; 0 1 0; 0 0 1]; 134 %! x = [1, 0, 0; 0, 1, 0; 0, 0, 1];
117 %! y = [0 1 0; 0 0 1; 1 0 0]; 135 %! y = [0, 1, 0; 0, 0, 1; 1, 0, 0];
118 %! r = [0 0 1; 1 0 0; 0 1 0]; 136 %! r = [0, 0, 1; 1, 0, 0; 0, 1, 0];
119 %! assert (cross (x, y, 2), r, 2e-8); 137 %! assert (cross (x, y, 2), r, eps);
120 %! assert (cross (x, y, 1), -r, 2e-8); 138 %! assert (cross (x, y, 1), -r, eps);
139
140 %!test <*65527>
141 %! x = cat (3, [1, 1, 1]', [1, 1, 1]');
142 %! y = cat (3, [1, 0, 0], [1, 0, 0]);
143 %! fail ("cross (x, y)", "X and Y must have the same dimensions");
144 %! fail ("cross (y, x)", "X and Y must have the same dimensions");
121 145
122 ## Test input validation 146 ## Test input validation
123 %!error <Invalid call> cross () 147 %!error <Invalid call> cross ()
124 %!error <Invalid call> cross (1) 148 %!error <Invalid call> cross (1)
125 ## FIXME: Need tests for other error() conditions and warning() calls. 149 %!error <must have at least one dimension with 3 elements> cross (0, 0)
126 %!error <must have at least one dimension with 3 elements> cross (0,0) 150 %!error <must have at least one dimension with 3 elements> cross ([1, 2], [3, 4])
151 %!error <must have at least one dimension with 3 elements> cross ([1, 2], [3, 4, 5])
152 %!error <must have three elements in dimension DIM> cross (0, 0, 1)
153 %!error <must have three elements in dimension DIM> cross ([1, 2, 3], [1, 2, 3], 1)
154 %!error <must have three elements in dimension DIM> cross ([1, 2, 3], [1, 2, 3], 9)
155 %!error <must have three elements in dimension DIM> cross (magic (3), magic (3), 4)
156 %!error <DIM must be a positive scalar whole number> cross ([1, 2, 3], [4, 5, 6], {1})
157 %!error <DIM must be a positive scalar whole number> cross ([1, 2, 3], [4, 5, 6], "a")
158 %!error <DIM must be a positive scalar whole number> cross ([1, 2, 3], [4, 5, 6], true)
159 %!error <DIM must be a positive scalar whole number> cross ([1, 2, 3], [4, 5, 6], [1, 2])
160 %!error <DIM must be a positive scalar whole number> cross ([1, 2, 3], [4, 5, 6], 0)
161 %!error <DIM must be a positive scalar whole number> cross ([1, 2, 3], [4, 5, 6], -1)
162 %!error <DIM must be a positive scalar whole number> cross ([1, 2, 3], [4, 5, 6], 1.5)
163 %!error <DIM must be a positive scalar whole number> cross ([1, 2, 3], [4, 5, 6], 2i)
164 %!error <X and Y must have the same dimensions> cross ([1, 2, 3], [3, 4])
165 %!warning <taking cross product of column by row> cross ([1, 2, 3]', [4, 5, 6]);
166 %!warning <taking cross product of row by column> cross ([1, 2, 3], [4, 5, 6]');