{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from traitlets.config.manager import BaseJSONConfigManager\n", "import jupyter_core\n", "path = \"/Users/i.oseledets/anaconda2/envs/teaching/etc/jupyter/nbconfig\"\n", "cm = BaseJSONConfigManager(config_dir=path)\n", "cm.update(\"livereveal\", {\n", " \"theme\": \"sky\",\n", " \"transition\": \"zoom\",\n", " \"start_slideshow_at\": \"selected\",\n", " \"scroll\": True\n", "})" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Lecture 9: From dense to sparse linear algebra" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Recap of the previous lectures\n", "- Algorithms for the symmetric eigenvalue problems \n", " - QR algorithm\n", " - Divide-and-Conquer\n", " - bisection\n", "- SVD and its applications\n", " - collaborative filtering\n", " - integral equations\n", " - latent semantic indexing" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Interesting fact 1\n", "\n", "Before $$A_k = Q_k R_k, \\quad A_{k+1} = R_k Q_k,$$\n", "\n", "there was $LU-UL$ algorithm by [Heinz Rutishauser](https://en.wikipedia.org/wiki/Heinz_Rutishauser)\n", "\n", "$$A_k = L_k U_k, \\quad A_{k+1} = U_k L_k.$$\n", "\n", "This generates a sequence of similar matrices, but is less stable than QR algorithm.\n", "\n", "Int. fact: Heinz Rutishauser has proposed the **for** keyword in programming language." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Interesting fact 2\n", "\n", "Recent paper [Strassen algorithm reloaded](http://delivery.acm.org/10.1145/3020000/3014983/a59-huang.pdf?ip=89.106.174.124&id=3014983&acc=OPEN&key=4D4702B0C3E38B35%2E4D4702B0C3E38B35%2E4D4702B0C3E38B35%2E6D218144511F3437&CFID=850797828&CFTOKEN=25065737&__acm__=1479192384_96a207c8760b13d6521f71b8bbc370f0)\n", "claim to **break conventional wisdom** that Strassen algorithm is not very practical." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "- Conventional wisdom: it is only\n", " practical for very large matrices. The proposed implementation is practical\n", " for small matrices. \n", "- Conventional wisdom: the matrices being\n", " multiplied should be relatively square. The proposed implementation is\n", " practical for rank-$k$ updates, where $k$ is relatively small (a shape\n", " of importance for libraries like LAPACK). \n", "- Conventional wisdom:\n", " it inherently requires substantial workspace. The proposed implementation\n", " requires no workspace beyond buffers already incorporated\n", " into conventional high-performance DGEMM implementations.\n", "- Conventional wisdom: a Strassen DGEMM interface must pass\n", " in workspace. The proposed implementation requires no such workspace\n", " and can be plug-compatible with the standard DGEMM interface.\n", "- Conventional wisdom: it is hard to demonstrate speedup\n", " on multi-core architectures. The proposed implementation demonstrates\n", " speedup over conventional DGEMM even on an IntelR Xeon\n", " PhiTM coprocessor utilizing 240 threads. It is shown how a distributed\n", " memory matrix-matrix multiplication also benefits from\n", " these advances." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Sparse matrices \n", "\n", "For dense linear algebra problems, we are limited by the memory to store the full matrix, it is $N^2$ parameters.\n", "\n", "The class of **sparse** matrices where most of the elements are zero, allows us **at least** to store such matrices.\n", "\n", "The question if we can:\n", "\n", "- Solve linear systems\n", "- Solve eigenvalue problems\n", "\n", "with sparse matrices" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Today lecture\n", "\n", "Today we will talk about **sparse matrices**, where they arise, how we store them, how we operate with them.\n", "\n", "\n", "- Formats: list of lists and compressed sparse row format, relation to graphs\n", "- Fast matrix-by-vector product\n", "- Fast direct solvers for Gaussian elimination (start)\n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Applications of sparse matrices\n", "\n", "Sparse matrices arise:\n", "\n", "- In Partial Differential Equations (PDE), mathematical modelling\n", "- In graphs mining (social networks)\n", "- In recommender systems\n", "- Wherever relations between objects are \"sparse\"." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Sparse matrices are ubiquitous in PDE\n", "Consider the simplest partial differential equation (PDE), called **Laplace equation**: \n", "\n", "$$\n", " \\Delta T = \\frac{\\partial^2 T}{\\partial x^2} + \\frac{\\partial^2 T}{\\partial y^2} = f(x,y), \\quad x,y\\in \\Omega\\equiv[0,1]^2,\n", "$$\n", "\n", "$$\n", " T_{\\partial\\Omega} = 0.\n", "$$" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Discretization\n", "\n", "$$\\frac{\\partial^2 T}{\\partial x^2} \\approx \\frac{T(x+h) + T(x-h) - 2T(x)}{h^2} + \\mathcal{O}(h^2),$$\n", "\n", "same for $\\frac{\\partial^2 T}{\\partial y^2},$\n", "and we get a linear system. \n", " First, let us consider **one-dimensional case**:" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "After the discretization of the one-dimensional Laplace equation with Dirichlet boundary conditions we have\n", "\n", "$$\\frac{u_{i+1} + u_{i-1} - 2u_i}{h^2} = f_i,\\quad i=1,\\dots,N-1$$\n", "\n", "$$ u_{0} = u_N = 0$$\n", "or in the matrix form\n", "\n", "$$ A u = f,$$\n", "and (for $n = 5$)\n", "$$A=-\\frac{1}{h^2}\\begin{bmatrix} 2 & -1 & 0 & 0 & 0\\\\ -1 & 2 & -1 & 0 &0 \\\\ 0 & -1 & 2& -1 & 0 \\\\ 0 & 0 & -1 & 2 &-1\\\\ 0 & 0 & 0 & -1 & 2 \\end{bmatrix}$$" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "The matrix is **triadiagonal** and **sparse** \n", "(and also **Toeplitz**: all elements on the diagonal are the same)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Block structure in 2D\n", "In two dimensions, we get equation of the form\n", "\n", "$$-\\frac{4u_{ij} -u_{(i-1)j} - u_{(i+1)j} - u_{i(j-1)}-u_{i(j+1)}}{h^2} = f_{ij},$$\n", "\n", "or in the **Kronecker product form** \n", "\n", "$$\\Delta_{2D} = \\Delta_{1D} \\otimes I + I \\otimes \\Delta_{1D},$$\n", "\n", "where $\\Delta_{1D}$ is a 1D Laplacian, and $\\otimes$ is a **Kronecker product** of matrices. \n", "\n", "For matrices $A\\in\\mathbb{R}^{n\\times m}$ and $B\\in\\mathbb{R}^{l\\times k}$ its Kronecker product is defined as a block matrix of the form \n", "\n", "$$\n", " A\\otimes B = \\begin{bmatrix}a_{11}B & \\dots & a_{1m}B \\\\ \\vdots & \\ddots & \\vdots \\\\ a_{n1}B & \\dots & a_{nm}B\\end{bmatrix}\\in\\mathbb{R}^{nl\\times mk}.\n", "$$" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "In the block matrix form the 2D-Laplace matrix can be written in the following form: \n", "\n", "$$A = -\\frac{1}{h^2}\\begin{bmatrix} \\Delta_1 + 2I & -I & 0 & 0 & 0\\\\ -I & \\Delta_1 + 2I & -I & 0 &0 \\\\ 0 & -I & \\Delta_1 + 2I & -I & 0 \\\\ 0 & 0 & -I & \\Delta_1 + 2I &-I\\\\ 0 & 0 & 0 & -I & \\Delta_1 + 2I \\end{bmatrix}$$" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Short list of Kronecker product properties\n", "\n", "- It is bilinear\n", "- $(A\\otimes B) (C\\otimes D) = AC \\otimes BD$\n", "- Let $\\mathrm{vec}(X)$ be vectorization of matrix $X$ columnwise. Then \n", "$\\mathrm{vec}(AXB) = (B^T \\otimes A) \\mathrm{vec}(X).$" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Sparse matrix\n", "\n", "We can create this matrix using **scipy.sparse** package (actually this is **not the best** sparse matrix package)\n", "\n", "We can go to really large sizes (at least, to store this matrix in the memory).\n", "\n", "Please note the following functions\n", "- Create sparse matrices with given diagonals ```spdiags```\n", "- Kronecker product of sparse matrices ```kron```\n", "- There is also overloaded arithmectics for sparse matrices" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false, "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n0vCLFOr/vZKAwUorgPwAAAAASUVORK5CYII=\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import numpy as np\n", "import scipy as sp\n", "import scipy.sparse\n", "from scipy.sparse import csc_matrix, csr_matrix\n", "import matplotlib.pyplot as plt\n", "import scipy.linalg\n", "import scipy.sparse.linalg\n", "%matplotlib inline\n", "n = 5\n", "ex = np.ones(n);\n", "lp1 = sp.sparse.spdiags(np.vstack((ex, -2*ex, ex)), [-1, 0, 1], n, n, 'csr'); \n", "e = sp.sparse.eye(n)\n", "A = sp.sparse.kron(lp1, e) + sp.sparse.kron(e, lp1)\n", "A = csc_matrix(A)\n", "plt.spy(A, aspect='equal', marker='.', markersize=5)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Sparsity pattern \n", "\n", "The ```spy``` command plots the sparsity pattern of the matrix: the $(i, j)$ pixel is drawn, if the corresponding matrix element is non-zero.\n", "\n", "Sparsity pattern is really important for the understanding the complexity of the sparse linear algebra algorithms. \n", "\n", "Often, only the sparsity pattern is needed for the analysis of \"how complex\" the matrix is." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Sparse matrix: definition\n", "\n", "The definition of \"sparse matrix\" is that the number of **non-zero elements** is much less than the total number of elements, so you can do the basic linear algebra operations (like solving linear systems at the first place) can be done faster, than if working for with the full matrix.\n", "\n", "The **scipy.sparse** package has tools for solving sparse linear systems:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false, "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "n = 10\n", "ex = np.ones(n);\n", "lp1 = sp.sparse.spdiags(np.vstack((ex, -2*ex, ex)), [-1, 0, 1], n, n, 'csr'); \n", "e = sp.sparse.eye(n)\n", "A = sp.sparse.kron(lp1, e) + sp.sparse.kron(e, lp1)\n", "A = csr_matrix(A)\n", "rhs = np.ones(n * n)\n", "sol = sp.sparse.linalg.spsolve(A, rhs)\n", "_, (ax1, ax2) = plt.subplots(1, 2)\n", "ax1.plot(sol)\n", "ax1.set_title('Not reshaped solution')\n", "ax2.contourf(sol.reshape((n, n), order='f'))\n", "ax2.set_title('Reshaped solution')" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## What we need to find out to see how it actually works\n", "\n", "**Question 1:** How to store the sparse matrix in memory?\n", "\n", "**Question 2:** How to multiply sparse matrix by vector fast?\n", "\n", "**Question 3:** How to solve linear systems with sparse matrices fast?\n", "\n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Sparse matrix storage\n", "\n", "There are many storage formats, important ones:\n", "\n", "- COO (Coordinate format)\n", "- LIL (Lists of lists)\n", "- CSR (compressed sparse row)\n", "- CSC (compressed sparse column)\n", "- Block variants\n", "\n", "In ```scipy``` there are constructors for each of these formats, e.g. ```scipy.sparse.lil_matrix(A)```." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Coordinate format (COO)\n", "\n", "The simplest format is to use **coordinate format** to represent the sparse matrix as positions and values of non-zero elements.\n", "\n", "```python\n", "i, j, val\n", "```\n", "\n", "where ```i, j``` are integer array of indices, ```val``` is the real array of matrix elements.
\n", "So we need to store $3\\cdot$ **nnz** elements, where **nnz** denotes number of nonzeroes in the matrix.\n", "\n", "**Q:** What is good and what is bad in this format?" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Main disadvantages\n", "\n", "- It is not optimal in storage (why?)\n", "- It is not optimal for matrix-by-vector product (why?)\n", "- It is not optimal for removing elements as you must make **nnz** operations to find one element (this is good in LIL format)\n", "\n", "First two disadvantages are solved by **compressed sparse row** (CSR) format." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Compressed sparse row (CSR)\n", "\n", "In the CSR format a matrix is stored as 3 different arrays: \n", "```python\n", "ia, ja, sa\n", "```\n", "where:\n", "\n", "- **ia** (row start) is an integer array of length $n+1$ \n", "- **ja** (column indices) is an integer array of length **nnz** \n", "- **sa** (values) is an real-value array of length **nnz**\n", "\n", "\n", "\n", "So, we got $2\\cdot{\\bf nnz} + n+1$ elements." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## CSR helps for matrix-by-vector product as well\n", "```python\n", " for i in range(n):\n", " for k in range(ia[i]:ia[i+1]):\n", " y[i] += sa[k] * x[ja[k]]\n", "```" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Let us do a short timing test" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false, "slideshow": { "slide_type": "slide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1.22 ms ± 11.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n", "1.52 ms ± 50.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n" ] } ], "source": [ "import numpy as np\n", "import scipy as sp\n", "import scipy.sparse\n", "import scipy.sparse.linalg\n", "from scipy.sparse import csc_matrix, csr_matrix, coo_matrix\n", "import matplotlib.pyplot as plt\n", "%matplotlib inline\n", "n = 400\n", "ex = np.ones(n);\n", "lp1 = sp.sparse.spdiags(np.vstack((ex, -2*ex, ex)), [-1, 0, 1], n, n, 'csr'); \n", "e = sp.sparse.eye(n)\n", "A = sp.sparse.kron(lp1, e) + sp.sparse.kron(e, lp1)\n", "A = csr_matrix(A)\n", "rhs = np.ones(n * n)\n", "B = coo_matrix(A)\n", "%timeit A.dot(rhs)\n", "%timeit B.dot(rhs)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "As you see, **CSR** is faster, and for more **unstructured patterns** the gain will be larger." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Sparse matrices and efficiency\n", "Sparse matrices give complexity reduction. \n", "But they are **not very good** for parallel/GPU implementation. \n", "\n", "And they do not give maximal efficiency due to **random data access**. \n", "Typically, peak efficiency of $10\\%-15\\%$ is considered good. " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Recall how we measure efficiency of linear algebra operations\n", "\n", "The standard way to measure the efficiency of linear algebra operations on a particular computing architecture is to use **flops** (number of floating point operations per second)\n", "\n", "The peak performance is determined as \n", "\n", " **frequency** $\\times$ **number of cores** $\\times$ **pipeline size** $\\times$ 2.\n", "\n", "We can measure peak efficiency of an ordinary matrix-by-vector product." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false, "slideshow": { "slide_type": "slide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Time: 7.3e-03, Efficiency: 4.4e+00 Gflops\n" ] } ], "source": [ "import numpy as np\n", "import time\n", "n = 4000\n", "a = np.random.randn(n, n)\n", "v = np.random.randn(n)\n", "t = time.time()\n", "np.dot(a, v)\n", "t = time.time() - t\n", "print('Time: {0: 3.1e}, Efficiency: {1: 3.1e} Gflops'.\\\n", " format(t, ((2 * n ** 2)/t) / 10 ** 9))" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false, "slideshow": { "slide_type": "slide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Time: 1.4e-04, Efficiency: 8.4e-03 Gflops\n" ] } ], "source": [ "n = 400\n", "ex = np.ones(n);\n", "a = sp.sparse.spdiags(np.vstack((ex, -2*ex, ex)), [-1, 0, 1], n, n, 'csr'); \n", "rhs = np.random.randn(n)\n", "t = time.time()\n", "a.dot(rhs)\n", "t = time.time() - t\n", "print('Time: {0: 3.1e}, Efficiency: {1: 3.1e} Gflops'.\\\n", " format(t, (3 * n) / t / 10 ** 9))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## How to make things more efficient\n", "\n", "Sparse matrix computations dominate linear algebra computations nowadays.
\n", "They allow us to work with much larger matrices, but they utilize only $10\\%-15\\%$ percent of the peak computer performance.
\n", "It means, that our computer architecture **is not well suited** for standard sparse matrix algorithms.\n", "\n", "There are many possible solutions of the problem, for example:\n", "\n", "1. Use block sparse format\n", "2. Reorder equations to make them more \"block\"\n", "3. Instead of vectors, use \"block vectors\"" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false, "slideshow": { "slide_type": "slide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Time: 5.5e-03, Efficiency: 5.5e-01 Gflops\n" ] } ], "source": [ "n = 1000000\n", "k = 1\n", "ex = np.ones(n);\n", "a = sp.sparse.spdiags(np.vstack((ex, -2*ex, ex)), [-1, 0, 1], n, n, 'csr'); \n", "rhs = np.random.randn(n, k)\n", "t = time.time()\n", "a.dot(rhs)\n", "t = time.time() - t\n", "print('Time: {0: 3.1e}, Efficiency: {1: 3.1e} Gflops'.\\\n", " format(t, (3 * n * k) / t / 10 ** 9))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Florida sparse matrix collection\n", "\n", "There are many other types of matrices besides tridiagonal/block tridiagonal\n", "\n", "[Florida sparse matrix collection](http://www.cise.ufl.edu/research/sparse/matrices/) which contains all sorts of matrices for different applications.\n", "\n", "It also allows for finding test matrices as well! \n", "\n", "We can have a look." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false, "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from IPython.display import HTML\n", "HTML('')" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Visualization of sparse matrices and graphs\n", "\n", "Sparse matrices and fast algorithms (especially for linear systems) have deep connection with graph theory.\n", "\n", "First of all, sparse matrix can be treated as an **adjacency matrix** of a certain graph:\n", "\n", "The vertices $(i, j)$ are connected, if the corresponding matrix element is non-zero.\n", "\n", "If the matrix is symmetric the graph can be undirected, otherwise it is directed." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Graph structure is important for LU decomposition\n", "\n", "Why sparse linear systems can be solved faster, what is the technique? \n", "\n", "In the LU factorization of the matrix $A$ the factors $L$ and $U$ can be also sparse:\n", "\n", "$$A = L U$$\n", "\n", "And solving linear systems with **sparse** triangular matrices is very easy. \n", "\n", " Note that the inverse matrix is not sparse! \n" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false, "slideshow": { "slide_type": "slide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[-2. 1. 0. 0. 0. 0. 0.]\n", " [ 1. -2. 1. 0. 0. 0. 0.]\n", " [ 0. 1. -2. 1. 0. 0. 0.]\n", " [ 0. 0. 1. -2. 1. 0. 0.]\n", " [ 0. 0. 0. 1. -2. 1. 0.]\n", " [ 0. 0. 0. 0. 1. -2. 1.]\n", " [ 0. 0. 0. 0. 0. 1. -2.]]\n", "[[-0.875 -0.75 -0.625 -0.5 -0.375 -0.25 -0.125]\n", " [-0.75 -1.5 -1.25 -1. -0.75 -0.5 -0.25 ]\n", " [-0.625 -1.25 -1.875 -1.5 -1.125 -0.75 -0.375]\n", " [-0.5 -1. -1.5 -2. -1.5 -1. -0.5 ]\n", " [-0.375 -0.75 -1.125 -1.5 -1.875 -1.25 -0.625]\n", " [-0.25 -0.5 -0.75 -1. -1.25 -1.5 -0.75 ]\n", " [-0.125 -0.25 -0.375 -0.5 -0.625 -0.75 -0.875]]\n" ] } ], "source": [ "#Indeed, it is not sparse\n", "n = 7\n", "ex = np.ones(n);\n", "a = sp.sparse.spdiags(np.vstack((ex, -2*ex, ex)), [-1, 0, 1], n, n, 'csr'); \n", "a = a.todense()\n", "b = np.array(np.linalg.inv(a))\n", "print(a)\n", "print(b)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## And the factors...\n", "\n", "$L$ and $U$ are typically sparse. In the tridiagonal case they are even bidiagonal!" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[ 1. 0. 0. 0. 0. 0. 0. ]\n", " [-0.5 1. 0. 0. 0. 0. 0. ]\n", " [ 0. -0.66666667 1. 0. 0. 0. 0. ]\n", " [ 0. 0. -0.75 1. 0. 0. 0. ]\n", " [ 0. 0. 0. -0.8 1. 0. 0. ]\n", " [ 0. 0. 0. 0. -0.83333333 1. 0. ]\n", " [ 0. 0. 0. 0. 0. -0.85714286\n", " 1. ]]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/home/alex/anaconda2/envs/p3/lib/python3.6/site-packages/scipy/sparse/linalg/dsolve/linsolve.py:253: SparseEfficiencyWarning: splu requires CSC matrix format\n", " warn('splu requires CSC matrix format', SparseEfficiencyWarning)\n" ] } ], "source": [ "from scipy.sparse.linalg import splu\n", "T = splu(a, permc_spec=\"NATURAL\")\n", "print(T.L.todense())" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Interesting to note that ```splu``` without ```permc_spec``` will produce permutations which will not yield the bidiagonal factor:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[ 1. 0. 0. 0. 0. 0. 0. ]\n", " [-0.5 1. 0. 0. 0. 0. 0. ]\n", " [ 0. -0.66666667 1. 0. 0. 0. 0. ]\n", " [ 0. 0. -0.75 1. 0. 0. 0. ]\n", " [ 0. 0. 0. 0. 1. 0. 0. ]\n", " [ 0. 0. 0. -0.8 -0.5 1. 0. ]\n", " [ 0. 0. 0. 0. -0.5 -0.71428571\n", " 1. ]]\n", "[0 1 2 3 5 4 6]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/home/alex/anaconda2/envs/p3/lib/python3.6/site-packages/scipy/sparse/linalg/dsolve/linsolve.py:253: SparseEfficiencyWarning: splu requires CSC matrix format\n", " warn('splu requires CSC matrix format', SparseEfficiencyWarning)\n" ] } ], "source": [ "from scipy.sparse.linalg import splu\n", "T = splu(a)\n", "print(T.L.todense())\n", "print(T.perm_c)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## 2D-case\n", "In a 2D case everything is much worse:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "n = 20\n", "ex = np.ones(n);\n", "lp1 = sp.sparse.spdiags(np.vstack((ex, -2*ex, ex)), [-1, 0, 1], n, n, 'csr'); \n", "e = sp.sparse.eye(n)\n", "A = sp.sparse.kron(lp1, e) + sp.sparse.kron(e, lp1)\n", "A = csc_matrix(A)\n", "T = scipy.sparse.linalg.spilu(A)\n", "plt.spy(T.L, marker='.', color='k', markersize=8)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "For correct permutation in 2D case the number of nonzeros in $L$ factor grows as $\\mathcal{O}(N \\log N)$. But complexity is $\\mathcal{O}(N^{3/2})$." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Sparse matrices and graph ordering\n", "The number of nonzeros in LU decomposition has a deep connection to the graph theory.\n", "\n", "(I.e., there is an edge between $(i, j)$ if $a_{ij} \\ne 0$.\n", "\n", "``networkx package`` can be used to visualize graphs, given only the adjacency matrix. \n", "\n", "It may even recover to some extend the graph structure." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false, "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "image/png": "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\nNAny8vL4448/GsQ+l8vF27dvKwn3q1evoKSkhCny8lj66BEkuVxwJCRQcOgQWk+c+MNODtu+fTvC\nw8MRHh7+w/r4Pd68eYM+ffogLi6ukiBUzK62t7fHokWLGsWfu3fvYsyYMXjy5Mk/cbyrIT8/H1pa\nWnj27JlQ18UTEUaNGgVNTU2MHz+e1+qOjY3FvXv3kJ+fjxYtWqBjx44wNDTk6zLX1dXlm+hFgYE4\n4ugI2717oTh9eo11z507F82aNYOHh4fQ7oeBobFgBLoBuXnzJiZPnoyXL182+oQsDoeD5ORkFE+b\nhs6RkbzPfSQksFhCosrcy3p6epCTk2tUP//Nz9y1DXzNTdy7d28sW7asyvOpqano2bMnLl++jB49\nejSKTytXrsTz588RFBRU4wvPvn37cPv2bfj7+wvdj+zsbJiamiJ09my0e/UKxzIysPXlSyxevBhu\nbm748uULX6u74jg5ORmKiorQ09PDGHFxTLt2DeJstsDhPLOystChQwc8fvwYGhoaQr8vBoYGpWGT\nZf3/UlpaSvr6+nTx4kWR+rGhWzcqb978a3pGaWmiwEBeCseTJ0/SmjVryNHRkbp160YyMjIkJydH\nZmZm5OzsTO7u7nTq1CmKjY1t8DR+LBaLevToQQcOHGjQehqKGzdukKamJpWUlFRb7uzZs6Sjo9No\naRHLysqoS5cudPDgwWrLcblcMjAwoFu3bjWYL9fmzaOiv9OEljVvTkWnTtV4DZvNpuTkZAoNDaWn\nFhZ8qUYLJk8WqN4VK1bQ9OnT6+s+A0Ojw7SgGwh3d3c8evQIFy9eFJkPYWFhcHNzQ9y2bWh+82aN\nk9OICNnZ2bzWS0JCAm9LTExEixYt+FrbFZuurm69wzpu27YN165d+ym7tjkcDrp3747ly5fDwcGh\nxvKzZs1Cfn4+/Pz8GuVe4+LiYGFhgejoaOjq6lZZJjw8HIsWLcLTp0+F7lNiYiK2bNmC3n/9hRll\nZf+cqO2ys6AglNnbQ4LNRnmzZpgqJYUOK1bgt99+qzboz+fPn6Gvr4+7d+9CX1+/HnfCwNDIiPgF\n4T/JmzdvqG3btpSWliYyH9hsNnXs2JHOnz8vFHtcLpcyMjLo5s2bdOjQIVqyZAnZ2dmRsbExSUpK\nkpqaGllaWtKMGTNox44ddOHCBXr58mWNLUoiohcvXpC8vDylpqYKxdfG5tixY9SrVy/icrkClS8u\n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\n85GUlIRZs2ZBS0urkUZNVDe8xE1EzyQhBHbu3ImZM2di7NixWNyvHzK3b8fKX37B5U6dsGbNGvTu\n3buxh0lUZwxoInqmZWZm4ntnZ3waFwdtIVCiqQn1iAiojRrV2EMjqhde4iaiZ5q+vj58evWC9r9z\nDY2iIqgdOdLIoyKqPwY0ET377O3LO2RxFTB6XvASNxE9H6KiytbPtrfnKmD0XGBAExERqSBe4iYi\nIlJBDGgiIiIVxIAmIiJSQQxoIiIiFcSAJiIiUkEMaCIiIhXEgCYiIlJBDGgiIiIVxIAmIiJSQQxo\nIiIiFcSAJiIiUkEMaCIiIhXEgCYiIlJBDGgiIiIVxIAmIiJSQQxoIiIiFcSAJiIiUkEMaCIiIhXE\ngCYiIlJBDGgiIiIVxIAmIiJSQQxoIiIiFcSAJiIiUkEMaCIiIhXEgCYiIlJBDGgiIiIVxIAmIiJS\nQQxoIiIiFcSAJiIiUkEMaCIiIhXEgCYiIlJBDGgiIiIVxIAmIiJSQQxoIiIiFcSAJiIiUkEMaCIi\nIhXEgCYiIlJBDGgiIiIVxIAmIiJSQQxoIiIiFcSAJiIiUkEMaCIiIhXEgCYiIlJBDGgiIiIVxIAm\nIiJSQf8PY12Y3qngTjkAAAAASUVORK5CYII=\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import networkx as nx\n", "n = 10\n", "ex = np.ones(n);\n", "lp1 = sp.sparse.spdiags(np.vstack((ex, -2*ex, ex)), [-1, 0, 1], n, n, 'csr'); \n", "e = sp.sparse.eye(n)\n", "A = sp.sparse.kron(lp1, e) + sp.sparse.kron(e, lp1)\n", "A = csc_matrix(A)\n", "G = nx.Graph(A)\n", "nx.draw(G, pos=nx.spring_layout(G), node_size=10)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Fill-in\n", "\n", "The fill-in of a matrix are those entries which change from an initial zero to a nonzero value during the execution of an algorithm.\n", "\n", "The fill-in is different for different permutations. So, before factorization we need to find reordering which produces the smallest fill-in.\n", "\n", "**Example**\n", "$$A = \n", " \\begin{bmatrix} \n", " * & * & * & * & *\\\\\n", " * & * & 0 & 0 & 0 \\\\\n", " * & 0 & * & 0 & 0 \\\\\n", " * & 0 & 0& * & 0 \\\\\n", " * & 0 & 0& 0 & *\n", " \\end{bmatrix}\n", "$$\n", "If we eliminate elements from the top to the bottom, then we will obtain dense matrix.\n", "However, we could maintain sparsity if elimination was done from the bottom to the top." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Fill-in minimization\n", "\n", "Reordering the rows and the columns of the sparse matrix in order to reduce the number of nonzeros in $L$ and $U$ factors is called **fill-in** minimization.
\n", "\n", "Examples of algorithms for fill-in minimization:\n", "\n", "- **Minimum degree ordering** - order by the degree of the vertex\n", "- **Cuthill–McKee algorithm** (and reverse Cuthill-McKee) - reorder to minimize the bandwidth (does not exploit graph representation).\n", "- **Nested dissection**: split the graph into two with minimal number of vertices on the separator (set of vertices removed after we separate the graph into two distinct connected graphs).
Complexity of the algorithm depends on the size of the graph separator. For 1D Laplacian separator contains only 1 vertex, in 2D - $\\sqrt{N}$ vertices." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Take home message\n", "- CSR format for storage\n", "- Sparse matrices & graphs ordering\n", "- Ordering is important for LU fill-in: more details this Thursday" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Questions?" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from IPython.core.display import HTML\n", "def css_styling():\n", " styles = open(\"./styles/custom.css\", \"r\").read()\n", " return HTML(styles)\n", "css_styling()" ] } ], "metadata": { "anaconda-cloud": {}, "celltoolbar": "Slideshow", "kernelspec": { "display_name": "Python [conda env:py35]", "language": "python", "name": "conda-env-py35-py" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.2" }, "nav_menu": {}, "toc": { "navigate_menu": true, "number_sections": false, "sideBar": true, "threshold": 6, "toc_cell": false, "toc_section_display": "block", "toc_window_display": false } }, "nbformat": 4, "nbformat_minor": 1 }