In [1]:
## Randomized matrix algorithms.
# Our overall point: A matrix of random entries is not random.
# More technically correct is:
# A matrix of random entries has "non-random" properties.
In [2]:
## Point 0. A sum of indep. random values isn't all that random.
x = rand(1000)
sum(x)
Out[2]:
486.8153570717421
In [3]:
## Point 1. A matrix of random indep. entries isn't all that random.
using LinearAlgebra
A = rand(50,50)
A = A + A'
maximum(eigvals(A))
Out[3]:
50.2529963807071
In [4]:
## Q: What is the eigenvector?
A = randn(1000,1000)
A = A + A'
lams,V = eigen!(A)
i = argmax(lams)
vi = V[:,i]
Out[4]:
1000-element Array{Float64,1}:
  0.033370858210042834 
 -0.002664730227436435 
 -0.07527724689313725  
 -0.014601451444760754 
  0.0556414424168894   
  0.008543151017988733 
 -0.021260965514193972 
  0.011854208758790495 
  0.002703973020230745 
 -0.005262125879736487 
  0.007735205851539917 
  0.05826135504973946  
  0.05302273272370446  
  â‹®                    
  0.0012984902934284892
 -0.08224593405033034  
  0.019797847427827392 
  0.03815355176084209  
 -0.024270795623292124 
  0.0065666812680647835
  0.00912498074580162  
 -0.010749400763958514 
  0.02213420765463886  
 -0.03299003205472431  
 -0.07753776623222333  
 -0.03280054248629301  
In [5]:
## A: The eigenvector appears to be converging to the normalized
# version of all ones!
In [6]:
##m
# ## This is an instance of a property called concentration.
# Concentration is just the property that a collection of random
# variable samples should be behave like the mean.

using Plots
pyplot()
n = 10_000
X = randn(n,5)
plot(cumsum(X ./ (1:n), dims=2), xscale=:log10)
gui()
In [7]:
## The same holds true for a collection of matrices.
using StatsBase
function eigtrial(n,nt)
  lams = Vector{Float64}()
  for t=1:nt
    A = rand(50,50)
    A = A + A'
    push!(lams, eigvals!(A)...)
  end
  h = fit(Histogram, lams; nbins = 50)
  h = normalize(h)
  return h
end
h = eigtrial(50,100)
plot(h.edges, h.weights)
Out[7]:
In [8]:
##
h = eigtrial(50,100)
plot!(h.edges, h.weights)
Out[8]:
In [9]:
##
h = eigtrial(50,100)
plot!(h.edges, h.weights)
Out[9]:
In [10]:
##
h = eigtrial(50,100)
plot!(h.edges, h.weights)
Out[10]:
In [11]:
##
# See, this is not random at all! It's basically
# the same every time!
In [12]:
## The idea of randomized-matrix algorithms is to
# use this non-randomness of random-matrices to
# make things faster!
In [13]:
## Let's look at the eigenfaces matrix.
using DelimitedFiles
A = readdlm(download("http://www.cs.purdue.edu/homes/dgleich/cs515-2019/homeworks/Yale_64.csv"),',')
Out[13]:
165×4096 Array{Float64,2}:
 47.0  48.0  49.0  51.0  52.0  48.0  …  132.0  182.0  224.0  242.0  250.0
 63.0  68.0  61.0  68.0  70.0  65.0     240.0  247.0  255.0  254.0  244.0
 44.0  37.0  40.0  39.0  43.0  48.0     247.0  245.0  255.0  251.0  229.0
 61.0  58.0  59.0  53.0  44.0  40.0     255.0  253.0  255.0  254.0  255.0
 70.0  55.0  60.0  59.0  61.0  48.0     255.0  255.0  254.0  252.0  255.0
 53.0  55.0  66.0  66.0  61.0  44.0  …   30.0   32.0   32.0   31.0   28.0
 74.0  60.0  58.0  46.0  60.0  55.0     255.0  255.0  255.0  255.0  255.0
 79.0  71.0  72.0  72.0  72.0  64.0     241.0  249.0  248.0  255.0  253.0
  8.0  10.0  11.0  11.0   9.0   6.0     210.0  213.0  234.0  252.0  241.0
 72.0  66.0  66.0  62.0  57.0  57.0     248.0  255.0  255.0  252.0  255.0
 54.0  60.0  58.0  53.0  44.0  37.0  …  255.0  255.0  255.0  255.0  254.0
 55.0  56.0  62.0  68.0  75.0  87.0     255.0  255.0  255.0  255.0  255.0
 17.0  17.0  18.0  21.0  19.0  18.0     213.0  204.0  219.0  237.0  201.0
  ⋮                             ⋮    ⋱                                ⋮  
  5.0   4.0   5.0   6.0   5.0   5.0     255.0  254.0  252.0  255.0  255.0
  8.0   6.0   5.0   3.0   3.0   4.0     255.0  255.0  244.0  240.0  243.0
  9.0   8.0   6.0   8.0   9.0   8.0  …  243.0  239.0  235.0  194.0  210.0
 32.0  30.0  35.0  49.0  62.0  72.0     220.0  191.0  137.0  102.0   87.0
 10.0   8.0  10.0   9.0   9.0   8.0     176.0  233.0  240.0  232.0  182.0
 12.0  15.0  15.0  16.0  17.0  16.0     206.0  219.0  240.0  246.0  249.0
  6.0   6.0   4.0   3.0   3.0   5.0      21.0   19.0   18.0   16.0   14.0
 11.0   9.0   6.0   7.0  11.0  12.0  …  211.0  213.0  185.0  230.0  244.0
 27.0  24.0  21.0  27.0  38.0  47.0     243.0  229.0  236.0  252.0  254.0
  0.0   0.0   0.0   0.0   0.0   0.0     247.0  248.0  244.0  237.0  254.0
 12.0  10.0   8.0  10.0  13.0  13.0     252.0  227.0  198.0  231.0  199.0
  9.0   8.0   8.0   8.0   9.0  11.0     202.0  174.0  141.0  109.0   82.0
