Testing for quad.form() et seq

In versions prior to 1.2-19, the emulator package included a serious bug in the quad.form() family of functions in which the complex conjugate of the correct answer was returned (which did not matter in my usual use-case because my matrices were Hermitian). This short vignette demonstrates that the bug has been fixed. Note that the fix was considerably more complicated than simply returning the complex conjugate of the old functions’ value, which would have been terribly inefficient. The actual fix avoids taking more conjugates than absolutely necessary. The vignette checks all the functions in the series, including the ones that have not been changed such as quad.form.inv(). First load the package:

library("emulator")

We need a helper function to create random complex matrices (NB: we cannot use the cmvnorm package because that depends on the emulator package):

rcm <- function(row,col){
   matrix(rnorm(row*col)+1i*rnorm(row*col),row,col)
}

Then use this function to define a square matrix M with complex entries (NB: not Hermitian!), and a couple of rectangular matrices, also complex:

rcm <- function(row,col){matrix(rnorm(row*col)+1i*rnorm(row*col),row,col)}
M <- rcm(2,2)
x <- rcm(2,3)
y <- rcm(3,2)
x1 <- rcm(2,3)
y1 <- rcm(3,2)

Set up a numerical tester function:

tester <- function(a,b,TOL=1e-13){stopifnot(all(abs(a-b)< TOL))}

(previous versions used a tolerance of 1e-15, which was occasionally not met). Now test each function:

