function cholmod_updown_demo
%CHOLMOD_UPDOWN_DEMO a demo for CHOLMOD's update/downdate & row add/delete
%
% Provides a short demo of CHOLMOD's update/downdate and row add/delete
% functions.
%
% Example:
%   cholmod_updown_demo
%
% See also cholmod_demo

% Copyright 2006-2023, Timothy A. Davis, All Rights Reserved.
% SPDX-License-Identifier: GPL-2.0+

fprintf ('\n\n------------------------------------------------------------\n') ;
fprintf ('demo of CHOLMOD update/downdate and row add/delete functions\n') ;
fprintf ('------------------------------------------------------------\n\n') ;

% A is the matrix used in the MATLAB 'bench' demo
A = delsq (numgrid ('L', 300)) ;
n = size (A,1) ;
% make b small so we can use absolute error norms, norm (A*x-b)
b = rand (n,1) / n ;

% L*D*L' = A(p,p).  The 2nd argument is zero if A is positive definite.
[LD,ignore,p] = ldlchol (A) ;                                               %#ok

% solve Ax=b using the L*D*L' factorization of A(p,p)
c = b (p) ;
x = ldlsolve (LD, c) ;
x (p) = x ;
err = norm (A*x-b) ;
fprintf ('norm (A*x-b) using ldlsolve:   %g\n', err) ;
if (err > 1e-12)
    error ('!')
end

% solve again, just using backslash
tic
x = A\b ;
t1 = toc ;
err = norm (A*x-b) ;
fprintf ('norm (A*x-b) using backslash:  %g\n', err) ;
fprintf ('time for x=A\\b: %g\n', t1) ;
if (err > 1e-12)
    error ('!')
end

% check the norm of LDL'-S
[L,D] = ldlsplit (LD) ;                                                     %#ok
S = A (p,p) ;
% err = norm (L*D*L' - S, 1) ;
fprintf ('nnz (L) for original matrix:   %d\n', nnz (L)) ;

% rank-1 update of the L*D*L' factorization
% A becomes A2=A+W*W', and S becomes S2=S+C*C'.  Some fillin occurs.
fprintf ('\n\n------------------------------------------------------------\n') ;
fprintf ('Update A to A+W*W'', and the permuted S=A(p,p) becomes S+C*C'':\n') ;
W = sparse ([1 2 3 152], [1 1 1 1], [5 -1 -1 -1], n, 1)                     %#ok
C = W (p,:)                                                                 %#ok
tic
LD2 = ldlupdate (LD, C, '+') ;
t2 = toc ;
S2 = S + C*C' ;
A2 = A + W*W' ;
[L,D] = ldlsplit (LD2) ;
err = norm (L*D*L' - S2, 1) ;
fprintf ('norm (LDL-S2) after update:    %g\n', err) ;
fprintf ('nnz (L) after rank-1 update:   %d\n', nnz (L)) ;
if (err > 1e-12)
    error ('!')
end

% solve A2*x=b using the updated LDL factorization
tic
c = b (p) ;
x = ldlsolve (LD2, c) ;
x (p) = x ;
t3 = toc ;
err = norm (A2*x-b) ;
fprintf ('norm (A*x-b) using ldlsolve:   %g\n', err) ;
if (err > 1e-12)
    error ('!')
end
fprintf ('time to solve x=A\\b using update: %g\n', t2 + t3) ;

% solve again, just using backslash
tic
x = A2\b ;
t1 = toc ;
err = norm (A2*x-b) ;
fprintf ('norm (A*x-b) using backslash:  %g\n', err) ;
if (err > 1e-12)
    error ('!')
end
fprintf ('time for x=A\\b: %g\n', t1) ;
fprintf ('speedup of update vs backslash: %g\n', t1 / (t2 + t3)) ;

% invert the permutation
invp = zeros (1,n) ;
invp (p) = 1:n ;

% delete row 3 of A to get A3.  This corresponds to row invp(3) in S and LDL
fprintf ('\n\n------------------------------------------------------------\n') ;
fprintf ('Delete row 3\n') ;
k = 3 ;
pk = invp (k) ;
I = speye (n) ;
A3 = A ;
A3 (:,k) = I (:,k) ;
A3 (k,:) = I (k,:) ;
tic
LD3 = ldlrowmod (LD, pk) ;
t2 = toc ;

S3 = S ;
S3 (:,pk) = I (:,pk) ;
S3 (pk,:) = I (pk,:) ;
[L,D] = ldlsplit (LD3) ;
err = norm (L*D*L' - S3, 1) ;
fprintf ('norm (LDL-S3) after row del:   %g\n', err) ;
if (err > 1e-12)
    error ('!')
end

% solve A3*x=b using the modified LDL factorization
tic
c = b (p) ;
x = ldlsolve (LD3, c) ;     % x = S3\c
x (p) = x ;                 % now x = A3\b
t3 = toc ;
err = norm (A3*x-b) ;
fprintf ('norm (A3*x-b) after row del:   %g\n', err) ;
fprintf ('nnz (L) after row del:         %d\n', nnz (L)) ;
if (err > 1e-12)
    error ('!')
end

% solve again using backslash
tic
x = A3\b ;
t1 = toc ;
err = norm (A3*x-b) ;
fprintf ('norm (A3*x-b) with backslash:  %g\n', err) ;
fprintf ('time for x=A\\b with backslash %g\n', t1) ;
fprintf ('time for x=A\\b using rowdel:  %g\n', t2 + t3) ;
fprintf ('speedup of rowdel vs backslash: %g\n', t1 / (t2 + t3)) ;

% add row 3 back to A3 to get A4.  This corresponds to row invp(3) in S and LDL
fprintf ('\n\n------------------------------------------------------------\n') ;
fprintf ('Add row 3 back\n') ;
W = sparse ([1 3 7 9], [1 1 1 1], [-1 8 -1 -1], n, 1) ;
A4 = A3 ;
A4 (:,k) = W ;
A4 (k,:) = W' ;
C = W (p) ;             % permuted version of row/column 3
S4 = S3 ;
S4 (:,pk) = C ;
S4 (pk,:) = C' ;

tic
LD4 = ldlrowmod (LD3, pk, C) ;
t2 = toc ;
fprintf ('nnz (L) after row add:         %d\n', nnz (L)) ;

[L,D] = ldlsplit (LD4) ;
err = norm (L*D*L' - S4, 1) ;
fprintf ('norm (LDL-S4) after row add:   %g\n', err) ;
if (err > 1e-12)
    error ('!')
end

% solve A4*x=b using the modified LDL factorization
tic
c = b (p) ;
x = ldlsolve (LD4, c) ;     % x = S4\c
x (p) = x ;                 % now x = A4\b
t3 = toc ;
err = norm (A4*x-b) ;
fprintf ('norm (A4*x-b) after row add:   %g\n', err) ;
if (err > 1e-12)
    error ('!')
end

% solve A4*x=b using backslash
tic
x = A4\b ;
t1 = toc ;
err = norm (A4*x-b) ;
fprintf ('norm (A4*x-b) with backslash:  %g\n', err) ;
if (err > 1e-12)
    error ('!')
end

fprintf ('time for x=A\\b with backslash %g\n', t1) ;
fprintf ('time for x=A\\b using rowadd:  %g\n', t2 + t3) ;
fprintf ('speedup of rowadd vs backslash: %g\n', t1 / (t2 + t3)) ;