In [14]:
##
M = A'
U,Sig,V = svd(M)
Out[14]:
SVD{Float64,Float64,Adjoint{Float64,Array{Float64,2}}}([-0.004794554643220445 -0.005611934933678217 … -0.0021564112916703433 0.016873189357716426; -0.0047714217799709165 -0.005098272416702468 … 0.013807026913200304 0.022165921289367778; … ; -0.027558912853241108 0.041174136814629135 … 0.006737588423656211 0.01583677258428024; -0.02651109474218349 0.0397050756857079 … -0.013624149424393182 0.0006654617330305198], [85883.71492374448, 16330.811851875102, 14222.124921450804, 9277.563343385998, 8911.419229196994, 8033.801635842138, 6966.946285472259, 6655.154091782826, 5548.738872424876, 5396.281289355448  …  474.90709838028965, 470.2739412119555, 457.77365969358544, 453.8433459438483, 435.42920127202257, 383.97971162090954, 286.8117862591766, 241.6685276867817, 3.814003109947372e-11, 5.184364311227659e-13], [-0.08930330580973167 -0.1072443227535132 … -0.08696876376383537 -0.08491761807046372; -0.11548054409948011 -0.05938878381524759 … 0.04355713270590955 0.005567332115744821; … ; -3.78796839889652e-17 1.601868679345486e-17 … -1.297853251475637e-16 -3.8462667749829254e-17; 3.742654826172251e-17 3.3520086918652727e-16 … -9.962232934885327e-16 -7.556165250377887e-16])
In [15]:
## A given matrix-times-random matrix is often called
# a sketch because it preserves certain properties due
# to what happens with random matrices being well-behaved
# and deterministic. It's a "sketch" betcause it's smaller.
# The algorithm here is from Halko Martinsson and Tropp.
# TODO, write the reference!
function simple_sketch(M::AbstractMatrix, k::Int, p::Int=10)
  m,n = size(M)
  S = M*randn(n,k+p)

  return Matrix(qr!(S).Q)
end
Q = simple_sketch(M, 5)
Out[15]:
4096×15 Array{Float64,2}:
 -0.00510725  -0.00180594  -0.0126789    …  -0.0125842  -0.0317027 
 -0.00472259  -0.00163584  -0.0149772       -0.0123805  -0.0286049 
 -0.00452942  -0.00181586  -0.0133018       -0.0124958  -0.0272108 
 -0.0044227   -0.00358697  -0.0126797       -0.0130942  -0.0245196 
 -0.00459788  -0.00588688  -0.0116022       -0.0118695  -0.0247071 
 -0.00414274  -0.00722801  -0.0102414    …  -0.0132153  -0.025395  
 -0.00405022  -0.00791939  -0.00440644      -0.0150245  -0.0231967 
 -0.00355675  -0.00920683  -0.00224739      -0.0181417  -0.0187993 
 -0.00297763  -0.00732108  -0.000354955     -0.0237476  -0.0218275 
 -0.00214718  -0.00563696  -0.00758552      -0.0291277  -0.0219342 
 -0.00339718  -0.00552869  -0.0159105    …  -0.0332788  -0.0262589 
 -0.00692126  -0.0145771   -0.0150012       -0.0245993  -0.0381907 
 -0.00872328  -0.0114083    0.00184385      -0.0215704  -0.0547475 
  ⋮                                      ⋱                         
 -0.0307153   -0.0300633    0.00563546      -0.0250656   0.0134116 
 -0.0271979   -0.0239899    0.0290986    …  -0.0299978   0.00957624
 -0.0262926   -0.0152148    0.0398039       -0.0318241   0.00967158
 -0.023981    -0.00854043   0.0442708       -0.0500246   0.0153986 
 -0.0255767   -0.00786682   0.0629183       -0.0581139  -0.0193331 
 -0.0271043   -0.00329403   0.06357         -0.0615371  -0.033011  
 -0.0324076   -0.00812428   0.0274905    …  -0.0491848  -0.0247267 
 -0.036215    -0.0122473    0.0248247       -0.029981   -0.027882  
 -0.0359375   -0.020368     0.0138193       -0.0125464  -0.0283779 
 -0.037717    -0.0271228   -0.0116255        0.0115657  -0.049975  
 -0.0414989   -0.0396902   -0.0271844        0.0437904  -0.0552986 
 -0.0368925   -0.032226    -0.0137381    …   0.0661965  -0.0408587 
