{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Gram–Schmidt orthogonalization\n", "\n", "Chapter 4.4 illustrates a hand technique for computing orthonormal vectors q₁,q₂,… from arbitrary vectors a,b,… with the property that the first k vectors in the original set span the same subspace as the orthonormal set, and this is true for k=1,2,3,...\n", "\n", "We will move this hand technique to the computer in this notebook. Some of you will notice that on the computer one can combine operations in a simpler block fashion. " ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "using LinearAlgebra" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6×4 Matrix{Int64}:\n", " 5 10 6 8\n", " 10 6 5 1\n", " 2 1 7 3\n", " 7 5 4 8\n", " 1 3 8 3\n", " 1 8 9 4" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# start with four arbitrary independent vectors in ℝᵐ\n", "# with random entries from 1 to 10.\n", "m = 6\n", "a₁ = rand(1:10,m)\n", "a₂ = rand(1:10,m)\n", "a₃ = rand(1:10,m)\n", "a₄ = rand(1:10,m)\n", "A = [a₁ a₂ a₃ a₄] # show them as the columns of a 6×4 matrix A" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6×4 Matrix{Float64}:\n", " 5.0 5.61111 -3.16215 1.37162\n", " 10.0 -2.77778 -0.0979465 -3.70534\n", " 2.0 -0.755556 6.16936 1.22464\n", " 7.0 -1.14444 -0.324354 4.20193\n", " 1.0 2.12222 5.22283 0.0756904\n", " 1.0 7.12222 1.49913 -1.74319" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# The vₖ are vectors, but they are all orthogonal and\n", "#span([v₁]) = span([a₁])\n", "#span([v₁ v₂]) = span([a₁ a₂])\n", "#span([v₁ v₂ v₃]) = span([a₁ a₂ a₃] )\n", "#span([v₁ v₂ v₃ v₄]) = span([a₁ a₂ a₃ a₄])\n", "v₁ = a₁\n", "v₂ = a₂ - v₁*(v₁'a₂)/(v₁'v₁)\n", "v₃ = a₃ - v₁*(v₁'a₃)/(v₁'v₁) - v₂*(v₂'a₃)/(v₂'v₂)\n", "v₄ = a₄ - v₁*(v₁'a₄)/(v₁'v₁) - v₂*(v₂'a₄)/(v₂'v₂) - v₃*(v₃'a₄)/(v₃'v₃)\n", "\n", "# gather into a matrix V with orthogonal but *not* orthonormal columns\n", "V = [v₁ v₂ v₃ v₄]" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6×4 Matrix{Float64}:\n", " 0.372678 0.571756 -0.358733 0.223061\n", " 0.745356 -0.283047 -0.0111116 -0.602583\n", " 0.149071 -0.0769889 0.699888 0.199157\n", " 0.521749 -0.116616 -0.0367966 0.683342\n", " 0.0745356 0.216248 0.592508 0.0123092\n", " 0.0745356 0.725734 0.170071 -0.283488" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# now we normalize\n", "q₁ = normalize(v₁)\n", "q₂ = normalize(v₂)\n", "q₃ = normalize(v₃)\n", "q₄ = normalize(v₄);\n", "\n", "# Gather into a matrix Q with orthonormal columns\n", "Q = [q₁ q₂ q₃ q₄]" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4×4 Matrix{Float64}:\n", " 1.0 -9.21828e-17 8.99478e-17 1.35877e-16\n", " -9.21828e-17 1.0 1.0142e-17 1.42552e-16\n", " 8.99478e-17 1.0142e-17 1.0 -2.09229e-16\n", " 1.35877e-16 1.42552e-16 -2.09229e-16 1.0" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#check that Q has orthonormal columns\n", "Q'Q" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4×4 Matrix{Float64}:\n", " 0.0 -9.21828e-17 8.99478e-17 1.35877e-16\n", " -9.21828e-17 0.0 1.0142e-17 1.42552e-16\n", " 8.99478e-17 1.0142e-17 0.0 -2.09229e-16\n", " 1.35877e-16 1.42552e-16 -2.09229e-16 2.22045e-16" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Q'Q - I" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Q'Q ≈ I" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4×4 Matrix{Float64}:\n", " 180.0 -1.42109e-14 1.33227e-14 1.28786e-14\n", " -1.42109e-14 96.3111 9.32625e-15 6.94211e-15\n", " 1.33227e-14 9.32625e-15 77.7003 -1.16249e-14\n", " 1.28786e-14 6.94211e-15 -1.16249e-14 37.8113" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# compare to what happens if we didn't normalize:\n", "V'V # = diagonal matrix (orthogonal columns, but not orthonormal)" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4×4 Matrix{Float64}:\n", " 13.4164 11.7766 10.3605 8.86974\n", " -0.0 9.81382 9.2715 6.67879\n", " 0.0 0.0 8.81478 1.38213\n", " 0.0 0.0 0.0 6.14909" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# What does this triangular structure say?\n", "round.(Q'A, digits=5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## QR factorization\n", "\n", "How do we do all this at once on a computer? We ask the computer to factor the matrix as $QR$ (orthonormal columns times upper triangular)." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "LinearAlgebra.QRCompactWY{Float64, Matrix{Float64}}\n", "Q factor:\n", "6×6 LinearAlgebra.QRCompactWYQ{Float64, Matrix{Float64}}:\n", " -0.372678 0.571756 0.358733 -0.223061 -0.0840586 -0.590504\n", " -0.745356 -0.283047 0.0111116 0.602583 0.0189054 -0.0272178\n", " -0.149071 -0.0769889 -0.699888 -0.199157 -0.619405 -0.242243\n", " -0.521749 -0.116616 0.0367966 -0.683342 0.131189 0.478182\n", " -0.0745356 0.216248 -0.592508 -0.0123092 0.74464 -0.204877\n", " -0.0745356 0.725734 -0.170071 0.283488 -0.192913 0.566789\n", "R factor:\n", "4×4 Matrix{Float64}:\n", " -13.4164 -11.7766 -10.3604 -8.86974\n", " 0.0 9.81382 9.2715 6.67879\n", " 0.0 0.0 -8.81478 -1.38213\n", " 0.0 0.0 0.0 -6.14909" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "F = qr(A) # returns a \"factorization object\" that stores both Q (implicitly) and R" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4×4 Matrix{Float64}:\n", " -13.4164 -11.7766 -10.3604 -8.86974\n", " 0.0 9.81382 9.2715 6.67879\n", " 0.0 0.0 -8.81478 -1.38213\n", " 0.0 0.0 0.0 -6.14909" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "R = F.R" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6×4 Matrix{Float64}:\n", " -0.372678 0.571756 0.358733 -0.223061\n", " -0.745356 -0.283047 0.0111116 0.602583\n", " -0.149071 -0.0769889 -0.699888 -0.199157\n", " -0.521749 -0.116616 0.0367966 -0.683342\n", " -0.0745356 0.216248 -0.592508 -0.0123092\n", " -0.0745356 0.725734 -0.170071 0.283488" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Q2 = Matrix(F.Q) # extract the \"thin\" QR factor you would get from Gram–Schmidt" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4×4 Matrix{Float64}:\n", " -1.0 -0.0 -0.0 0.0\n", " 0.0 1.0 0.0 -0.0\n", " -0.0 0.0 -1.0 0.0\n", " -0.0 -0.0 -0.0 -1.0" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "round.(Q'Q2, digits=5) # almost I, up to signs" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4×4 Matrix{Float64}:\n", " -13.4164 -11.7766 -10.3604 -8.86974\n", " 0.0 9.81382 9.2715 6.67879\n", " 0.0 0.0 -8.81478 -1.38213\n", " 0.0 0.0 0.0 -6.14909" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "R # Recognize this matrix?" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Q2*R ≈ A" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6-element Vector{Float64}:\n", " 0.5450097629781777\n", " 0.8599801580391012\n", " 0.7036387925825908\n", " 0.7553540639899048\n", " 0.7234080262946185\n", " 0.14725528162868073" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "b = rand(6)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4-element Vector{Float64}:\n", " 0.08995466028300682\n", " -0.0768023071730735\n", " 0.07513430689992019\n", " 0.03688677949256471" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A \\ b" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4-element Vector{Float64}:\n", " 0.08995466028300675\n", " -0.07680230717307351\n", " 0.07513430689992023\n", " 0.03688677949256475" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "inv(A'A) * A'b" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4-element Vector{Float64}:\n", " 0.0899546602830068\n", " -0.07680230717307351\n", " 0.07513430689992022\n", " 0.03688677949256465" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "R \\ (Q2'b)[1:4]" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4-element Vector{Float64}:\n", " 0.08995466028300678\n", " -0.07680230717307349\n", " 0.07513430689992023\n", " 0.03688677949256459" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "F \\ b # the factorization object F can be used directly for a least-square solve" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "@webio": { "lastCommId": null, "lastKernelId": null }, "kernelspec": { "display_name": "Julia 1.7.1", "language": "julia", "name": "julia-1.7" }, "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", "version": "1.7.1" } }, "nbformat": 4, "nbformat_minor": 2 }