Test of ht(x) = \(x^*\) = \(\overline{x'}\) (Hermitian transpose):

ht(x)=t(Conj(x))

(jj1 <- Conj(t(x)))
#>                     [,1]                  [,2]
#> [1,] -0.204576-0.640419i -1.4525117-0.9493113i
#> [2,] -2.153785-0.188260i -0.7882761+0.7939511i
#> [3,] -0.613245+1.024112i -0.7938764+2.7810537i
(jj2 <- t(Conj(x)))
#>                     [,1]                  [,2]
#> [1,] -0.204576-0.640419i -1.4525117-0.9493113i
#> [2,] -2.153785-0.188260i -0.7882761+0.7939511i
#> [3,] -0.613245+1.024112i -0.7938764+2.7810537i
(jj3 <- ht(x))
#>                     [,1]                  [,2]
#> [1,] -0.204576-0.640419i -1.4525117-0.9493113i
#> [2,] -2.153785-0.188260i -0.7882761+0.7939511i
#> [3,] -0.613245+1.024112i -0.7938764+2.7810537i
tester(jj1,jj3)
tester(jj2,jj3)

Test of cprod() = \(x^*y\):

cprod(x,y)=crossprod(Conj(x),y)

(jj1 <- ht(x) %*% x1)
#>                     [,1]                 [,2]                [,3]
#> [1,]  1.029839-0.394071i 0.0195763+0.4164957i -3.052500+2.970614i
#> [2,] -3.076731+1.438518i 1.7931570-1.4349116i -0.134602+2.195432i
#> [3,] -0.117493-1.305357i 0.0556772-0.2328632i  6.541921+3.729678i
(jj2 <- cprod(x,x1))
#>                     [,1]                 [,2]                [,3]
#> [1,]  1.029839-0.394071i 0.0195763+0.4164957i -3.052500+2.970614i
#> [2,] -3.076731+1.438518i 1.7931570-1.4349116i -0.134602+2.195432i
#> [3,] -0.117493-1.305357i 0.0556772-0.2328632i  6.541921+3.729678i
tester(jj1,jj2)

Test of tcprod() = \(x y^*\):

tcprod(x,y)=crossprod(x,Conj(y))

(jj1 <- ht(x1) %*% x)
#>                       [,1]                [,2]                  [,3]
#> [1,]  1.0298385+0.3940714i -3.076731-1.438518i -0.1174925+1.3053575i
#> [2,]  0.0195763-0.4164957i  1.793157+1.434912i  0.0556772+0.2328632i
#> [3,] -3.0525005-2.9706144i -0.134602-2.195432i  6.5419211-3.7296778i
(jj2 <- cprod(x1,x))
#>                       [,1]                [,2]                  [,3]
#> [1,]  1.0298385+0.3940714i -3.076731-1.438518i -0.1174925+1.3053575i
#> [2,]  0.0195763-0.4164957i  1.793157+1.434912i  0.0556772+0.2328632i
#> [3,] -3.0525005-2.9706144i -0.134602-2.195432i  6.5419211-3.7296778i
tester(jj1,jj2)

Test of quad.form() = \(x^*Mx\):

quad.form(M,x)=crossprod(crossprod(M,Conj(x)),x))

(jj1 <- ht(x) %*% M %*% x)
#>                     [,1]               [,2]                [,3]
#> [1,] -0.361280-1.642215i 1.317059+1.932716i  2.265098-0.183945i
#> [2,] -2.598642+1.800000i 3.086616+0.822312i -2.943405-4.593919i
#> [3,] -1.780870+1.316800i 2.353631-3.665747i -1.385598-2.269765i
(jj2 <- quad.form(M,x))
#>                     [,1]               [,2]                [,3]
#> [1,] -0.361280-1.642215i 1.317059+1.932716i  2.265098-0.183945i
#> [2,] -2.598642+1.800000i 3.086616+0.822312i -2.943405-4.593919i
#> [3,] -1.780870+1.316800i 2.353631-3.665747i -1.385598-2.269765i
tester(jj1,jj2)

Test of quad.form.inv() = \(x^*M^{-1}x\):

quad.form.inv(M,x)=cprod(x,solve(M,x))

(jj1 <- ht(x) %*% solve(M) %*% x)
#>                    [,1]                [,2]                 [,3]
#> [1,] 3.301444+4.444975i -1.965104+1.331585i -8.531962+ 3.411522i
#> [2,] 6.952041-3.592892i  3.061049+1.989404i  2.684448+13.213309i
#> [3,] 4.284159-7.747642i  2.698008+1.872862i 10.743977+10.076370i
(jj2 <- quad.form(solve(M),x))
#>                    [,1]                [,2]                 [,3]
#> [1,] 3.301444+4.444975i -1.965104+1.331585i -8.531962+ 3.411522i
#> [2,] 6.952041-3.592892i  3.061049+1.989404i  2.684448+13.213309i
#> [3,] 4.284159-7.747642i  2.698008+1.872862i 10.743977+10.076370i
max(abs(jj1-jj2))
#> [1] 0

Test of quad.3form() = \(x^*My\):

quad.3form(M,l,r)=crossprod(crossprod(M,Conj(l)),r)

(jj1 <- ht(x) %*% M %*% x1)
#>                     [,1]               [,2]                [,3]
#> [1,] -2.757785-3.661513i 1.717177+1.465892i  3.355718-0.945156i
#> [2,] -9.449372+2.931895i 4.294998-2.030691i -2.034164-9.085500i
#> [3,] -2.763455+7.175352i 0.624385-3.836955i -4.109222-3.118393i
(jj2 <- quad.3form(M,x,x1))
#>                     [,1]               [,2]                [,3]
#> [1,] -2.757785-3.661513i 1.717177+1.465892i  3.355718-0.945156i
#> [2,] -9.449372+2.931895i 4.294998-2.030691i -2.034164-9.085500i
#> [3,] -2.763455+7.175352i 0.624385-3.836955i -4.109222-3.118393i
tester(jj1,jj2)

Test of quad.3tform() = \(xMy^*\):

quad.3tform(M,l,r)=tcrossprod(left,tcrossprod(Conj(right),M))

(jj1 <- y %*% M %*% ht(y1))
#>                     [,1]                [,2]                [,3]
#> [1,]  2.259877+5.817718i  0.322623-4.214798i 10.124807-0.885336i
#> [2,]  4.371737+0.802188i -1.599502-0.621855i -2.984204-5.842088i
#> [3,] -3.934572+3.925103i  2.347600-0.779937i  5.252914+2.317635i
(jj2 <- quad.3tform(M,y,y1))
#>                     [,1]                [,2]                [,3]
#> [1,]  2.259877+5.817718i  0.322623-4.214798i 10.124807-0.885336i
#> [2,]  4.371737+0.802188i -1.599502-0.621855i -2.984204-5.842088i
#> [3,] -3.934572+3.925103i  2.347600-0.779937i  5.252914+2.317635i
tester(jj1,jj2)

Test of quad.tform() = \(xMx^*\):

quad.tform(M,x)=tcrossprod(x,tcrossprod(Conj(x),M))

(jj1 <- y %*% M %*% ht(y))
#>                     [,1]                  [,2]                [,3]
#> [1,]  9.737195-4.998527i -0.2167237-2.6083911i -4.818669-2.121937i
#> [2,] -2.394459-7.490901i  0.2953538+0.0874635i -2.368207+1.201750i
#> [3,]  8.017339+3.448196i  0.7731700+0.3924916i  0.652498-3.840184i
(jj2 <- quad.tform(M,y))
#>                     [,1]                  [,2]                [,3]
#> [1,]  9.737195-4.998527i -0.2167237-2.6083911i -4.818669-2.121937i
#> [2,] -2.394459-7.490901i  0.2953538+0.0874635i -2.368207+1.201750i
#> [3,]  8.017339+3.448196i  0.7731700+0.3924916i  0.652498-3.840184i
tester(jj1,jj2)

Test of quad.tform.inv() = \(xM^{-1}x^*\):

quad.tform.inv(M,x)=quad.form.inv(M,ht(x))

(jj1 <- y %*% solve(M) %*% ht(y))
#>                     [,1]                [,2]               [,3]
#> [1,]   0.70258+10.80578i  0.531841+4.672058i 7.649395-2.749588i
#> [2,]   1.55894+10.05695i  2.189819+1.477317i 4.120047-3.688441i
#> [3,] -12.99446- 1.68843i -3.470249+1.353259i 2.556249+7.365396i
(jj2 <- quad.tform.inv(M,y))
#>                     [,1]                [,2]               [,3]
#> [1,]   0.70258+10.80578i  0.531841+4.672058i 7.649395-2.749588i
#> [2,]   1.55894+10.05695i  2.189819+1.477317i 4.120047-3.688441i
#> [3,] -12.99446- 1.68843i -3.470249+1.353259i 2.556249+7.365396i
tester(jj1,jj2)

Test of quad.diag() = \(\operatorname{diag}(x^*Mx)\) = diag(quad.form()):

quad.diag(M,x)=colSums(crossprod(M,Conj(x)) * x)

(jj1 <- diag(ht(x) %*% M %*% x))
#> [1] -0.361280-1.642215i  3.086616+0.822312i -1.385598-2.269765i
(jj2 <- diag(quad.form(M,x)))
#> [1] -0.361280-1.642215i  3.086616+0.822312i -1.385598-2.269765i
(jj3 <- quad.diag(M,x))
#> [1] -0.361280-1.642215i  3.086616+0.822312i -1.385598-2.269765i
tester(jj1,jj3)
tester(jj2,jj3)

Test of quad.tdiag() = \(\operatorname{diag}(xMx^*)\) = diag(quad.tform()):

quad.tdiag(M,x)=rowSums(tcrossprod(Conj(x), M) * x)

(jj1 <- diag(y %*% M %*% ht(y)))
#> [1] 9.7371946-4.9985268i 0.2953538+0.0874635i 0.6524977-3.8401838i
(jj2 <- diag(quad.tform(M,y)))
#> [1] 9.7371946-4.9985268i 0.2953538+0.0874635i 0.6524977-3.8401838i
(jj3 <- quad.tdiag(M,y))
#> [1] 9.7371946-4.9985268i 0.2953538+0.0874635i 0.6524977-3.8401838i
tester(jj1,jj3)
tester(jj2,jj3)

Test of quad.3diag() = \(\operatorname{diag}(x^*My)\)

quad.3diag(M,l,r)=colSums(crossprod(M, Conj(left)) * right)

(jj1 <- diag(ht(x) %*% M %*% x1))
#> [1] -2.757785-3.661513i  4.294998-2.030691i -4.109222-3.118393i
(jj2 <- diag(quad.3form(M,x,x1)))
#> [1] -2.757785-3.661513i  4.294998-2.030691i -4.109222-3.118393i
(jj3 <- quad.3diag(M,x,x1))
#> [1] -2.757785-3.661513i  4.294998-2.030691i -4.109222-3.118393i
tester(jj1,jj3)
tester(jj2,jj3)

Test of quad.3tdiag() = \(\operatorname{diag}(xMy^*)\)

quad.3tdiag(M,l,r)=colSums(t(left) * tcprod(M, right))

(jj1 <- diag(y %*% M %*% ht(y1)))
#> [1]  2.259877+5.817718i -1.599502-0.621855i  5.252914+2.317635i
(jj2 <- diag(quad.3tform(M,y,y1)))
#> [1]  2.259877+5.817718i -1.599502-0.621855i  5.252914+2.317635i
(jj3 <- quad.3tdiag(M,y,y1))
#> [1]  2.259877+5.817718i -1.599502-0.621855i  5.252914+2.317635i
tester(jj1,jj3)
tester(jj2,jj3)