{"id":17113060,"url":"https://github.com/emmt/triangularmatricestutorial.jl","last_synced_at":"2026-03-18T23:26:20.228Z","repository":{"id":37012479,"uuid":"504608201","full_name":"emmt/TriangularMatricesTutorial.jl","owner":"emmt","description":"Experiment algorithms involving triangular matrices with Julia","archived":false,"fork":false,"pushed_at":"2022-06-19T10:37:37.000Z","size":18,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-29T05:41:53.832Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Julia","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/emmt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-06-17T16:47:39.000Z","updated_at":"2022-06-19T17:06:07.000Z","dependencies_parsed_at":"2022-08-18T00:50:20.862Z","dependency_job_id":null,"html_url":"https://github.com/emmt/TriangularMatricesTutorial.jl","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emmt%2FTriangularMatricesTutorial.jl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emmt%2FTriangularMatricesTutorial.jl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emmt%2FTriangularMatricesTutorial.jl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emmt%2FTriangularMatricesTutorial.jl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/emmt","download_url":"https://codeload.github.com/emmt/TriangularMatricesTutorial.jl/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245177283,"owners_count":20573074,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-14T17:02:41.864Z","updated_at":"2026-01-04T17:50:44.546Z","avatar_url":"https://github.com/emmt.png","language":"Julia","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TriangularMatricesTutorial\n\n[![Build Status](https://github.com/emmt/TriangularMatricesTutorial.jl/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/emmt/TriangularMatricesTutorial.jl/actions/workflows/CI.yml?query=branch%3Amain)\n[![Build Status](https://ci.appveyor.com/api/projects/status/github/emmt/TriangularMatricesTutorial.jl?svg=true)](https://ci.appveyor.com/project/emmt/TriangularMatricesTutorial-jl)\n[![Coverage](https://codecov.io/gh/emmt/TriangularMatricesTutorial.jl/branch/main/graph/badge.svg)](https://codecov.io/gh/emmt/TriangularMatricesTutorial.jl)\n\nThis repository is to experiment algorithms involving triangular matrices.  The\nmain objective is to provide reference algorithms that can be fast and yet\nwhose code remains readable.  In some sense, this shows how Julia can be good\nat helping you to write efficient code that is almost as simple as its\nmathematical counterpart.  You are encouraged to have a look at\n[`src/lmul.jl`](src/lmul.jl) which implements left-multiplication and\n[`src/ldiv.jl`](src/ldiv.jl) which implements left-division by triangular\nmatrices.\n\nThe following is provided:\n\n- In-place row-wise and column-wise left-multiplication (by `lmul` and `lmul!`\n  methods) and left-division (by `ldiv` and `ldiv!` methods) of a vector by a\n  triangular matrix, by its adjoint, or by its transpose.\n\nThis package is largely a work in progress, and we consider implementing the\nfollowing things:\n\n- Algorithms for the Cholesky decomposition.\n\n- Linear least-squares.\n\n\n## Usage\n\nLeft-multiplication and left-division of vector `b` by triangular matrix `A`\ncan be computed by:\n\n```julia\nusing TriangularMatricesTutorial\nlmul(A, b) # -\u003e A*b\nldiv(A, b) # -\u003e A\\b\n```\n\nNothing terrible, yet you can choose a row-wise or column-wise method:\n\n```julia\nlmul(RowWise(), A, b) # -\u003e A*b computed by a row-wise algorithm\nldiv(RowWise(), A, b) # -\u003e A\\b computed by a row-wise algorithm\nlmul(ColumnWise(), A, b) # -\u003e A*b computed by a column-wise algorithm\nldiv(ColumnWise(), A, b) # -\u003e A\\b computed by a column-wise algorithm\n```\n\nWhich algorithm is the fastest depends largely on the storage order of the\nentries of `A` (colum-major or row-major).  Column-wise methods addresses the\nmatrix entries in column-major order so you might think that they are the\nmethods of choice since Julia stores regular arrays in column-major order.\nJulia is however quite flexible about the definition of a matrix so the most\nsuitable method depends on the type of `A`.  For example, Julia makes use of\nannotations to lazily represent the transpose or the adjoint of a matrix, which\nlazily changes the storage order of entries.  If no specific row-/column-wise\nmethod is chosen, `lmul` and `ldiv` will automatically peek a suitable one\n(hopefully).  This automatic choice may not always be accurate (e.g. gor small\nmatrices) and you may want to try a different one.  You may also want to play\nwith this option.  This is precisely one of the purpose of this package!\n\nThe `RowWise` and `ColumnWise` methods take an optional argument to specify the\noptimization level for loops:\n\n- `Debug` to perform bounds checking.  This yields the safest but usually\n  the slowest code.\n\n- `InBounds` to assume that all indices are correct and avoid bounds checking.\n  This yields code that is a bit faster than with `Debug`, although Julia has a\n  tendency to be very smart and fast for bounds checking.\n\n- `Vectorize` to perform SIMD loop optimization.  This also avoid bounds\n  checking.  This usually yields the fastest code.\n\nThe default optimization level is `Vectorize`, but you may want to play with\nthis parameter.\n\nIf you want to completely avoid allocations, you may use the in-place versions\nof the `lmul` and `ldiv` methods:\n\n```julia\nlmul!(opt, [dst=b,] A, b) -\u003e dst\nldiv!(opt, [dst=b,] A, b) -\u003e dst\n```\n\nwhich overwrite `dst` with the result of `A*b` and of `A\\b`.  If `dst` is not\nspecified and if `A` is a triangular matrix, the operation can be applied\nin-place, overwriting `b`.  Argument `opt` is either an optimization level\n(e.g. `Debug`, `InBounds`, or `Vectorize`) or an instance of `RowWise` or\n`ColumnWise`.  This argument is required to avoid type-piracy.  If you do not\nspecify it, you will be calling the methods implemented by Julia.\n\nThe following table summarizes some results for `L` lower triangular of size\n`n×n` with for `n=100` and elements of type `T=Float64` (the number of\noperations is `n²` for multiplying by a triangular matrix, `2n²-n` for a full\nsquare matrix).\n\n| Code                                                     | Time     | Power       |\n|:---------------------------------------------------------|:---------|:------------|\n| `@btime lmul!($(RowWise(Debug)),$dst,$L,$b);`            | 3.369 μs | 2.97 Gflops |\n| `@btime lmul!($(RowWise(InBounds)),$dst,$L,$b);`         | 2.783 μs | 3.59 Gflops |\n| `@btime lmul!($(RowWise(Vectorize)),$dst,$L,$b);`        | 4.394 μs | 2.28 Gflops |\n| `@btime lmul!($(ColumnWiseWise(Debug)),$dst,$L,$b);`     | 2.895 μs | 3.45 Gflops |\n| `@btime lmul!($(ColumnWiseWise(InBounds)),$dst,$L,$b);`  | 1.338 μs | 7.47 Gflops |\n| `@btime lmul!($(ColumnWiseWise(Vectorize)),$dst,$L,$b);` | 1.303 μs | 7.67 Gflops |\n\nFor the left-division:\n\n| Code                                                     | Time     | Power       |\n|:---------------------------------------------------------|:---------|:------------|\n| `@btime ldiv!($dst,$L,$b);`                              | 1.596 μs | 6.27 Gflops |\n| `@btime ldiv!($(RowWise(Debug)),$dst,$L,$b);`            | 3.290 μs | 3.04 Gflops |\n| `@btime ldiv!($(RowWise(InBounds)),$dst,$L,$b);`         | 2.832 μs | 3.53 Gflops |\n| `@btime ldiv!($(RowWise(Vectorize)),$dst,$L,$b);`        | 3.954 μs | 2.53 Gflops |\n| `@btime ldiv!($(ColumnWiseWise(Debug)),$dst,$L,$b);`     | 3.692 μs | 2.71 Gflops |\n| `@btime ldiv!($(ColumnWiseWise(InBounds)),$dst,$L,$b);`  | 1.308 μs | 7.65 Gflops |\n| `@btime ldiv!($(ColumnWiseWise(Vectorize)),$dst,$L,$b);` | 1.411 μs | 7.09 Gflops |\n\nThe first line is using the method in `LinearAlgebra` (based on BLAS) which is\nabout 20% slower than the best method (here `ColumnWiseWise(InBounds)`).  Not\ntoo bad for pure Julia code.  Note that BLAS will win for large matrix\n(typically for `n` larger than a few hundreds).\n\n\n## Implementation notes\n\nWe use [`MayOptimize`]() package for flexible loop optimizations and specific\ntypes (based on `TriangularMatricesTutorial.Algorithm`) to avoid type-piracy.\n\nJulia annotates matrix for lazy transpose and adjoint and to indicate specific\nmatrix structure (`LowerTriangular`, `UnitLowerTriangular`, etc.).  The\ntriangle matrix annotation has some overheads in indexing operations (to ensure\nthat the results of the `getindex` and `setindex!` calls are consistent with\nthe structure of the matrix).  To avoid these overheads, we unveil the parent\nmatrix of triangular matrices and account for the specific structure by\nrestricting the indices ised.  Adjoint and transpose are handled without\npenalty so this is left unchanged (it reduces a lot the number of algorithms to\ncode).\n\nSince Julia 1.6, the triangular annotation is always kept on top of the other\nannotations so unveilling it is easy but this has to be explicitely done for\nother.\n\nIn spite of this, implemented algorithms remain quite readable and simple and\nyet efficient.\n\n## Installation\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femmt%2Ftriangularmatricestutorial.jl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Femmt%2Ftriangularmatricestutorial.jl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femmt%2Ftriangularmatricestutorial.jl/lists"}