{"id":19093712,"url":"https://github.com/juliaarrays/lazyarrays.jl","last_synced_at":"2025-10-05T03:47:11.607Z","repository":{"id":33502115,"uuid":"136339004","full_name":"JuliaArrays/LazyArrays.jl","owner":"JuliaArrays","description":"Lazy arrays and linear algebra in Julia","archived":false,"fork":false,"pushed_at":"2025-02-12T08:28:10.000Z","size":1134,"stargazers_count":307,"open_issues_count":43,"forks_count":29,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-02-12T09:42:03.120Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","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/JuliaArrays.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-06-06T14:17:05.000Z","updated_at":"2025-02-10T15:37:21.000Z","dependencies_parsed_at":"2023-10-03T12:58:04.339Z","dependency_job_id":"c4a2fed6-b537-464e-bd9b-8e834f32ab2c","html_url":"https://github.com/JuliaArrays/LazyArrays.jl","commit_stats":{"total_commits":342,"total_committers":19,"mean_commits":18.0,"dds":0.1257309941520468,"last_synced_commit":"fd72ec5f322c882e27bafa74d8e8e1b4bbb9e5b4"},"previous_names":[],"tags_count":172,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JuliaArrays%2FLazyArrays.jl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JuliaArrays%2FLazyArrays.jl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JuliaArrays%2FLazyArrays.jl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JuliaArrays%2FLazyArrays.jl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JuliaArrays","download_url":"https://codeload.github.com/JuliaArrays/LazyArrays.jl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240142895,"owners_count":19754643,"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-11-09T03:25:48.342Z","updated_at":"2025-10-05T03:47:06.521Z","avatar_url":"https://github.com/JuliaArrays.png","language":"Julia","funding_links":[],"categories":[],"sub_categories":[],"readme":"# LazyArrays.jl\n\n[![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://juliaarrays.github.io/LazyArrays.jl/dev)\n[![Build Status](https://github.com/JuliaArrays/LazyArrays.jl/workflows/CI/badge.svg)](https://github.com/JuliaArrays/LazyArrays.jl/actions)\n[![codecov](https://codecov.io/gh/JuliaArrays/LazyArrays.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/JuliaArrays/LazyArrays.jl)\n[![pkgeval](https://juliahub.com/docs/General/LazyArrays/stable/pkgeval.svg)](https://juliaci.github.io/NanosoldierReports/pkgeval_badges/report.html)\n\nLazy arrays and linear algebra in Julia\n\nThis package supports lazy analogues of array operations like `vcat`, `hcat`,\nand multiplication. This helps with the implementation of matrix-free methods\nfor iterative solvers.\n\nThe package has been designed with high-performance in mind, so should outperform\nthe non-lazy analogues from Base for many operations like `copyto!` and broadcasting.\nSome operations will be inherently slower due to extra computation, like `getindex`.\nPlease file an issue for any examples that are significantly slower than their\nthe analogue in Base.\n\n## Lazy operations\n\nTo construct a lazy representation of a function call `f(x,y,z...)`, use the command\n`applied(f, x, y, z...)`. This will return an unmaterialized object typically of type `Applied`\nthat represents the operation. To realize that object, call `materialize`, which \nwill typically be equivalent to calling `f(x,y,z...)`. A macro `@~` is available as a shorthand:\n```julia\njulia\u003e using LazyArrays, LinearAlgebra\n\njulia\u003e applied(exp, 1)\nApplied(exp,1)\n\njulia\u003e materialize(applied(exp, 1))\n2.718281828459045\n\njulia\u003e materialize(@~ exp(1))\n2.718281828459045\n\njulia\u003e exp(1)\n2.718281828459045\n```\n\nNote that `@~` causes sub-expressions to be wrapped in an `applied` call, not\njust the top-level expression. This can lead to `MethodError`s when lazy\napplication of sub-expressions is not yet implemented. For example,\n```julia\njulia\u003e @~ Vector(1:10) .* ones(10)'\nERROR: MethodError: ...\n\njulia\u003e A = Vector(1:10); B = ones(10); (@~ sum(A .* B')) |\u003e materialize\n550.0\n```\n\nThe benefit of lazy operations is that they can be materialized in-place, \npossible using simplifications. For example, it is possible to \ndo BLAS-like Matrix-Vector operations of the form `α*A*x + β*y` as \nimplemented in `BLAS.gemv!` using a lazy applied object:\n```julia\njulia\u003e A = randn(5,5); b = randn(5); c = randn(5); d = similar(c);\n\njulia\u003e d .= @~ 2.0 * A * b + 3.0 * c # Calls gemv!\n5-element Array{Float64,1}:\n -2.5366335879717514\n -5.305097174484744  \n -9.818431932350942  \n  2.421562605495651  \n  0.26792916096572983\n\njulia\u003e 2*(A*b) + 3c\n5-element Array{Float64,1}:\n -2.5366335879717514\n -5.305097174484744  \n -9.818431932350942  \n  2.421562605495651  \n  0.26792916096572983\n\njulia\u003e function mymul(A, b, c, d) # need to put in function for benchmarking\n       d .= @~ 2.0 * A * b + 3.0 * c\n       end\nmymul (generic function with 1 method)\n\njulia\u003e @btime mymul(A, b, c, d) # calls gemv!\n  77.444 ns (0 allocations: 0 bytes)\n5-element Array{Float64,1}:\n -2.5366335879717514\n -5.305097174484744  \n -9.818431932350942  \n  2.421562605495651  \n  0.26792916096572983\n\njulia\u003e @btime 2*(A*b) + 3c; # does not call gemv!\n  241.659 ns (4 allocations: 512 bytes)\n```\n\nThis also works for inverses, which lower to BLAS calls whenever possible:\n```julia\njulia\u003e A = randn(5,5); b = randn(5); c = similar(b);\n\njulia\u003e c .= @~ A \\ b\n5-element Array{Float64,1}:\n -2.5366335879717514\n -5.305097174484744  \n -9.818431932350942  \n  2.421562605495651  \n  0.26792916096572983\n```\n\n\n\n## Lazy arrays\n\nOften we want lazy realizations of matrices, which are supported via `ApplyArray`.\nFor example, the following creates a lazy matrix exponential:\n```julia\njulia\u003e E = ApplyArray(exp, [1 2; 3 4])\n2×2 ApplyArray{Float64,2,typeof(exp),Tuple{Array{Int64,2}}}:\n  51.969   74.7366\n 112.105  164.074 \n```\n\nA lazy matrix exponential is useful for, say, in-place matrix-exponential*vector:\n```julia\njulia\u003e b = Vector{Float64}(undef, 2); b .= @~ E*[4,4]\n2-element Array{Float64,1}:\n  506.8220830628333\n 1104.7145995988594\n```\nWhile this works, it is not actually optimised (yet). \n\nOther options do have special implementations that make them fast. We now give some examples. \n\n\n### Concatenation\n\nLazy `vcat` and `hcat` allow for representing the concatenation of\nvectors without actually allocating memory, and support a fast\n`copyto!`  for allocation-free population of a vector.\n```julia\njulia\u003e using BenchmarkTools\n\njulia\u003e A = ApplyArray(vcat,1:5,2:3) # allocation-free\n7-element ApplyArray{Int64,1,typeof(vcat),Tuple{UnitRange{Int64},UnitRange{Int64}}}:\n 1\n 2\n 3\n 4\n 5\n 2\n 3\n\njulia\u003e Vector(A) == vcat(1:5, 2:3)\ntrue\n\njulia\u003e b = Array{Int}(undef, length(A)); @btime copyto!(b, A);\n  26.670 ns (0 allocations: 0 bytes)\n\njulia\u003e @btime vcat(1:5, 2:3); # takes twice as long due to memory creation\n  43.336 ns (1 allocation: 144 bytes)\n```\nSimilar is the lazy analogue of `hcat`:\n```julia\njulia\u003e A = ApplyArray(hcat, 1:3, randn(3,10))\n3×11 ApplyArray{Float64,2,typeof(hcat),Tuple{UnitRange{Int64},Array{Float64,2}}}:\n 1.0   1.16561    0.224871  -1.36416   -0.30675    0.103714    0.590141   0.982382  -1.50045    0.323747  -1.28173  \n 2.0   1.04648    1.35506   -0.147157   0.995657  -0.616321   -0.128672  -0.671445  -0.563587  -0.268389  -1.71004  \n 3.0  -0.433093  -0.325207  -1.38496   -0.391113  -0.0568739  -1.55796   -1.00747    0.473686  -1.2113     0.0119156\n\njulia\u003e Matrix(A) == hcat(A.args...)\ntrue\n\njulia\u003e B = Array{Float64}(undef, size(A)...); @btime copyto!(B, A);\n  109.625 ns (1 allocation: 32 bytes)\n\njulia\u003e @btime hcat(A.args...); # takes twice as long due to memory creation\n  274.620 ns (6 allocations: 560 bytes)\n```\n\n\n\n### Kronecker products\n\nWe can represent Kronecker products of arrays without constructing the full\narray:\n\n```julia\njulia\u003e A = randn(2,2); B = randn(3,3);\n\njulia\u003e K = ApplyArray(kron,A,B)\n6×6 ApplyArray{Float64,2,typeof(kron),Tuple{Array{Float64,2},Array{Float64,2}}}:\n -1.08736   -0.19547   -0.132824   1.60531    0.288579    0.196093 \n  0.353898   0.445557  -0.257776  -0.522472  -0.657791    0.380564 \n -0.723707   0.911737  -0.710378   1.06843   -1.34603     1.04876  \n  1.40606    0.252761   0.171754  -0.403809  -0.0725908  -0.0493262\n -0.457623  -0.576146   0.333329   0.131426   0.165464   -0.0957293\n  0.935821  -1.17896    0.918584  -0.26876    0.338588   -0.26381  \n\njulia\u003e C = Matrix{Float64}(undef, 6, 6); @btime copyto!(C, K);\n  61.528 ns (0 allocations: 0 bytes)\n\njulia\u003e C == kron(A,B)\ntrue\n```\n\n\n## Broadcasting\n\nBase includes a lazy broadcast object called `Broadcasting`, but this is\nnot a subtype of `AbstractArray`. Here we have `BroadcastArray` which replicates\nthe functionality of `Broadcasting` while supporting the array interface.\n```julia\njulia\u003e A = randn(6,6);\n\njulia\u003e B = BroadcastArray(exp, A);\n\njulia\u003e Matrix(B) == exp.(A)\ntrue\n\njulia\u003e B = BroadcastArray(+, A, 2);\n\njulia\u003e B == A .+ 2\ntrue\n```\nSuch arrays can also be created using the macro `@~` which acts on ordinary \nbroadcasting expressions combined with `LazyArray`:\n```julia\njulia\u003e C = rand(1000)';\n\njulia\u003e D = LazyArray(@~ exp.(C))\n\njulia\u003e E = LazyArray(@~ @. 2 + log(C))\n\njulia\u003e @btime sum(LazyArray(@~ C .* C'); dims=1) # without `@~`, 1.438 ms (5 allocations: 7.64 MiB)\n  74.425 μs (7 allocations: 8.08 KiB)\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuliaarrays%2Flazyarrays.jl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjuliaarrays%2Flazyarrays.jl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuliaarrays%2Flazyarrays.jl/lists"}