In [16]:
##
B = Q'*M
UB,SigB,VB = svd(B)
Uhat = Q*UB
Out[16]:
4096×15 Array{Float64,2}:
 0.00619497  -0.000580546   0.00468472  …  -0.0132774    -0.00553071
 0.00587551  -0.00127459    0.00716444     -0.0107326    -0.0044508 
 0.00600515  -0.00113082    0.00647561     -0.0099448    -0.0044322 
 0.00621939   0.000532514   0.00678671     -0.00696755   -0.00190611
 0.00648387   0.00175194    0.00543398     -0.00615403    0.00110415
 0.00658072   0.00322649    0.00572511  …  -0.00522789    0.00361279
 0.00709133   0.00490869    0.00341345     -0.00538607    0.00891885
 0.00742672   0.00619469    0.00279732     -0.00221671    0.0149834 
 0.00799575   0.00676655    0.00204652      0.000110705   0.0125845 
 0.0084264    0.00641997    0.00176505      0.00942735    0.0122384 
 0.00887105   0.00705932    0.00412919  …   0.0106932     0.00198029
 0.00923619   0.00992497    0.0085926      -0.0113445    -0.0036925 
 0.0103203    0.0111427     0.0103216      -0.0340774    -0.00914189
 ⋮                                      ⋱                           
 0.0313125    0.0356885     0.0367294       0.0402634     3.00385e-5
 0.0296887    0.0386276     0.0301403   …   0.0220125     0.00749363
 0.0284861    0.0349908     0.0303614       0.0116812     0.0128618 
 0.0289853    0.036922      0.0351592       0.0238225     0.0148775 
 0.0296483    0.032662      0.031732        0.000327195  -0.00633513
 0.03078      0.0288991     0.0330526      -0.00885673   -0.0290833 
 0.0319012    0.0245482     0.040901    …   0.00686319   -0.0547997 
 0.0335869    0.0236495     0.0371275      -0.00979775   -0.0556751 
 0.0326985    0.0186101     0.0229532      -0.0197714    -0.0436291 
 0.0320724    0.0178606     0.0107072      -0.0299665    -0.0770996 
 0.0309213    0.0194907    -0.00516882     -0.0548186    -0.0819233 
 0.0276978    0.0177294    -0.0064569   …  -0.0647091    -0.080812  
In [17]:
## Compare the two
[Sig[1:5] SigB[1:5]]
Out[17]:
5×2 Array{Float64,2}:
 85883.7   85650.9
 16330.8   14228.4
 14222.1   12707.8
  9277.56   8090.0
  8911.42   7264.4
In [18]:
## Look at the Vector
norm(Uhat[:,1] - U[:,1])
Out[18]:
1.9986408400559785
In [19]:
##
plot(
  heatmap(reshape(abs.(Uhat[:,1]), 64, 64), title="Random",color=:blues),
  heatmap(reshape(abs.(U[:,1]), 64, 64), title="Exact",color=:blues),
  yflip=true, )
gui()
In [20]:
##
# So the field is often dominated by analysis showing
# that these results are not a fluke! In fact, we can
# get quite precise bounds on how much work is needed
# in a variety of situations to get good accuracy.
In [21]:
##
plot(
  heatmap(reshape(abs.(Uhat[:,2]), 64, 64), title="Random",color=:blues),
  heatmap(reshape(abs.(U[:,2]), 64, 64), title="Exact",color=:blues),
  yflip=true, )
gui()