Skip to content
Snippets Groups Projects
Commit dc0cef11 authored by Achilles Kappis's avatar Achilles Kappis
Browse files

Change the diagCorr function to diagMetric because the former did not provide good results

parent aa3aac23
No related branches found
No related tags found
No related merge requests found
......@@ -3,13 +3,13 @@
% Author: Achilles Kappis
% e-mail: axilleaz@protonmail.com
%
% Date: 17/02/2025 (DD/MM/YYYY)
% Date: 18/02/2025 (DD/MM/YYYY)
%
% Copyright: MIT
% --------------------------------------------------
% Functionality: Calculate a "diagonality" measure. This provides a notion
% "how diagonal" a matrix is. In practice this is the
% correlation coefficient of a matrix to a diagonal matrix.
% Functionality: Calculate a "diagonality" measure based on the norm of the
% difference of between a matrix and the same matrix with
% its principal diagonal removed.
% --------------------------------------------------
% Input
%
......@@ -17,7 +17,25 @@
% be either a 2D array (matrix) or a 3D array (stacked
% matrices), in which case the first two dimensions will be
% treated as matrices of which the "diagonality" will be
% checked. The matrices must be square.
% checked. The matrices must be square (the first two
% dimensions must be equal).
%
% matNorm [numeric/char/string] (Optional): The norm to be used in the
% calculation of the diagonality
% metric. This can take one of
% the values 1, 2, and Inf, that
% define the L1, L2 or Linf
% norms, or be one of the strings
% "Fro", or "Frobenius" (not
% case-sensitive) to use the
% Frobenius norm.
% [Default: "Fro"].
%
% normalise [logical] (Optional): Whether to normalise the result with the
% norm of the matrix. This will provide a
% results between 0, when all the elements
% along the diagonal are zero and 1 when
% the matrix is diagonal.
%
% --------------------------------------------------
% Output
......@@ -31,15 +49,17 @@
% --------------------------------------------------
% Notes
%
% - The metric is calculated as provided in the Mathematics StackExchange
% at https://math.stackexchange.com/a/1393907.
% - The metric is calculated as norm(A - diag(diag(A))), where the
% norm used is the one defined by the argument "matNorm". If "normalise"
% is true, the metric is calculated as
% 1 - norm(A - diag(diag(A))/norm(A).
%
% --------------------------------------------------
function [diagonality] = diagCorr(mat)
function diagonality = diagMetric(mat, matNorm, normalise)
% ====================================================
% Check for number of arguments
% ====================================================
narginchk(1, 1);
narginchk(1, 3);
nargoutchk(0, 1);
% ====================================================
......@@ -48,34 +68,40 @@ function [diagonality] = diagCorr(mat)
% Validate mandatory arguments
validateattributes(mat, "numeric", {'3d', 'square', 'nonempty'}, mfilename, "Matrices to be checked for diagonality", 1);
% Validate optional arguments
if nargin > 1 && ~isempty(matNorm)
% Make sure we have either a string-like object or a numeric scalar
validateattributes(matNorm, {'char', 'string', 'numeric'}, {'scalar', 'nonempty', 'real'}, mfilename, "The matrix norm to be used", 2);
% ====================================================
% Calculate auxiliary vectors
% ====================================================
elemSum = ones(1, size(mat, 1));
r = 1:length(elemSum);
r2 = r.^2;
if nnz(matNorm == [1, 2, Inf]) == 0 && nnz(strcmpi(matNorm, ["Fro", "Frobenius"])) == 0
error("The 'matNorm' argument can be either 1, 2, Inf, 'Fro', or 'Frobenius'.");
end
if ~isnumeric(matNorm)
matNorm = lower(matNorm);
end
else
matNorm = "fro";
end
if nargin > 2 && ~isempty(normalise)
validateattributes(normalise, {'logical', 'numeric'}, {'scalar', 'nonempty', 'real', 'finite', 'nonnan'}, mfilename, "Normalisation flag", 3);
normalise = logical(normalise);
else
normalise = true;
end
% ====================================================
% Calculate correlation coefficient to diagonal matrix
% Calculate the metric
% ====================================================
% Go through the matrices
for idx = size(mat, 3):-1:1
% Get the matrix at the current index
tmpMat = mat(:, :, idx);
% Calculate auxiliary values
n = sum(tmpMat, "all");
sumRow = r * tmpMat * elemSum';
sumCol = elemSum * tmpMat * r';
sumRow2 = r2 * tmpMat * elemSum';
sumCol2 = elemSum * tmpMat * r2';
sumRowCol = r * tmpMat * r';
diagonality(idx) = norm(tmpMat - diag(diag(tmpMat)), matNorm);
diagonality(idx) = (n * sumRowCol - sumRow * sumCol)/(sqrt(n * sumRow2 - (sumRow^2)) * sqrt(n * sumCol2 - (sumCol^2)));
if normalise
diagonality(idx) = 1 - diagonality(idx)/norm(tmpMat, matNorm);
end
end
% Get rid of possible imaginary residuals
diagonality = real(diagonality);
end
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment