{ "cells": [ { "cell_type": "code", "execution_count": 1, "id": "40e2ecf6-a1ee-4d82-924a-e2f763915652", "metadata": {}, "outputs": [], "source": [ "using LinearAlgebra, Plots" ] }, { "cell_type": "code", "execution_count": 2, "id": "89746093-dc10-4bb2-9646-c84d5db0d8f8", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "householder_vector (generic function with 2 methods)" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "function householder_vector(x::Vector{<:AbstractFloat})::Tuple{Vector, AbstractFloat}\n", " # returns the normalized vector u such that H*x is a multiple of e_1\n", "\n", " s = norm(x)\n", " if x[1] ≥ 0\n", " s = -s\n", " end\n", " u = copy(x)\n", " u[1] -= s\n", " u ./= norm(u)\n", " return u, s\n", "end\n", "\n", "function householder_vector(x::Matrix{<:AbstractFloat})::Tuple{Matrix, AbstractFloat}\n", " # returns the normalized vector u such that H*x is a multiple of e_1\n", "\n", " s = norm(x)\n", " if x[1] ≥ 0\n", " s = -s\n", " end\n", " u = copy(x)\n", " u[1] -= s\n", " u ./= norm(u)\n", " return u, s\n", "end" ] }, { "cell_type": "code", "execution_count": 3, "id": "262a769a-aa42-4929-bbf4-7f5a97783810", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "([0.7960091839647405, 0.3357514552548967, 0.503627182882345], -3.7416573867739413)" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "3-element Vector{Float64}:\n", " 1.0\n", " 2.0\n", " 3.0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "x = [1., 2, 3]\n", "householder_vector(x) |> display\n", "x |> display\n", "\n", "# better with copy and division in place\n", "# @benchmark householder_vector(randn(100_000))" ] }, { "cell_type": "code", "execution_count": 4, "id": "457b3bcf-a077-42cb-9a6c-f6d1d6e00504", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5-element Vector{Float64}:\n", " 3.983324158315414\n", " -5.457417586244235e-16\n", " -4.598436127585974e-17\n", " -1.4573782140540267e-16\n", " 3.2867357162787514e-16" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "A = randn(5, 4)\n", "\n", "# first step of QR factorization\n", "R1 = A\n", "(u1, s1) = householder_vector(R1[1:end,1])\n", "\n", "H1 = I - 2 * u1 * u1'\n", "\n", "Q1 = H1\n", "\n", "Q1 * R1[1:end, 1] |> display # what we expect -> a multiple of e_1" ] }, { "cell_type": "code", "execution_count": 5, "id": "fd98a89e-01b9-4403-8bca-b9e1443b2eea", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×4 Matrix{Float64}:\n", " 3.98332 -1.23134 1.8182 0.590776\n", " 3.80466e-16 1.52712 -1.85383 -0.709258\n", " -3.09958e-16 -1.11852e-17 -0.563749 -1.85235\n", " 4.05338e-16 -4.61156e-17 -0.174643 1.27511\n", " 1.55202e-16 1.4856e-17 1.73802 0.266999" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# second step\n", "R2 = Q1 * R1\n", "\n", "(u2, s2) = householder_vector(R2[2:end, 2])\n", "H2 = I - 2 * u2 * u2'\n", "\n", "# there is no blkdiag method in julia\n", "# (maybe look into https://github.com/JuliaArrays/BlockDiagonals.jl)\n", "# there are 2 methods (blocks is an array of blocks):\n", "### METHOD 1:\n", "# cat(blocks..., dims=(1,2))\n", "### METHOD 2:\n", "# using SparseArrays\n", "# blockdiag(SparseMatrixCSC.(blocks)...)\n", "## method 2 is slightly faster with subsequent matrix multiplication\n", "# performance is ignored in this step\n", "Q2 = cat(1, H2, dims=(1, 2))\n", "\n", "Q2 * R2" ] }, { "cell_type": "code", "execution_count": 6, "id": "a563f47e-7eda-46c7-9804-080335bcb8a3", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×4 Matrix{Float64}:\n", " 3.98332 -1.23134 1.8182 0.590776\n", " 3.80466e-16 1.52712 -1.85383 -0.709258\n", " 2.03593e-16 2.18903e-17 1.83549 0.700423\n", " 4.4272e-16 -4.37079e-17 6.42625e-17 1.46093\n", " -2.16817e-16 -9.104e-18 -4.23002e-16 -1.58224" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# third step\n", "\n", "R3 = Q2 * R2\n", "\n", "(u3, s3) = householder_vector(R3[3:end, 3])\n", "H3 = I - 2 * u3 * u3'\n", "\n", "Q3 = cat(Diagonal(ones(2)), H3, dims=(1,2))\n", "Q3 * R3" ] }, { "cell_type": "code", "execution_count": 7, "id": "85f5eb54-f3fe-40c8-86d0-90e80895b753", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×4 Matrix{Float64}:\n", " 3.98332 -1.23134 1.8182 0.590776\n", " 3.80466e-16 1.52712 -1.85383 -0.709258\n", " 2.03593e-16 2.18903e-17 1.83549 0.700423\n", " -4.5963e-16 2.29618e-17 -3.54379e-16 -2.15356\n", " 1.78186e-16 -3.82887e-17 -2.39742e-16 1.50034e-16" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# fourth step\n", "\n", "R4 = Q3 * R3\n", "\n", "(u4, s4) = householder_vector(R4[4:end, 4])\n", "H4 = I - 2 * u4 * u4'\n", "\n", "Q4 = cat(Diagonal(ones(3)), H4, dims=(1,2))\n", "Q4 * R4\n", "\n", "# done because we arrived at the second dimension of A" ] }, { "cell_type": "code", "execution_count": 8, "id": "eff90e91-7856-4fd7-b2a5-79c0f681147a", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "qrfactorization (generic function with 1 method)" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "function qrfactorization(A::Matrix{<:AbstractFloat})::Tuple{Matrix{<:AbstractFloat}, Matrix{<:AbstractFloat}}\n", " (m, n) = size(A)\n", " R = copy(A)\n", " Q = Diagonal(ones(eltype(A), m))\n", "\n", " for k ∈ 1:n\n", " (u, s) = householder_vector(R[k:end, k])\n", " # construct R\n", " R[k, k] = s\n", " R[k+1:end, k] .= 0\n", " R[k:end, k+1:end] -= 2 * u * (u' * R[k:end, k+1:end])\n", " # contruct the new H\n", " H = I - 2 * u * u'\n", " # contruct the Q\n", " Q = Q * cat(Diagonal(ones(eltype(A), k-1)), H, dims=(1,2)) # very inefficient (maybe simply send back the list of u_i)\n", " end\n", " return (Q, R)\n", "end" ] }, { "cell_type": "code", "execution_count": 9, "id": "4dbe13ff-44f5-4bd2-8452-a9e1477c80ff", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "true" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "A = randn(Float32, 1000, 20)\n", "(Q, R) = qrfactorization(A)\n", "(norm(A - Q*R) ≤ size(A)[1] * 2^-23 * norm(A)) |> display\n", "(norm(I - Q*Q') ≤ size(A)[1] * 2^-23) |> display" ] } ], "metadata": { "kernelspec": { "display_name": "Julia 1.9.3", "language": "julia", "name": "julia-1.9" }, "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", "version": "1.9.3" } }, "nbformat": 4, "nbformat_minor": 5 }