######################################################################## ## ## Copyright (C) 2000-2020 The Octave Project Developers ## ## See the file COPYRIGHT.md in the top-level directory of this ## distribution or . ## ## This file is part of Octave. ## ## Octave is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## Octave is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with Octave; see the file COPYING. If not, see ## . ## ######################################################################## ## -*- texinfo -*- ## @deftypefn {} {} uniquetol (@var{x}, @var{tol}) ## @deftypefnx {} {} uniquetol (@var{x}) ## @deftypefnx {} {} uniquetol (@var{x}, @var{tol}, "rows") ## @deftypefnx {} {} uniquetol (@dots{}, "prop1", "value1", @dots{}) ## @deftypefnx {} {[@var{C}, @var{IA}, @var{IC}] =} uniquetol (@dots{}) ## @deftypefnx {} {[@var{C}, @var{IA}, @var{IC}] =} uniquetol (@dots{}, "OutputAllIndices", true) ## Return the unique elements of @var{x} using tolerance tol. ## Two values, u and v, are within tolerance if abs(u-v) <= tol*max(abs(A(:))) ## ## If the tolerance is not provided: ## tol=1e-12 for double precision inputs ## tol=1e-6 for single precision inputs ## ## If the input @var{x} is a column vector then return a column vector; ## Otherwise, return a row vector. @var{x} must be numeric. ## ## If the optional argument @qcode{"rows"} is given then return the unique ## rows of @var{x}, @qcode{"ByRows"}, true does the same thing. ## ## If requested, return column index vectors @var{i} and @var{j} such that ## @code{@var{y} = @var{x}(@var{IA})} and @code{@var{x} = @var{y}(@var{IC})}. ## ## If the option "OutputAllIndices", true is provided @var{IA} is a cell array ## that contains the indices for all elements in A that are within tolerance of ## a value in C. That is, each cell in IA corresponds to a value in C, and the ## values in each cell correspond to locations in A. ## ## If the option "DataScale", scalar or vector if the "ByRows" option is true, ## with a length equal to the number of rows is provided changes the tolerance ## test to be abs(u-v) <= tol*DS. ## ## Example : ## ## @example ## @group ## x = (1:6)*pi; ## y = 10.^log10(x); ## C = uniquetol([x, y]) ## @result{} [3.1416, 6.2832, 9.4248, 12.5664, 15.7080, 18.8496] ## D = unique([x, y]) ## @result{} [3.1416, 6.2832, 9.4248, 12.5664, 12.5664, 15.7080, ## 18.8496, 18.8496] ## @end group ## @end example ## ## @seealso{unique, union, intersect, setdiff, setxor, ismember} ## @end deftypefn function [C,IA,IC]=uniquetol (A,varargin) if (nargin < 1) print_usage (); endif maxabsA = max (abs (A(!isinf (A))(:))); ByRows = false; OutputAllIndices = false; DataScale = maxabsA; sizeA = size (A); if (isempty (A)) C = A; IA = []; IC = []; return endif if (nargin ()==1 || !(isnumeric (varargin{1}) && isscalar (varargin{1}))) if isa (A, "double") tol = 1e-12; elseif (isa (A, "single")) tol = 1e-6; else error ("uniquetol: Precision of A must be double or single."); endif else tol = varargin{1}; endif for k = 1:length(varargin) if (isnumeric (varargin{k})) continue elseif (ischar (varargin{k})) if strcmpi (varargin{k}, "rows") ByRows = true; elseif (strcmpi (varargin{k}, "byrows")) ByRows = logical (varargin{k+1}); elseif (strcmpi (varargin{k}, "OutputAllIndices")) OutputAllIndices = logical (varargin{k+1}); elseif (strcmpi (varargin{k}, "DataScale")) DataScale = varargin{k+1}(:).'; columnsDataScale = columns (DataScale); if (!(columnsDataScale == 1 || columnsDataScale == sizeA(2))) error ("uniquetol: Incorrect size of data scale."); endif endif endif endfor if (!ByRows) A = A(:); endif points = rows (A); d = columns (A); Iall = zeros (points,1); I = nan (d,1); IA = {}; J = nan (d,1); j=1; ii=0; tolDataScale = tol * DataScale; for i=1:points if (any (Iall == i)) continue else equ = all (abs (A - A(i,:)) <= tolDataScale,2); equ(i,1) = equ(i,1) || any (isnan (A(i,:)) | isinf (A(i,:)),2); sumeq=sum (equ); ia = find (equ); if (OutputAllIndices) IA{end+1} = ia; endif Iall(ii+(1:sumeq)) = ia; I(j) = ia(1); J(equ) = j; ii+= sumeq; j+= 1; endif endfor I(isnan (I)) = []; J(isnan (J)) = []; C = A(I,:); if (sizeA(1) == 1 && sizeA(2) > 1 && !ByRows) C = C.'; I = I.'; J = J.'; endif if (!OutputAllIndices) IA = I(1:j-1); endif IC = J; endfunction %!assert (uniquetol ([1 1 2; 1 2 1; 1 1 2]), [1;2]) %!assert (uniquetol ([1 1 2; 1 0 1; 1 1 2], 1e-12, "rows"), [1 1 2; 1 0 1]) %!assert (uniquetol ([]), []) %!assert (uniquetol ([1]), [1]) %!assert (uniquetol ([1 2]), [1 2]) %!assert (uniquetol ([1;2]), [1;2]) %!assert (uniquetol ([1,NaN,Inf,NaN,Inf]), [1,NaN,Inf,NaN,Inf]) %!assert (uniquetol (zeros (1,0)), zeros (1,0)) %!assert (uniquetol (zeros (1,0), 1e-12, "rows"), zeros (1,0)) %!assert (uniquetol ([1,2,2,3,2,4], 1e-12, "rows"), [1,2,2,3,2,4]) %!assert (uniquetol ([1,2,2,3,2,4]), [1,2,3,4]) %!assert (uniquetol ([1,2,2,3,2,4]', 1e-12, "rows"), [1;2;3;4]) %!assert (uniquetol (sparse ([2,0;2,0])), sparse([2;0])) %!assert (uniquetol (sparse ([1,2;2,3])), sparse([1;2;3])) %!assert (uniquetol ([1,2,2,3,2,4]', 1e-12, "rows"), [1;2;3;4]) %!assert (uniquetol (single ([1,2,2,3,2,4]), 1e-12, "rows"), single ([1,2,2,3,2,4])) %!assert (uniquetol (single ([1,2,2,3,2,4])), single ([1,2,3,4])) %!assert (uniquetol (single ([1,2,2,3,2,4]'), 1e-12, "rows"), single ([1;2;3;4])) %!test %! [y,i,j] = uniquetol ([1,1,2,3,3,3,4]); %! assert (y, [1,2,3,4]); %! assert (i, [1;3;4;7]); %! assert (j, [1;1;2;3;3;3;4]); %!test %! A = [1,2,3; 1,2,3]; %! [y,i,j] = uniquetol (A, "rows"); %! assert (y, [1,2,3]); %! assert (A(i,:), y); %! assert (y(j,:), A); %!test %! x = (1:6)'*pi; %! y = 10.^log10 (x); %! C = uniquetol ([x;y]); %! assert (C, x); %!test %! A = [0.05 0.11 0.18; 0.18 0.21 0.29; 0.34 0.36 0.41; 0.46 0.52 0.76]; %! B = log10(10.^A); %! C = uniquetol ([A;B], 'rows'); %! assert (C, A); %!test %! A = [0.05 0.11 0.18; 0.18 0.21 0.29; 0.34 0.36 0.41; 0.46 0.52 0.76]; %! B = log10(10.^A); %! C = uniquetol ([A;B], 'ByRows', true); %! assert (C, A); %!test %! A = [.1 .2 .3 10]; %! [C,IA,IC] = uniquetol (A,.1,'OutputAllIndices',true); %! assert (C, [.1, 10]); %! assert (IA, {(1:3)',4}); %! assert (IC, [1;1;1;2]); %!test %! x = 10^10; %! C = uniquetol ([x, exp(log(x))],1e-6,'DataScale',1); %! assert (C, [x, exp(log(x))]); %!error uniquetol ()