function [Abasis, Abasisi, Asub]= getLinearIndependent(A,ignore_constant_shift)
% [Abasis, Abasisi, Asub]= getLinearIndependent(A,ignore_constant_shift)
%
%
% Purpose: Takes in a matrix of column vectors and identifies
% the subset of columns that forms a linear independent basis (Abasis).
%
%
% It also clusters columns in the original matrix into groups of those that
% share linear dependence (Asub). (e.g. If col2 = 2*col1, then col1 and
% col2 would be grouped together).
%
%
% Usage:
% [Abasis, Abasisi, Asub]= getLinearIndependent(A)
% [Abasis, Abasisi, Asub]= getLinearIndependent(A,ignore_constant_shift)
%
% Inputs:
% A: Input matrix of numerics
%
% Inputs (Optional):
% ignore_constant_shift: Flag (true/false [default]) for ignoring constant term
% in determining independence (e.g. if col2 = 10-col1, col1 and col2 will
% be grouped together if true; otherwise separately if false).
%
% Outputs:
% Abasis: The subset of linearly independent vectors in A that form a
% basis of A.
%
% Abasisi: Index locations of original basis vectors in A, such
% that Abasis = A(:,Abasisi).
%
% Asub: Cell array with one element for each basis vector in A. Each cell
% in Asub identifies clusters of columns in the original matrix A that
% share linear dependence.
%
% Examples:
%
% % -----------------------
% % % % % Example 1: % % %
% % -----------------------
%
% A = [1, 9, 2, 3, 2; 2, 8, 2, 3, 4; 3, 7, 3, 4 6; 4, 6, 4, 5, 8 ; 5, 5, 5, 6, 10];
%
% % A =
% % 1 9 2 3 2
% % 2 8 2 3 4
% % 3 7 3 4 6
% % 4 6 4 5 8
% % 5 5 5 6 10
% %
% % Note that: col2 = 10 - col1
% % col4 = col3 + 1
% % col5 = col1*2
%
% [Abasis, Abasisi, Asub]= getLinearIndependent(A, true)
%
% % -----------------------
% % % % % Result 1: % % %
% % -----------------------
% % Abasis = % Subset of basis vectors
% % 1 2
% % 2 2
% % 3 3
% % 4 4
% % 5 5
% % Abasisi = % Indices of basis vectors
% % 1 3
% % Asub =
% % 1x2 cell array
% % [1x3 double] [1x2 double]
% % Asub{1} : [1, 2, 5] % Subset of columns described by 1st basis vector
% % Asub{2} : [3, 4] % Subset of columns described by 2nd basis vector
% %
% % -----------------------
% % % % % Example 2: % % %
% % -----------------------
%
% A2 = [ [2,2,2,2,2]', A]
%
% % A2 =
% % 2 1 9 2 3 2
% % 2 2 8 2 3 4
% % 2 3 7 3 4 6
% % 2 4 6 4 5 8
% % 2 5 5 5 6 10
% %
%
% [Abasis, Abasisi, Asub]= getLinearIndependent(A2, false)
%
% % -----------------------
% % % % % Result 2: % % %
% % -----------------------
% % Abasis =
% % 1 2 2
% % 2 2 2
% % 3 3 2
% % 4 4 2
% % 5 5 2
% % Abasisi =
% % 1 2 4
% % Asub =
% % 1x3 cell array
% % [1x3 double] [1x2 double] [4]
% % Asub{1} : [1, 3, 5]
% % Asub{2} : [2, 6]
% % Asub{3} : [4]
%
%
% Author: David Stanley, Boston University, 2017
%
% See also: getLinearIndependentCell, rref
if nargin < 2
ignore_constant_shift = false;
end
sz = size(A);
% Add a column of 1's to allow columns to be linear functions of
% eachother, offset by a constant
if ignore_constant_shift
A = [ones(sz(1),1), A];
end
% Put A in reduced row-echelon form.
[R,jb] = rref(A);
% Remove the 1st column if we inserted a constant column
Nnz = length(jb); % Number of non-zero rows in R matrix
if ignore_constant_shift
R = R(:,2:end); % Drop first column, corresponding to "constant" column
R(1:Nnz,:) = R(circshift(1:Nnz,-1),:); % Move row corresponding to "constant" column pivot point down to end; shift everything else up by one.
jb = jb(2:end); % Ignore the 1st column pivot point
jb = jb - 1; % Subtract 1 since we are dropping the 1st column
A = A(:,2:end); % Remove the constant column which we added artificially above.
end
% Start identifying clusters
Asub = cell(1,Nnz);
available_ind = true(1,sz(2));
for i = 1:Nnz
curr_cluster = R(i,:);
Asub{i} = find(abs(curr_cluster) > 0 & available_ind);
available_ind(Asub{i}) = false; % Remove from set of availble columns
end
% Since we artificially added in a column of constants, if there were
% no other columns linearly dependent on a constant value, then
% Asub{end} will be empty and we can drop it.
if ignore_constant_shift
if isempty(Asub{Nnz})
Asub = Asub(1:end-1);
end
end
Abasisi = zeros(1,length(Asub));
for i = 1:length(Asub)
Abasisi(i) = Asub{i}(1);
end
Abasis = A(:,Abasisi);
end