Repository: JuliaArrays/FillArrays.jl Branch: master Commit: 27bf96383e9e Files: 28 Total size: 239.9 KB Directory structure: gitextract_tzhpayfy/ ├── .github/ │ ├── dependabot.yml │ └── workflows/ │ ├── CompatHelper.yml │ ├── Invalidations.yml │ ├── TagBot.yml │ ├── ci.yml │ ├── docs.yml │ └── downstream.yml ├── .gitignore ├── LICENSE ├── Project.toml ├── README.md ├── appveyor.yml ├── benchmark/ │ └── benchmarks.jl ├── docs/ │ ├── Project.toml │ ├── make.jl │ └── src/ │ └── index.md ├── ext/ │ ├── FillArraysPDMatsExt.jl │ ├── FillArraysSparseArraysExt.jl │ ├── FillArraysStaticArraysExt.jl │ └── FillArraysStatisticsExt.jl ├── src/ │ ├── FillArrays.jl │ ├── fillalgebra.jl │ ├── fillbroadcast.jl │ ├── oneelement.jl │ └── trues.jl └── test/ ├── aqua.jl ├── infinitearrays.jl └── runtests.jl ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/dependabot.yml ================================================ # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: "github-actions" directory: "/" # Location of package manifests schedule: interval: "weekly" ================================================ FILE: .github/workflows/CompatHelper.yml ================================================ name: CompatHelper on: schedule: - cron: 0 0 * * * workflow_dispatch: permissions: contents: write pull-requests: write jobs: CompatHelper: runs-on: ubuntu-latest steps: - name: Check if Julia is already available in the PATH id: julia_in_path run: which julia continue-on-error: true - name: Install Julia, but only if it is not already available in the PATH uses: julia-actions/setup-julia@v3 with: version: '1' arch: ${{ runner.arch }} if: steps.julia_in_path.outcome != 'success' - name: "Add the General registry via Git" run: | import Pkg ENV["JULIA_PKG_SERVER"] = "" Pkg.Registry.add("General") shell: julia --color=yes {0} - name: "Install CompatHelper" run: | import Pkg name = "CompatHelper" uuid = "aa819f21-2bde-4658-8897-bab36330d9b7" version = "3" Pkg.add(; name, uuid, version) shell: julia --color=yes {0} - name: "Run CompatHelper" run: | import CompatHelper CompatHelper.main() shell: julia --color=yes {0} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # COMPATHELPER_PRIV: ${{ secrets.DOCUMENTER_KEY }} COMPATHELPER_PRIV: ${{ secrets.COMPATHELPER_PRIV }} ================================================ FILE: .github/workflows/Invalidations.yml ================================================ name: Invalidations on: pull_request: paths-ignore: - 'LICENSE' - 'README.md' - '.github/workflows/TagBot.yml' concurrency: # Skip intermediate builds: always. # Cancel intermediate builds: always. group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: evaluate: # Only run on PRs to the default branch. # In the PR trigger above branches can be specified only explicitly whereas this check should work for master, main, or any other default branch if: github.base_ref == github.event.repository.default_branch runs-on: ubuntu-latest steps: - uses: julia-actions/setup-julia@v3 with: version: '1.10' - uses: actions/checkout@v6 - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-invalidations@v1 id: invs_pr - uses: actions/checkout@v6 with: ref: ${{ github.event.repository.default_branch }} - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-invalidations@v1 id: invs_default - name: Report invalidation counts run: | echo "Invalidations on default branch: ${{ steps.invs_default.outputs.total }} (${{ steps.invs_default.outputs.deps }} via deps)" >> $GITHUB_STEP_SUMMARY echo "This branch: ${{ steps.invs_pr.outputs.total }} (${{ steps.invs_pr.outputs.deps }} via deps)" >> $GITHUB_STEP_SUMMARY - name: Check if the PR does increase number of invalidations if: steps.invs_pr.outputs.total > steps.invs_default.outputs.total run: exit 1 ================================================ FILE: .github/workflows/TagBot.yml ================================================ name: TagBot on: issue_comment: types: - created workflow_dispatch: inputs: lookback: default: 3 permissions: contents: write jobs: TagBot: if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot' runs-on: ubuntu-latest steps: - uses: JuliaRegistries/TagBot@v1 with: token: ${{ secrets.GITHUB_TOKEN }} ssh: ${{ secrets.DOCUMENTER_KEY }} ================================================ FILE: .github/workflows/ci.yml ================================================ name: CI on: push: branches: - master tags: [v*] paths-ignore: - 'LICENSE' - 'README.md' - '.github/workflows/TagBot.yml' pull_request: paths-ignore: - 'LICENSE' - 'README.md' - '.github/workflows/TagBot.yml' concurrency: group: build-${{ github.event.pull_request.number || github.ref }}-${{ github.workflow }} cancel-in-progress: true jobs: pre_job: # continue-on-error: true # Uncomment once integration is finished runs-on: ubuntu-latest # Map a step output to a job output outputs: should_skip: ${{ steps.skip_check.outputs.should_skip }} steps: - id: skip_check uses: fkirc/skip-duplicate-actions@v5 test: needs: pre_job if: needs.pre_job.outputs.should_skip != 'true' name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ github.event_name }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: version: - 'min' - 'lts' - '1' - 'pre' os: - ubuntu-latest - macOS-latest - windows-latest steps: - uses: actions/checkout@v6 - uses: julia-actions/setup-julia@v3 with: version: ${{ matrix.version }} - uses: julia-actions/cache@v3 - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-runtest@v1 - uses: julia-actions/julia-processcoverage@v1 - uses: codecov/codecov-action@v6 with: token: ${{ secrets.CODECOV_TOKEN }} files: lcov.info ================================================ FILE: .github/workflows/docs.yml ================================================ name: Documentation on: pull_request: paths-ignore: - 'LICENSE' - 'README.md' - '.github/workflows/TagBot.yml' push: branches: - 'master' - 'release-' tags: '*' paths-ignore: - 'LICENSE' - 'README.md' - '.github/workflows/TagBot.yml' release: types: [published] jobs: build: runs-on: ${{ matrix.os }} strategy: matrix: julia-version: [1] os: [ubuntu-latest] steps: - uses: actions/checkout@v6 - uses: julia-actions/setup-julia@latest with: version: ${{ matrix.julia-version }} - name: Cache artifacts uses: actions/cache@v5 env: cache-name: cache-artifacts with: path: ~/.julia/artifacts key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }} restore-keys: | ${{ runner.os }}-test-${{ env.cache-name }}- ${{ runner.os }}-test- ${{ runner.os }}- - name: Install dependencies run: julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()' - name: Build and deploy env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} # If authenticating with SSH deploy key run: julia --project=docs/ docs/make.jl ================================================ FILE: .github/workflows/downstream.yml ================================================ name: IntegrationTest on: push: branches: [master] tags: [v*] paths-ignore: - 'LICENSE' - 'README.md' - '.github/workflows/TagBot.yml' pull_request: paths-ignore: - 'LICENSE' - 'README.md' - '.github/workflows/TagBot.yml' concurrency: group: build-${{ github.event.pull_request.number || github.ref }}-${{ github.workflow }} cancel-in-progress: true jobs: pre_job: # continue-on-error: true # Uncomment once integration is finished runs-on: ubuntu-latest # Map a step output to a job output outputs: should_skip: ${{ steps.skip_check.outputs.should_skip }} steps: - id: skip_check uses: fkirc/skip-duplicate-actions@v5 test: needs: pre_job if: needs.pre_job.outputs.should_skip != 'true' name: ${{ matrix.package.group }}/${{ matrix.package.repo }}/${{ matrix.julia-version }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: julia-version: ['1'] os: [ubuntu-latest] package: - {repo: Distributions.jl, group: JuliaStats} - {repo: BlockArrays.jl, group: JuliaArrays} - {repo: LazyArrays.jl, group: JuliaArrays} - {repo: InfiniteArrays.jl, group: JuliaArrays} - {repo: ArrayLayouts.jl, group: JuliaLinearAlgebra} - {repo: LazyBandedMatrices.jl, group: JuliaLinearAlgebra} - {repo: BandedMatrices.jl, group: JuliaLinearAlgebra} - {repo: BlockBandedMatrices.jl, group: JuliaLinearAlgebra} - {repo: InfiniteLinearAlgebra.jl, group: JuliaLinearAlgebra} - {repo: Optim.jl, group: JuliaNLSolvers} steps: - uses: actions/checkout@v6 - uses: julia-actions/setup-julia@v3 with: version: ${{ matrix.julia-version }} arch: x64 - uses: julia-actions/julia-buildpkg@latest - name: Clone Downstream uses: actions/checkout@v6 with: repository: ${{ matrix.package.group }}/${{ matrix.package.repo }} path: downstream - name: Load this and run the downstream tests shell: julia --color=yes --project=downstream {0} run: | using Pkg try # force it to use this PR's version of the package Pkg.develop(PackageSpec(path=".")) # resolver may fail with main deps Pkg.update() Pkg.test(; coverage = true, test_args=["--downstream_integration_test"]) # resolver may fail with test time deps catch err err isa Pkg.Resolve.ResolverError || rethrow() # If we can't resolve that means this is incompatible by SemVer and this is fine # It means we marked this as a breaking change, so we don't need to worry about # Mistakenly introducing a breaking change, as we have intentionally made one @info "Not compatible with this release. No problem." exception=err exit(0) # Exit immediately, as a success end - uses: julia-actions/julia-processcoverage@v1 - uses: codecov/codecov-action@v6 with: token: ${{ secrets.CODECOV_TOKEN }} files: lcov.info ================================================ FILE: .gitignore ================================================ *.jl.cov *.jl.*.cov *.jl.mem deps/deps.jl .DS_Store Manifest.toml Manifest-v*.*.toml docs/build ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2017 Sheehan Olver Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Project.toml ================================================ name = "FillArrays" uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" version = "1.16.0" [deps] LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" [weakdeps] PDMats = "90014a1f-27ba-587c-ab20-58faa44d9150" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" [extensions] FillArraysPDMatsExt = "PDMats" FillArraysSparseArraysExt = "SparseArrays" FillArraysStatisticsExt = "Statistics" FillArraysStaticArraysExt = "StaticArrays" [compat] Aqua = "0.8" Documenter = "1" Infinities = "0.1" LinearAlgebra = "1" PDMats = "0.11.17" Quaternions = "0.7" Random = "1" ReverseDiff = "1" SparseArrays = "1" StaticArrays = "1" Statistics = "1" Test = "1" julia = "1.10" [extras] Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" Infinities = "e1ba4f0e-776d-440f-acd9-e1d2e9742647" PDMats = "90014a1f-27ba-587c-ab20-58faa44d9150" Quaternions = "94ee1d12-ae83-5a48-8b1c-48b8ff168ae0" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] test = ["Aqua", "Test", "Infinities", "PDMats", "ReverseDiff", "SparseArrays", "StaticArrays", "Statistics", "Quaternions", "Documenter", "Random"] ================================================ FILE: README.md ================================================ # FillArrays.jl [![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://JuliaArrays.github.io/FillArrays.jl/stable) [![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://JuliaArrays.github.io/FillArrays.jl/dev) [![Build Status](https://github.com/JuliaArrays/FillArrays.jl/workflows/CI/badge.svg)](https://github.com/JuliaArrays/FillArrays.jl/actions) [![codecov](https://codecov.io/gh/JuliaArrays/FillArrays.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/JuliaArrays/FillArrays.jl) [![deps](https://juliahub.com/docs/FillArrays/deps.svg)](https://juliahub.com/ui/Packages/FillArrays/2Dg1l?t=2) [![version](https://juliahub.com/docs/FillArrays/version.svg)](https://juliahub.com/ui/Packages/FillArrays/2Dg1l) [![pkgeval](https://juliaci.github.io/NanosoldierReports/pkgeval_badges/F/FillArrays.svg)](https://juliaci.github.io/NanosoldierReports/pkgeval_badges/report.html) [![Aqua](https://raw.githubusercontent.com/JuliaTesting/Aqua.jl/master/badge.svg)](https://github.com/JuliaTesting/Aqua.jl) Julia package to lazily represent matrices filled with a single entry, as well as identity matrices. This package exports the following types: `Eye`, `Fill`, `Ones`, `Zeros`, `Trues`, `Falses`, and `OneElement`. The primary purpose of this package is to present a unified way of constructing matrices. For example, to construct a 5-by-5 `BandedMatrix` of all zeros with bandwidths `(1,2)`, one would use ```julia julia> BandedMatrix(Zeros(5,5), (1, 2)) ``` ## Usage Here are the matrix types: ```julia julia> Zeros(5, 6) 5×6 Zeros{Float64} julia> Zeros{Int}(2, 3) 2×3 Zeros{Int64} julia> Zeros(Int, 2, 3) # can also specify the type as an argument 2×3 Zeros{Int64} julia> Ones{Int}(5) 5-element Ones{Int64} julia> Eye{Int}(5) 5×5 Diagonal{Int64,Ones{Int64,1,Tuple{Base.OneTo{Int64}}}}: 1 ⋅ ⋅ ⋅ ⋅ ⋅ 1 ⋅ ⋅ ⋅ ⋅ ⋅ 1 ⋅ ⋅ ⋅ ⋅ ⋅ 1 ⋅ ⋅ ⋅ ⋅ ⋅ 1 julia> Fill(7.0f0, 3, 2) 3×2 Fill{Float32}: entries equal to 7.0 julia> Trues(2, 3) 2×3 Ones{Bool} julia> Falses(2) 2-element Zeros{Bool} julia> OneElement(3.0, (2,1), (5,6)) 5×6 OneElement{Float64, 2, Tuple{Int64, Int64}, Tuple{Base.OneTo{Int64}, Base.OneTo{Int64}}}: ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 3.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ``` They support conversion to other matrix types like `Array`, `SparseVector`, `SparseMatrix`, and `Diagonal`: ```julia julia> Matrix(Zeros(5, 5)) 5×5 Array{Float64,2}: 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 julia> SparseMatrixCSC(Zeros(5, 5)) 5×5 SparseMatrixCSC{Float64,Int64} with 0 stored entries julia> Array(Fill(7, (2,3))) 2×3 Array{Int64,2}: 7 7 7 7 7 7 ``` There is also support for offset index ranges, and the type includes the `axes`: ```julia julia> Ones((-3:2, 1:2)) 6×2 Ones{Float64,2,Tuple{UnitRange{Int64},UnitRange{Int64}}} with indices -3:2×1:2 julia> Fill(7, ((0:2), (-1:0))) 3×2 Fill{Int64,2,Tuple{UnitRange{Int64},UnitRange{Int64}}} with indices 0:2×-1:0: entries equal to 7 julia> typeof(Zeros(5,6)) Zeros{Float64,2,Tuple{Base.OneTo{Int64},Base.OneTo{Int64}}} ``` These types have methods that perform many operations efficiently, including elementary algebra operations like multiplication and addition, as well as linear algebra methods like `norm`, `adjoint`, `transpose` and `vec`. ## Warning! Broadcasting operations and `map`, `mapreduce` are also done efficiently, by evaluating the function being applied only once: ```julia julia> map(sqrt, Fill(4, 2,5)) # one evaluation, not 10, to save time 2×5 Fill{Float64}: entries equal to 2.0 julia> println.(Fill(pi, 10)) π 10-element Fill{Nothing}: entries equal to nothing ``` Notice that this will only match the behaviour of a dense matrix from `fill` if the function is pure. And that this shortcut is taken *before* any other fused broadcast: ```julia julia> map(_ -> rand(), Fill("pi", 2,5)) # not a pure function! 2×5 Fill{Float64}: entries equal to 0.7201617100284206 julia> map(_ -> rand(), fill("4", 2,5)) # 10 evaluations, different answer! 2×5 Matrix{Float64}: 0.43675 0.270809 0.56536 0.0948089 0.24655 0.959363 0.79598 0.238662 0.401909 0.317716 julia> ones(1,5) .+ (_ -> rand()).(Fill("vec", 2)) # Fill broadcast is done first 2×5 Matrix{Float64}: 1.51796 1.51796 1.51796 1.51796 1.51796 1.51796 1.51796 1.51796 1.51796 1.51796 julia> ones(1,5) .+ (_ -> rand()).(fill("vec", 2)) # fused, 10 evaluations 2×5 Matrix{Float64}: 1.51337 1.17578 1.19815 1.43035 1.2987 1.30253 1.21909 1.61755 1.02645 1.77681 ``` ================================================ FILE: appveyor.yml ================================================ environment: matrix: - julia_version: 1 - julia_version: 1.5 - julia_version: nightly platform: - x86 # 32-bit - x64 # 64-bit # # Uncomment the following lines to allow failures on nightly julia # # (tests will run but not make your overall status red) matrix: allow_failures: - julia_version: nightly branches: only: - master - /release-.*/ notifications: - provider: Email on_build_success: false on_build_failure: false on_build_status_changed: false install: - ps: iex ((new-object net.webclient).DownloadString("https://raw.githubusercontent.com/JuliaCI/Appveyor.jl/version-1/bin/install.ps1")) build_script: - echo "%JL_BUILD_SCRIPT%" - C:\julia\bin\julia -e "%JL_BUILD_SCRIPT%" test_script: - echo "%JL_TEST_SCRIPT%" - C:\julia\bin\julia -e "%JL_TEST_SCRIPT%" # # Uncomment to support code coverage upload. Should only be enabled for packages # # which would have coverage gaps without running on Windows # on_success: # - echo "%JL_CODECOV_SCRIPT%" # - C:\julia\bin\julia -e "%JL_CODECOV_SCRIPT%" ================================================ FILE: benchmark/benchmarks.jl ================================================ using BenchmarkTools using FillArrays using LinearAlgebra: triu, tril # Subtract the overhead from benchmark times BenchmarkTools.DEFAULT_PARAMETERS.overhead = BenchmarkTools.estimate_overhead() const SUITE = BenchmarkGroup() ### ### Eye ### g = addgroup!(SUITE, "Eye", []) eye1float = Eye{Float64}(1) eye10float = Eye{Float64}(10) eye1000float = Eye{Float64}(1000) eye10int = Eye{Int}(10) eye1000int = Eye{Int}(1000) r1 = addgroup!(g, "reduction", []) r2 = addgroup!(g, "properties", ["properties"]) r3 = addgroup!(g, "any/all", []) r4 = addgroup!(g, "iterate", []) r5 = addgroup!(g, "identities", []) dimstring(a) = string("n=", size(a, 1)) fulldimstring(a) = string("size=", size(a)) funop(fun, op) = string(fun,"(", op, ", a)") for a in (eye10float, eye1000float, eye10int, eye1000int) for fun in (sum,) r1[string(fun), string(eltype(a)), dimstring(a)] = @benchmarkable $fun($a) end for fun in (isone, iszero) r2[string(fun), string(eltype(a)), dimstring(a)] = @benchmarkable $fun($a) end for (fun, op) in ((any, isone), (all, isone)) r3[funop(fun, op), string(eltype(a)), dimstring(a)] = @benchmarkable $fun($op, $a) end for fun in (collect,) r4[string(fun), string(eltype(a)), dimstring(a)] = @benchmarkable $fun($a) end for fun in (permutedims, triu, tril, inv) r5[string(fun), string(eltype(a)), dimstring(a)] = @benchmarkable $fun($a) end end for a in (eye1float,) for (fun, op) in ((any, isone), (all, isone)) r3[funop(fun, op), string(eltype(a)), dimstring(a)] = @benchmarkable $fun($op, $a) end end ### ### Zeros ### g1 = addgroup!(SUITE, "Zeros", []) zeros10 = Zeros(10) zeros10x10 = Zeros(10, 10) z1 = addgroup!(g1, "properties", ["properties"]) for a in (zeros10, zeros10x10) for fun in (iszero, ) z1[string(fun), string(eltype(a)), fulldimstring(a)] = @benchmarkable $fun($a) end end # If a cache of tuned parameters already exists, use it, otherwise, tune and cache # the benchmark parameters. Reusing cached parameters is faster and more reliable # than re-tuning `SUITE` every time the file is included. paramspath = joinpath(dirname(@__FILE__), "params.json") if isfile(paramspath) loadparams!(SUITE, BenchmarkTools.load(paramspath)[1], :evals); else tune!(SUITE) BenchmarkTools.save(paramspath, params(SUITE)); end result = run(SUITE, verbose = true) ================================================ FILE: docs/Project.toml ================================================ [deps] Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" [compat] Documenter = "1" Random = "1" SparseArrays = "1" StaticArrays = "1" ================================================ FILE: docs/make.jl ================================================ using Documenter using FillArrays # Setup for doctests in docstrings DocMeta.setdocmeta!(FillArrays, :DocTestSetup, :(using FillArrays)) makedocs(; format = Documenter.HTML( canonical = "https://JuliaArrays.github.io/FillArrays.jl/stable/", ), pages = [ "Home" => "index.md", ], sitename = "FillArrays.jl", ) deploydocs(; repo = "github.com/JuliaArrays/FillArrays.jl") ================================================ FILE: docs/src/index.md ================================================ ```@meta DocTestSetup = quote using FillArrays end ``` # Introduction `FillArrays` allows one to lazily represent arrays filled with a single entry, as well as identity matrices. This package exports the following types: `Eye`, `Fill`, `Ones`, `Zeros`, `Trues` and `Falses`. Among these, the [`FillArrays.AbstractFill`](@ref) types represent lazy versions of dense arrays where all elements have the same value. `Eye`, on the other hand, represents a `Diagonal` matrix with ones along the principal diagonal. All these types accept sizes or axes as arguments, so one may create arrays of arbitrary sizes and dimensions. A rectangular `Eye` matrix may be constructed analogously, by passing the size of the matrix to `Eye`. ## Quick Start Create a 2x2 zero matrix ```jldoctest julia> z = Zeros(2,2) 2×2 Zeros{Float64} julia> Array(z) 2×2 Matrix{Float64}: 0.0 0.0 0.0 0.0 ``` We may specify the element type as ```jldoctest julia> z = Zeros{Int}(2,2) 2×2 Zeros{Int64} julia> Array(z) 2×2 Matrix{Int64}: 0 0 0 0 ``` We may create arrays with any number of dimensions. A `Vector` of ones may be created as ```jldoctest julia> a = Ones(4) 4-element Ones{Float64} julia> Array(a) 4-element Vector{Float64}: 1.0 1.0 1.0 1.0 ``` Similarly, a `2x3x2` array, where every element is equal to `10`, may be created as ```jldoctest julia> f = Fill(10, 2,3,2) 2×3×2 Fill{Int64}, with entries equal to 10 julia> Array(f) 2×3×2 Array{Int64, 3}: [:, :, 1] = 10 10 10 10 10 10 [:, :, 2] = 10 10 10 10 10 10 ``` The elements of a `Fill` array don't need to be restricted to numbers, and these may be any Julia object. For example, we may construct an array of strings using ```jldoctest julia> f = Fill("hello", 2,5) 2×5 Fill{String}, with entries equal to "hello" julia> Array(f) 2×5 Matrix{String}: "hello" "hello" "hello" "hello" "hello" "hello" "hello" "hello" "hello" "hello" ``` ### Conversion to a sparse form These `Fill` array types may be converted to sparse arrays as well, which might be useful in certain cases ```jldoctest sparse julia> using SparseArrays julia> z = Zeros{Int}(2,2) 2×2 Zeros{Int64} julia> sparse(z) 2×2 SparseMatrixCSC{Int64, Int64} with 0 stored entries: ⋅ ⋅ ⋅ ⋅ ``` Note, however, that most `Fill` arrays are not sparse, despite being lazily evaluated. These types have methods that perform many operations efficiently, including elementary algebra operations like multiplication and addition, as well as linear algebra methods like `norm`, `adjoint`, `transpose` and `vec`. ### Custom axes The various `Fill` equivalents all support offset or custom axes, where instead of the size, one may pass a `Tuple` of axes. So, for example, one may use a `SOneTo` axis from [`StaticArrays.jl`](https://github.com/JuliaArrays/StaticArrays.jl) to construct a statically sized `Fill`. ```jldoctest julia> using StaticArrays julia> f = Fill(2, (SOneTo(4), SOneTo(5))) 4×5 Fill{Int64, 2, Tuple{SOneTo{4}, SOneTo{5}}} with indices SOneTo(4)×SOneTo(5), with entries equal to 2 ``` The size of such an array would be known at compile time, permitting compiler optimizations. We may construct infinite fill arrays by passing infinite-sized axes, see [`InfiniteArrays.jl`](https://github.com/JuliaArrays/InfiniteArrays.jl). ### Other lazy types A lazy representation of an identity matrix may be constructured using `Eye`. For example, a `4x4` identity matrix with `Float32` elements may be constructed as ```jldoctest sparse julia> id = Eye{Float32}(4) 4×4 Eye{Float32} julia> Array(id) 4×4 Matrix{Float32}: 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 julia> sparse(id) 4×4 SparseMatrixCSC{Float32, Int64} with 4 stored entries: 1.0 ⋅ ⋅ ⋅ ⋅ 1.0 ⋅ ⋅ ⋅ ⋅ 1.0 ⋅ ⋅ ⋅ ⋅ 1.0 julia> idrect = Eye(2,5) # rectangular matrix 2×5 Eye{Float64} julia> sparse(idrect) 2×5 SparseMatrixCSC{Float64, Int64} with 2 stored entries: 1.0 ⋅ ⋅ ⋅ ⋅ ⋅ 1.0 ⋅ ⋅ ⋅ ``` Note that an `Eye` actually returns a `Diagonal` matrix, where the diagonal is a `Ones` vector. ## Warning about map and broadcasting Broadcasting operations, and `map` and `mapreduce`, are also done efficiently, by evaluating the function being applied only once: ```jldoctest julia> map(sqrt, Fill(4, 2,5)) # one evaluation, not 10, to save time 2×5 Fill{Float64}, with entries equal to 2.0 julia> println.(Fill(pi, 10)) π 10-element Fill{Nothing}, with entries equal to nothing ``` Notice that this will only match the behaviour of a dense matrix from `fill` if the function is pure. And that this shortcut is taken before any other fused broadcast: ```jldoctest; setup=:(using Random; Random.seed!(1234)) julia> map(_ -> rand(), Fill("pi", 2,5)) # not a pure function! 2×5 Fill{Float64}, with entries equal to 0.32597672886359486 julia> map(_ -> rand(), fill("4", 2,5)) # 10 evaluations, different answer! 2×5 Matrix{Float64}: 0.549051 0.894245 0.394255 0.795547 0.748415 0.218587 0.353112 0.953125 0.49425 0.578232 julia> ones(1,5) .+ (_ -> rand()).(Fill("vec", 2)) # Fill broadcast is done first 2×5 Matrix{Float64}: 1.72794 1.72794 1.72794 1.72794 1.72794 1.72794 1.72794 1.72794 1.72794 1.72794 julia> ones(1,5) .+ (_ -> rand()).(fill("vec", 2)) # fused, 10 evaluations 2×5 Matrix{Float64}: 1.00745 1.43924 1.95674 1.99667 1.11008 1.19938 1.68253 1.64786 1.74919 1.49138 ``` # API ```@autodocs Modules = [FillArrays] ``` ================================================ FILE: ext/FillArraysPDMatsExt.jl ================================================ module FillArraysPDMatsExt import FillArrays import FillArrays.LinearAlgebra import PDMats using FillArrays: mult_zeros, AbstractZeros using PDMats: ScalMat function PDMats.AbstractPDMat(a::LinearAlgebra.Diagonal{T,<:FillArrays.AbstractFill{T,1}}) where {T<:Real} dim = size(a, 1) return ScalMat(dim, FillArrays.getindex_value(a.diag)) end Base.:*(a::ScalMat, b::AbstractZeros{T, 1} where T) = mult_zeros(a, b) Base.:*(a::ScalMat, b::AbstractZeros{T, 2} where T) = mult_zeros(a, b) Base.:*(a::AbstractZeros{T, 2} where T, b::ScalMat) = mult_zeros(a, b) # This is implemented in case ScalMat implements right multiplication end # module ================================================ FILE: ext/FillArraysSparseArraysExt.jl ================================================ module FillArraysSparseArraysExt using SparseArrays using SparseArrays: SparseVectorUnion import Base: convert, kron using FillArrays using FillArrays: RectDiagonalFill, RectOrDiagonalFill, ZerosVector, ZerosMatrix, getindex_value, AbstractFillVector, _fill_dot # Specifying the full namespace is necessary because of https://github.com/JuliaLang/julia/issues/48533 # See https://github.com/JuliaStats/LogExpFunctions.jl/pull/63 using FillArrays.LinearAlgebra import LinearAlgebra: dot, kron, I ################## ## Sparse arrays ################## SparseVector{T}(Z::ZerosVector) where T = spzeros(T, length(Z)) SparseVector{Tv,Ti}(Z::ZerosVector) where {Tv,Ti} = spzeros(Tv, Ti, length(Z)) convert(::Type{AbstractSparseVector}, Z::ZerosVector{T}) where T = spzeros(T, length(Z)) convert(::Type{AbstractSparseVector{T}}, Z::ZerosVector) where T= spzeros(T, length(Z)) SparseMatrixCSC{T}(Z::ZerosMatrix) where T = spzeros(T, size(Z)...) SparseMatrixCSC{Tv,Ti}(Z::Zeros{T,2,Axes}) where {Tv,Ti<:Integer,T,Axes} = spzeros(Tv, Ti, size(Z)...) convert(::Type{AbstractSparseMatrix}, Z::ZerosMatrix{T}) where T = spzeros(T, size(Z)...) convert(::Type{AbstractSparseMatrix{T}}, Z::ZerosMatrix) where T = spzeros(T, size(Z)...) convert(::Type{AbstractSparseArray}, Z::Zeros{T}) where T = spzeros(T, size(Z)...) convert(::Type{AbstractSparseArray{Tv}}, Z::Zeros{T}) where {T,Tv} = spzeros(Tv, size(Z)...) convert(::Type{AbstractSparseArray{Tv,Ti}}, Z::Zeros{T}) where {T,Tv,Ti} = spzeros(Tv, Ti, size(Z)...) convert(::Type{AbstractSparseArray{Tv,Ti,N}}, Z::Zeros{T,N}) where {T,Tv,Ti,N} = spzeros(Tv, Ti, size(Z)...) SparseMatrixCSC{Tv}(Z::Eye{T}) where {T,Tv} = SparseMatrixCSC{Tv}(I, size(Z)...) # works around missing `speye`: SparseMatrixCSC{Tv,Ti}(Z::Eye{T}) where {T,Tv,Ti<:Integer} = convert(SparseMatrixCSC{Tv,Ti}, SparseMatrixCSC{Tv}(I, size(Z)...)) convert(::Type{AbstractSparseMatrix}, Z::Eye{T}) where {T} = SparseMatrixCSC{T}(I, size(Z)...) convert(::Type{AbstractSparseMatrix{Tv}}, Z::Eye{T}) where {T,Tv} = SparseMatrixCSC{Tv}(I, size(Z)...) convert(::Type{AbstractSparseArray}, Z::Eye{T}) where T = SparseMatrixCSC{T}(I, size(Z)...) convert(::Type{AbstractSparseArray{Tv}}, Z::Eye{T}) where {T,Tv} = SparseMatrixCSC{Tv}(I, size(Z)...) convert(::Type{AbstractSparseArray{Tv,Ti}}, Z::Eye{T}) where {T,Tv,Ti} = convert(SparseMatrixCSC{Tv,Ti}, Z) convert(::Type{AbstractSparseArray{Tv,Ti,2}}, Z::Eye{T}) where {T,Tv,Ti} = convert(SparseMatrixCSC{Tv,Ti}, Z) function SparseMatrixCSC{Tv}(R::RectOrDiagonalFill) where {Tv} SparseMatrixCSC{Tv,eltype(axes(R,1))}(R) end function SparseMatrixCSC{Tv,Ti}(R::RectOrDiagonalFill) where {Tv,Ti} Base.require_one_based_indexing(R) v = parent(R) J = getindex_value(v)*I SparseMatrixCSC{Tv,Ti}(J, size(R)) end # TODO: remove in v2.0 @deprecate kron(E1::RectDiagonalFill, E2::RectDiagonalFill) kron(sparse(E1), sparse(E2)) # Ambiguity. see #178 dot(x::AbstractFillVector, y::SparseVectorUnion) = _fill_dot(x, y) end # module ================================================ FILE: ext/FillArraysStaticArraysExt.jl ================================================ module FillArraysStaticArraysExt using FillArrays using StaticArrays import Base: promote_op import FillArrays: elconvert # Disambiguity methods for StaticArrays function Base.:+(a::FillArrays.Zeros, b::StaticArray) promote_shape(a,b) return elconvert(promote_op(+,eltype(a),eltype(b)),b) end function Base.:+(a::StaticArray, b::FillArrays.Zeros) promote_shape(a,b) return elconvert(promote_op(+,eltype(a),eltype(b)),a) end function Base.:-(a::StaticArray, b::FillArrays.Zeros) promote_shape(a,b) return elconvert(promote_op(-,eltype(a),eltype(b)),a) end function Base.:-(a::FillArrays.Zeros, b::StaticArray) promote_shape(a,b) return elconvert(promote_op(-,eltype(a),eltype(b)),-b) end end # module ================================================ FILE: ext/FillArraysStatisticsExt.jl ================================================ module FillArraysStatisticsExt import Statistics: mean, var, cov, cor using LinearAlgebra: diagind using FillArrays using FillArrays: AbstractFill, AbstractFillVector, AbstractFillMatrix, getindex_value mean(A::AbstractFill; dims=(:)) = mean(identity, A; dims=dims) function mean(f::Union{Function, Type}, A::AbstractFill; dims=(:)) val = float(f(getindex_value(A))) dims isa Colon ? val : Fill(val, ntuple(d -> d in dims ? 1 : size(A,d), ndims(A))...) end function var(A::AbstractFill{T}; corrected::Bool=true, mean=nothing, dims=(:)) where {T<:Number} dims isa Colon ? zero(float(T)) : Zeros{float(T)}(ntuple(d -> d in dims ? 1 : size(A,d), ndims(A))...) end cov(::AbstractFillVector{T}; corrected::Bool=true) where {T<:Number} = zero(float(T)) cov(A::AbstractFillMatrix{T}; corrected::Bool=true, dims::Integer=1) where {T<:Number} = Zeros{float(T)}(size(A, 3-dims), size(A, 3-dims)) cor(::AbstractFillVector{T}) where {T<:Number} = one(float(T)) function cor(A::AbstractFillMatrix{T}; dims::Integer=1) where {T<:Number} out = fill(float(T)(NaN), size(A, 3-dims), size(A, 3-dims)) out[diagind(out)] .= 1 out end end # module ================================================ FILE: src/FillArrays.jl ================================================ """ `FillArrays` module to lazily represent matrices with a single value """ module FillArrays using LinearAlgebra import Base: size, getindex, setindex!, IndexStyle, checkbounds, convert, +, -, *, /, \, diff, sum, cumsum, maximum, minimum, sort, sort!, any, all, axes, isone, iszero, iterate, unique, allunique, permutedims, inv, copy, vec, setindex!, count, ==, reshape, map, zero, show, view, in, mapreduce, one, reverse, promote_op, promote_rule, repeat, parent, similar, issorted, add_sum, accumulate, OneTo, permutedims import LinearAlgebra: rank, svdvals!, tril, triu, tril!, triu!, diag, transpose, adjoint, fill!, dot, norm2, norm1, normInf, normMinusInf, normp, lmul!, rmul!, diagzero, AdjointAbsVec, TransposeAbsVec, issymmetric, ishermitian, AdjOrTransAbsVec, checksquare, mul!, kron, AbstractTriangular import Base.Broadcast: broadcasted, DefaultArrayStyle, broadcast_shape, BroadcastStyle, Broadcasted export Zeros, Ones, Fill, Eye, Trues, Falses, OneElement import Base: oneto """ AbstractFill{T, N, Axes} <: AbstractArray{T, N} Supertype for lazy array types whose entries are all equal. Subtypes of `AbstractFill` should implement [`FillArrays.getindex_value`](@ref) to return the value of the entries. """ abstract type AbstractFill{T, N, Axes} <: AbstractArray{T, N} end const AbstractFillVector{T} = AbstractFill{T,1} const AbstractFillMatrix{T} = AbstractFill{T,2} const AbstractFillVecOrMat{T} = Union{AbstractFillVector{T},AbstractFillMatrix{T}} ==(a::AbstractFill, b::AbstractFill) = axes(a) == axes(b) && getindex_value(a) == getindex_value(b) @inline function Base.isassigned(F::AbstractFill, i::Integer...) @boundscheck checkbounds(Bool, F, to_indices(F, i)...) || return false return true end @inline function _fill_getindex(F::AbstractFill, kj::Integer...) @boundscheck checkbounds(F, kj...) getindex_value(F) end Base.@propagate_inbounds getindex(F::AbstractFill, k::Integer) = _fill_getindex(F, k) Base.@propagate_inbounds getindex(F::AbstractFill{T, N}, kj::Vararg{Integer, N}) where {T, N} = _fill_getindex(F, kj...) @inline function setindex!(F::AbstractFill, v, k::Integer) @boundscheck checkbounds(F, k) v == getindex_value(F) || throw(ArgumentError(LazyString("Cannot setindex! to ", v, " for an AbstractFill with value ", getindex_value(F), "."))) F end @inline function setindex!(F::AbstractFill{T, N}, v, kj::Vararg{Integer, N}) where {T, N} @boundscheck checkbounds(F, kj...) v == getindex_value(F) || throw(ArgumentError(LazyString("Cannot setindex! to ", v, " for an AbstractFill with value ", getindex_value(F), "."))) F end @inline function fill!(F::AbstractFill, v) v == getindex_value(F) || throw(ArgumentError(LazyString("Cannot fill! with ", v, " an AbstractFill with value ", getindex_value(F), "."))) F end rank(F::AbstractFill) = iszero(getindex_value(F)) ? 0 : 1 IndexStyle(::Type{<:AbstractFill{<:Any,N,<:NTuple{N,Base.OneTo{Int}}}}) where N = IndexLinear() issymmetric(F::AbstractFillMatrix) = axes(F,1) == axes(F,2) && (isempty(F) || issymmetric(getindex_value(F))) ishermitian(F::AbstractFillMatrix) = axes(F,1) == axes(F,2) && (isempty(F) || ishermitian(getindex_value(F))) Base.IteratorSize(::Type{<:AbstractFill{T,N,Axes}}) where {T,N,Axes} = _IteratorSize(Axes) _IteratorSize(::Type{Tuple{}}) = Base.HasShape{0}() _IteratorSize(::Type{Tuple{T}}) where {T} = Base.IteratorSize(T) function _IteratorSize(::Type{T}) where {T<:Tuple} N = fieldcount(T) s = ntuple(i-> Base.IteratorSize(fieldtype(T, i)), N) any(x -> x isa Base.IsInfinite, s) ? Base.IsInfinite() : Base.HasShape{N}() end """ Fill{T, N, Axes} where {T,N,Axes<:Tuple{Vararg{AbstractUnitRange,N}}} A lazy representation of an array of dimension `N` whose entries are all equal to a constant of type `T`, with axes of type `Axes`. Typically created by `Fill` or `Zeros` or `Ones` # Examples ```jldoctest julia> Fill(7, (2,3)) 2×3 Fill{Int64}, with entries equal to 7 julia> Fill{Float64, 1, Tuple{UnitRange{Int64}}}(7.0, (1:2,)) 2-element Fill{Float64, 1, Tuple{UnitRange{Int64}}} with indices 1:2, with entries equal to 7.0 ``` """ struct Fill{T, N, Axes} <: AbstractFill{T, N, Axes} value::T axes::Axes Fill{T,N,Axes}(x::T, sz::Axes) where Axes<:Tuple{Vararg{AbstractUnitRange,N}} where {T, N} = new{T,N,Axes}(x,sz) Fill{T,0,Tuple{}}(x::T, sz::Tuple{}) where T = new{T,0,Tuple{}}(x,sz) end const FillVector{T} = Fill{T,1} const FillMatrix{T} = Fill{T,2} const FillVecOrMat{T} = Union{FillVector{T},FillMatrix{T}} Fill{T,N,Axes}(x, sz::Axes) where Axes<:Tuple{Vararg{AbstractUnitRange,N}} where {T, N} = Fill{T,N,Axes}(convert(T, x)::T, sz) Fill{T,0}(x, ::Tuple{}) where T = Fill{T,0,Tuple{}}(convert(T, x)::T, ()) # ambiguity fix @inline Fill{T, N}(x, sz::Axes) where Axes<:Tuple{Vararg{AbstractUnitRange,N}} where {T, N} = Fill{T,N,Axes}(convert(T, x)::T, sz) @inline Fill{T, N}(x, sz::SZ) where SZ<:Tuple{Vararg{Integer,N}} where {T, N} = Fill{T,N}(x, oneto.(sz)) @inline Fill{T, N}(x, sz::Vararg{Integer, N}) where {T, N} = Fill{T,N}(convert(T, x)::T, sz) @inline Fill{T}(x, sz::Vararg{Integer,N}) where {T, N} = Fill{T, N}(x, sz) @inline Fill{T}(x, sz::Tuple{Vararg{Any,N}}) where {T, N} = Fill{T, N}(x, sz) """ `Fill(x, dims...)` construct lazy version of `fill(x, dims...)` """ @inline Fill(x::T, sz::Vararg{Integer,N}) where {T, N} = Fill{T, N}(x, sz) """ `Fill(x, dims)` construct lazy version of `fill(x, dims)` """ @inline Fill(x::T, sz::Tuple{Vararg{Any,N}}) where {T, N} = Fill{T, N}(x, sz) # We restrict to when T is specified to avoid ambiguity with a Fill of a Fill @inline Fill{T}(F::Fill{T}) where T = F @inline Fill{T,N}(F::Fill{T,N}) where {T,N} = F @inline Fill{T,N,Axes}(F::Fill{T,N,Axes}) where {T,N,Axes} = F @inline axes(F::Fill) = F.axes @inline size(F::Fill) = map(length, F.axes) """ FillArrays.getindex_value(F::AbstractFill) Return the value that `F` is filled with. # Examples ```jldoctest julia> f = Ones(3); julia> FillArrays.getindex_value(f) 1.0 julia> g = Fill(2, 10); julia> FillArrays.getindex_value(g) 2 ``` """ getindex_value @inline getindex_value(F::Fill) = F.value AbstractArray{T,N}(F::Fill{V,N}) where {T,V,N} = Fill{T}(convert(T, F.value)::T, F.axes) AbstractFill{T}(F::AbstractFill) where T = AbstractArray{T}(F) AbstractFill{T,N}(F::AbstractFill) where {T,N} = AbstractArray{T,N}(F) AbstractFill{T,N,Ax}(F::AbstractFill{<:Any,N,Ax}) where {T,N,Ax} = AbstractArray{T,N}(F) convert(::Type{AbstractFill{T}}, F::AbstractFill) where T = convert(AbstractArray{T}, F) convert(::Type{AbstractFill{T,N}}, F::AbstractFill) where {T,N} = convert(AbstractArray{T,N}, F) convert(::Type{AbstractFill{T,N,Ax}}, F::AbstractFill{<:Any,N,Ax}) where {T,N,Ax} = convert(AbstractArray{T,N}, F) copy(F::Fill) = Fill(F.value, F.axes) """ unique_value(arr::AbstractArray) Return `only(unique(arr))` without intermediate allocations. Throws an error if `arr` does not contain one and only one unique value. """ function unique_value(arr::AbstractArray) if isempty(arr) error("Cannot convert empty array to Fill") end val = first(arr) for x in arr if x !== val error(LazyString("Input array contains both ", x, " and ", val, ". Cannot convert to Fill")) end end return val end unique_value(f::AbstractFill) = getindex_value(f) convert(::Type{Fill}, arr::AbstractArray{T}) where T = Fill{T}(unique_value(arr), axes(arr)) convert(::Type{Fill{T}}, arr::AbstractArray) where T = Fill{T}(unique_value(arr), axes(arr)) convert(::Type{Fill{T,N}}, arr::AbstractArray{<:Any,N}) where {T,N} = Fill{T,N}(unique_value(arr), axes(arr)) convert(::Type{Fill{T,N,Axes}}, arr::AbstractArray{<:Any,N}) where {T,N,Axes} = Fill{T,N,Axes}(unique_value(arr), axes(arr)) # ambiguity fix convert(::Type{Fill}, arr::Fill{T}) where T = Fill{T}(unique_value(arr), axes(arr)) convert(::Type{T}, F::T) where T<:Fill = F getindex(F::Fill{<:Any,0}) = getindex_value(F) Base.@propagate_inbounds @inline function _fill_getindex(A::AbstractFill, I::Vararg{Union{Real, AbstractArray}, N}) where N @boundscheck checkbounds(A, I...) shape = Base.index_shape(I...) fillsimilar(A, shape) end Base.@propagate_inbounds @inline function _fill_getindex(A::AbstractFill, kr::AbstractArray{Bool}) @boundscheck checkbounds(A, kr) fillsimilar(A, count(kr)) end Base.@propagate_inbounds @inline Base._unsafe_getindex(::IndexStyle, F::AbstractFill, I::Vararg{Union{Real, AbstractArray}}) = _fill_getindex(F, I...) Base.@propagate_inbounds getindex(A::AbstractFill, kr::AbstractVector{Bool}) = _fill_getindex(A, kr) Base.@propagate_inbounds getindex(A::AbstractFill, kr::AbstractArray{Bool}) = _fill_getindex(A, kr) @inline Base.iterate(F::AbstractFill) = length(F) == 0 ? nothing : (v = getindex_value(F); (v, (v, 1))) @inline function Base.iterate(F::AbstractFill, (v, n)) 1 <= n < length(F) || return nothing v, (v, n+1) end # Iterators Iterators.rest(F::AbstractFill, (_,n)) = fillsimilar(F, n <= 0 ? 0 : max(length(F)-n, 0)) function Iterators.drop(F::AbstractFill, n::Integer) n >= 0 || throw(ArgumentError("drop length must be nonnegative")) fillsimilar(F, max(length(F)-n, 0)) end function Iterators.take(F::AbstractFill, n::Integer) n >= 0 || throw(ArgumentError("take length must be nonnegative")) fillsimilar(F, min(n, length(F))) end Base.rest(F::AbstractFill, s) = Iterators.rest(F, s) ################# # Sorting ################# function issorted(f::AbstractFill; kw...) v = getindex_value(f) issorted((v, v); kw...) end function sort(a::AbstractFill; kwds...) issorted(a; kwds...) # ensure that the values may be compared return a end function sort!(a::AbstractFill; kwds...) issorted(a; kwds...) # ensure that the values may be compared return a end svdvals!(a::AbstractFillMatrix) = [getindex_value(a)*sqrt(prod(size(a))); Zeros(min(size(a)...)-1)] function fill_reshape(parent, dims::Integer...) n = length(parent) prod(dims) == n || throw(DimensionMismatch(LazyString("parent has ", n, " elements, which is incompatible with size ", dims))) fillsimilar(parent, dims...) end if VERSION < v"1.12.0-DEV.1726" # resolve ambiguity with Base reshape(parent::AbstractFillVector, ::Colon) = parent reshape(parent::AbstractFill, dims::Integer...) = reshape(parent, dims) reshape(parent::AbstractFill, dims::Union{Int,Colon}...) = reshape(parent, dims) reshape(parent::AbstractFill, dims::Union{Integer,Colon}...) = reshape(parent, dims) reshape(parent::AbstractFill, dims::Tuple{Vararg{Union{Integer,Colon}}}) = fill_reshape(parent, Base._reshape_uncolon(parent, dims)...) reshape(parent::AbstractFill, dims::Tuple{Vararg{Union{Int,Colon}}}) = fill_reshape(parent, Base._reshape_uncolon(parent, dims)...) reshape(parent::AbstractFill, shp::Tuple{Union{Integer,Base.OneTo}, Vararg{Union{Integer,Base.OneTo}}}) = reshape(parent, Base.to_shape(shp)) # resolve ambiguity with Base reshape(parent::AbstractFillVector, ::Tuple{Colon}) = parent end reshape(parent::AbstractFill, dims::Dims) = fill_reshape(parent, dims...) reshape(parent::AbstractFill, dims::Tuple{Integer, Vararg{Integer}}) = fill_reshape(parent, dims...) for (AbsTyp, Typ, funcs, func) in ((:AbstractZeros, :Zeros, :zeros, :zero), (:AbstractOnes, :Ones, :ones, :one)) @eval begin abstract type $AbsTyp{T, N, Axes} <: AbstractFill{T, N, Axes} end $(Symbol(AbsTyp,"Vector")){T} = $AbsTyp{T,1} $(Symbol(AbsTyp,"Matrix")){T} = $AbsTyp{T,2} $(Symbol(AbsTyp,"VecOrMat")){T} = Union{$(Symbol(AbsTyp,"Vector")){T},$(Symbol(AbsTyp,"Matrix"))} """ `$($Typ){T, N, Axes} <: AbstractFill{T, N, Axes}` (lazy `$($funcs)` with axes)""" struct $Typ{T, N, Axes} <: $AbsTyp{T, N, Axes} axes::Axes @inline $Typ{T,N,Axes}(sz::Axes) where Axes<:Tuple{Vararg{AbstractUnitRange,N}} where {T, N} = new{T,N,Axes}(sz) @inline $Typ{T,N}(sz::Axes) where Axes<:Tuple{Vararg{AbstractUnitRange,N}} where {T, N} = new{T,N,Axes}(sz) @inline $Typ{T,0,Tuple{}}(sz::Tuple{}) where T = new{T,0,Tuple{}}(sz) end const $(Symbol(Typ,"Vector")){T} = $Typ{T,1} const $(Symbol(Typ,"Matrix")){T} = $Typ{T,2} const $(Symbol(Typ,"VecOrMat")){T} = Union{$Typ{T,1},$Typ{T,2}} @inline $Typ{T, 0}(sz::Tuple{}) where {T} = $Typ{T,0,Tuple{}}(sz) @inline $Typ{T, N}(sz::Tuple{Vararg{Integer, N}}) where {T, N} = $Typ{T,N}(oneto.(sz)) @inline $Typ{T, N}(sz::Vararg{Integer, N}) where {T, N} = $Typ{T,N}(sz) """ `$($Typ){T}(dims...)` construct lazy version of `$($funcs)(dims...)`""" @inline $Typ{T}(sz::Vararg{Integer,N}) where {T, N} = $Typ{T, N}(sz) @inline $Typ{T}(sz::SZ) where SZ<:Tuple{Vararg{Any,N}} where {T, N} = $Typ{T, N}(sz) @inline $Typ(sz::Vararg{Any,N}) where N = $Typ{Float64,N}(sz) @inline $Typ(sz::SZ) where SZ<:Tuple{Vararg{Any,N}} where N = $Typ{Float64,N}(sz) @inline $Typ{T}(n::Integer) where T = $Typ{T,1}(n) @inline $Typ(n::Integer) = $Typ{Float64,1}(n) @inline $Typ{T,N,Axes}(A::AbstractArray{V,N}) where{T,V,N,Axes} = $Typ{T,N,Axes}(axes(A)) @inline $Typ{T,N}(A::AbstractArray{V,N}) where{T,V,N} = $Typ{T,N}(size(A)) @inline $Typ{T}(A::AbstractArray) where{T} = $Typ{T}(size(A)) @inline $Typ(A::AbstractArray) = $Typ{eltype(A)}(A) @inline $Typ(::Type{T}, m...) where T = $Typ{T}(m...) @inline axes(Z::$Typ) = Z.axes @inline size(Z::$AbsTyp) = length.(axes(Z)) @inline getindex_value(Z::$AbsTyp{T}) where T = $func(T) AbstractArray{T}(F::$AbsTyp) where T = $Typ{T}(axes(F)) AbstractArray{T,N}(F::$AbsTyp{V,N}) where {T,V,N} = $Typ{T}(axes(F)) copy(F::$AbsTyp) = F getindex(F::$AbsTyp{T,0}) where T = getindex_value(F) promote_rule(::Type{$Typ{T, N, Axes}}, ::Type{$Typ{V, N, Axes}}) where {T,V,N,Axes} = $Typ{promote_type(T,V),N,Axes} function convert(::Type{Typ}, A::$AbsTyp{V,N,Axes}) where {T,V,N,Axes,Typ<:$AbsTyp{T,N,Axes}} convert(T, getindex_value(A)) # checks that the types are convertible Typ(axes(A)) end convert(::Type{$Typ{T,N}}, A::$AbsTyp{V,N,Axes}) where {T,V,N,Axes} = convert($Typ{T,N,Axes}, A) convert(::Type{$Typ{T}}, A::$AbsTyp{V,N,Axes}) where {T,V,N,Axes} = convert($Typ{T,N,Axes}, A) function convert(::Type{Typ}, A::AbstractFill{V,N}) where {T,V,N,Axes,Typ<:$AbsTyp{T,N,Axes}} axes(A) isa Axes || throw(ArgumentError(LazyString("cannot convert, as axes of array are not ", Axes))) val = getindex_value(A) y = convert(T, val) y == $func(T) || throw(ArgumentError(LazyString("cannot convert an array containinig ", val, " to ", Typ))) Typ(axes(A)) end function convert(::Type{$Typ{T,N}}, A::AbstractFill{<:Any,N}) where {T,N} convert($Typ{T,N,typeof(axes(A))}, A) end function convert(::Type{$Typ{T}}, A::AbstractFill{<:Any,N}) where {T,N} convert($Typ{T,N}, A) end function convert(::Type{$Typ}, A::AbstractFill{V,N}) where {V,N} convert($Typ{V,N}, A) end end end # conversions for TYPE in (:Fill, :AbstractFill, :Ones, :Zeros), STYPE in (:AbstractArray, :AbstractFill) @eval begin @inline $STYPE{T}(F::$TYPE{T}) where T = F @inline $STYPE{T,N}(F::$TYPE{T,N}) where {T,N} = F end end promote_rule(::Type{<:AbstractFill{T, N, Axes}}, ::Type{<:AbstractFill{V, N, Axes}}) where {T,V,N,Axes} = Fill{promote_type(T,V),N,Axes} """ fillsimilar(a::AbstractFill, axes...) creates a fill object that has the same fill value as `a` but with the specified axes. For example, if `a isa Zeros` then so is the returned object. """ fillsimilar(a::Ones{T}, axes...) where T = Ones{T}(axes...) fillsimilar(a::Zeros{T}, axes...) where T = Zeros{T}(axes...) fillsimilar(a::AbstractFill, axes...) = Fill(getindex_value(a), axes...) # functions function Base.sqrt(a::AbstractFillMatrix{<:Union{Real, Complex}}) Base.require_one_based_indexing(a) size(a,1) == size(a,2) || throw(DimensionMismatch(LazyString("matrix is not square: dimensions are ", size(a)))) _sqrt(a) end _sqrt(a::AbstractZerosMatrix) = float(a) function _sqrt(a::AbstractFillMatrix) n = size(a,1) n == 0 && return float(a) v = getindex_value(a) Fill(√(v/n), axes(a)) end function Base.cbrt(a::AbstractFillMatrix{<:Real}) Base.require_one_based_indexing(a) size(a,1) == size(a,2) || throw(DimensionMismatch(LazyString("matrix is not square: dimensions are ", size(a)))) _cbrt(a) end _cbrt(a::AbstractZerosMatrix) = float(a) function _cbrt(a::AbstractFillMatrix) n = size(a,1) n == 0 && return float(a) v = getindex_value(a) Fill(cbrt(v)/cbrt(n)^2, axes(a)) end struct RectDiagonal{T,V<:AbstractVector{T},Axes<:Tuple{Vararg{AbstractUnitRange,2}}} <: AbstractMatrix{T} diag::V axes::Axes @inline function RectDiagonal{T,V}(A::V, axes::Axes) where {T,V<:AbstractVector{T},Axes<:Tuple{Vararg{AbstractUnitRange,2}}} Base.require_one_based_indexing(A) @assert any(length(ax) == length(A) for ax in axes) rd = new{T,V,Axes}(A, axes) Base.require_one_based_indexing(rd) return rd end end @inline RectDiagonal{T,V}(A::V, sz::Tuple{Vararg{Integer, 2}}) where {T,V} = RectDiagonal{T,V}(A, oneto.(sz)) @inline RectDiagonal{T,V}(A::V, axes::Vararg{Any, 2}) where {T,V} = RectDiagonal{T,V}(A, axes) @inline RectDiagonal{T,V}(A::V, sz::Vararg{Integer, 2}) where {T,V} = RectDiagonal{T,V}(A, sz) @inline RectDiagonal{T,V}(A::V) where {T,V} = RectDiagonal{T,V}(A, (axes(A, 1), axes(A, 1))) @inline RectDiagonal{T}(A::V, args...) where {T,V} = RectDiagonal{T,V}(A, args...) @inline RectDiagonal(A::V, args...) where {V} = RectDiagonal{eltype(V),V}(A, args...) const UpperOrUnitUpperTriangular{T,S} = Union{UpperTriangular{T,S}, UnitUpperTriangular{T,S}} const LowerOrUnitLowerTriangular{T,S} = Union{LowerTriangular{T,S}, UnitLowerTriangular{T,S}} const UpperOrLowerTriangular{T,S} = Union{UpperOrUnitUpperTriangular{T,S}, LowerOrUnitLowerTriangular{T,S}} # patch missing overload from Base axes(rd::Diagonal{<:Any,<:AbstractFill}) = (axes(rd.diag,1),axes(rd.diag,1)) axes(T::UpperOrLowerTriangular{<:Any,<:AbstractFill}) = axes(parent(T)) axes(rd::RectDiagonal) = rd.axes size(rd::RectDiagonal) = map(length, rd.axes) parent(rd::RectDiagonal) = rd.diag @inline function getindex(rd::RectDiagonal{T}, i::Integer, j::Integer) where T @boundscheck checkbounds(rd, i, j) if i == j @inbounds r = rd.diag[i] else r = zero(T) end return r end function setindex!(rd::RectDiagonal, v, i::Integer, j::Integer) @boundscheck checkbounds(rd, i, j) if i == j @inbounds rd.diag[i] = v elseif !iszero(v) throw(ArgumentError(LazyString("cannot set off-diagonal entry (", i, ", ", j, ") to a nonzero value (", v, ")"))) end return v end diag(rd::RectDiagonal) = rd.diag for f in (:triu, :triu!, :tril, :tril!) @eval ($f)(M::RectDiagonal) = M end # Due to default definitions in LinearAlgebra only the following implementations are needed # (see above for more details) function +(a::RectDiagonal, b::UniformScaling) LinearAlgebra.checksquare(a) return Diagonal(a.diag .+ b.λ) end function -(a::UniformScaling, b::RectDiagonal) LinearAlgebra.checksquare(b) return Diagonal(a.λ .- b.diag) end Base.replace_in_print_matrix(A::RectDiagonal, i::Integer, j::Integer, s::AbstractString) = i == j ? s : Base.replace_with_centered_mark(s) const RectOrDiagonal{T,V,Axes} = Union{RectDiagonal{T,V,Axes}, Diagonal{T,V}} const RectOrDiagonalFill{T,V<:AbstractFillVector{T},Axes} = RectOrDiagonal{T,V,Axes} const RectDiagonalFill{T,V<:AbstractFillVector{T}} = RectDiagonal{T,V} const SquareEye{T,Axes} = Diagonal{T,Ones{T,1,Tuple{Axes}}} const Eye{T,Axes} = RectOrDiagonal{T,Ones{T,1,Tuple{Axes}}} @inline SquareEye{T}(n::Integer) where T = Diagonal(Ones{T}(n)) @inline SquareEye(n::Integer) = Diagonal(Ones(n)) @inline SquareEye{T}(ax::Tuple{AbstractUnitRange{Int}}) where T = Diagonal(Ones{T}(ax)) @inline SquareEye(ax::Tuple{AbstractUnitRange{Int}}) = Diagonal(Ones(ax)) @inline Eye{T}(n::Integer) where T = SquareEye{T}(n) @inline Eye(n::Integer) = SquareEye(n) @inline Eye{T}(ax::Tuple{AbstractUnitRange{Int}}) where T = SquareEye{T}(ax) @inline Eye(ax::Tuple{AbstractUnitRange{Int}}) = SquareEye(ax) # function iterate(iter::Eye, istate = (1, 1)) # (i::Int, j::Int) = istate # m = size(iter, 1) # return i > m ? nothing : # ((@inbounds getindex(iter, i, j)), # j == m ? (i + 1, 1) : (i, j + 1)) # end isone(::SquareEye) = true function diag(E::Eye, k::Integer=0) v = k == 0 ? oneunit(eltype(E)) : zero(eltype(E)) len = length(diagind(E, k)) Fill(v, len) end # These should actually be in StdLib, LinearAlgebra.jl, for all Diagonal for f in (:permutedims, :triu, :triu!, :tril, :tril!, :copy) @eval ($f)(IM::Diagonal{<:Any,<:AbstractFill}) = IM end inv(IM::SquareEye) = IM inv(IM::Diagonal{<:Any,<:AbstractFill}) = Diagonal(map(inv, IM.diag)) Eye(n::Integer, m::Integer) = RectDiagonal(Ones(min(n,m)), n, m) Eye{T}(n::Integer, m::Integer) where T = RectDiagonal{T}(Ones{T}(min(n,m)), n, m) function Eye{T}((a,b)::NTuple{2,AbstractUnitRange{Int}}) where T ab = length(a) ≤ length(b) ? a : b RectDiagonal{T}(Ones{T}((ab,)), (a,b)) end function Eye((a,b)::NTuple{2,AbstractUnitRange{Int}}) ab = length(a) ≤ length(b) ? a : b RectDiagonal(Ones((ab,)), (a,b)) end @inline Eye{T}(A::AbstractMatrix) where T = Eye{T}(size(A)...) @inline Eye(A::AbstractMatrix) = Eye{eltype(A)}(size(A)...) # This may break, as it uses undocumented internals of LinearAlgebra # Ideally this should be copied over to this package # Also, maybe this should reuse the broadcasting behavior of the parent, # once AbstractFill types implement their own BroadcastStyle BroadcastStyle(::Type{<:RectDiagonal}) = LinearAlgebra.StructuredMatrixStyle{RectDiagonal}() function LinearAlgebra.structured_broadcast_alloc(bc, ::Type{<:RectDiagonal}, ::Type{ElType}, n) where {ElType} RectDiagonal(Array{ElType}(undef, minimum(n)), axes(bc)) end @inline LinearAlgebra.fzero(S::RectDiagonal{T}) where {T} = zero(T) ######### # Special matrix types ######### ## Array Base.Array{T,N}(F::AbstractFill{V,N}) where {T,V,N} = convert(Array{T,N}, fill(convert(T, getindex_value(F)), size(F))) # These are in case `zeros` or `ones` are ever faster than `fill` for (Typ, funcs, func) in ((:AbstractZeros, :zeros, :zero), (:AbstractOnes, :ones, :one)) @eval begin Base.Array{T,N}(F::$Typ{V,N}) where {T,V,N} = $funcs(T,size(F)) end end if VERSION < v"1.11-" # temporary patch. should be a PR(#48895) to LinearAlgebra Diagonal{T}(A::AbstractFillMatrix) where T = Diagonal{T}(diag(A)) function convert(::Type{T}, A::AbstractFillMatrix) where T<:Diagonal checksquare(A) isdiag(A) ? T(diag(A)) : throw(InexactError(:convert, T, A)) end end Base.StepRangeLen(F::AbstractFillVector{T}) where T = StepRangeLen(getindex_value(F), zero(T), length(F)) convert(::Type{SL}, F::AbstractFillVector) where SL<:AbstractRange = convert(SL, StepRangeLen(F)) ################# # Structured matrix types ################# for SMT in (:Diagonal, :Bidiagonal, :Tridiagonal, :SymTridiagonal) @eval function diag(D::$SMT{T,<:AbstractFillVector{T}}, k::Integer=0) where {T<:Number} inds = (1,1) .+ (k >= 0 ? (0,k) : (-k,0)) v = get(D, inds, zero(eltype(D))) Fill(v, length(diagind(D, k))) end end ######### # maximum/minimum ######### for op in (:maximum, :minimum) @eval $op(x::AbstractFill) = getindex_value(x) end ######### # Cumsum ######### # These methods are necessary to deal with infinite arrays sum(x::AbstractFill) = getindex_value(x)*length(x) sum(f, x::AbstractFill) = length(x) * f(getindex_value(x)) sum(x::AbstractZeros) = getindex_value(x) # needed to support infinite case steprangelen(st...) = StepRangeLen(st...) function cumsum(x::AbstractFill{T,1}) where T V = promote_op(add_sum, T, T) steprangelen(convert(V,getindex_value(x)), getindex_value(x), length(x)) end cumsum(x::AbstractZerosVector{T}) where T = _range_convert(AbstractVector{promote_op(add_sum, T, T)}, x) cumsum(x::AbstractZerosVector{Bool}) = _range_convert(AbstractVector{Int}, x) cumsum(x::AbstractOnesVector{T}) where T<:Integer = _range_convert(AbstractVector{promote_op(add_sum, T, T)}, oneto(length(x))) cumsum(x::AbstractOnesVector{Bool}) = oneto(length(x)) for op in (:+, :-) @eval begin function accumulate(::typeof($op), x::AbstractFill{T,1}) where T V = promote_op($op, T, T) steprangelen(convert(V,getindex_value(x)), $op(getindex_value(x)), length(x)) end accumulate(::typeof($op), x::AbstractZerosVector{T}) where T = _range_convert(AbstractVector{promote_op($op, T, T)}, x) accumulate(::typeof($op), x::AbstractZerosVector{Bool}) = _range_convert(AbstractVector{Int}, x) end end accumulate(::typeof(+), x::AbstractOnesVector{T}) where T<:Integer = _range_convert(AbstractVector{promote_op(+, T, T)}, oneto(length(x))) accumulate(::typeof(+), x::AbstractOnesVector{Bool}) = oneto(length(x)) ######### # Diff ######### diff(x::AbstractFillVector{T}) where T = Zeros{T}(length(x)-1) ######### # unique ######### unique(x::AbstractFill) = fillsimilar(x, Int(!isempty(x))) allunique(x::AbstractFill) = length(x) < 2 ######### # zero ######### zero(r::AbstractZeros{T,N}) where {T,N} = r # TODO: Make this required? zero(r::AbstractOnes{T,N}) where {T,N} = Zeros{T,N}(axes(r)) zero(r::Fill{T,N}) where {T,N} = Zeros{T,N}(r.axes) ######### # oneunit ######### function one(A::AbstractFillMatrix{T}) where {T} Base.require_one_based_indexing(A) m, n = size(A) m == n || throw(ArgumentError("multiplicative identity defined only for square matrices")) SquareEye{T}(m) end ######### # any/all/isone/iszero ######### function isone(AF::AbstractFillMatrix) (n,m) = size(AF) n != m && return false (n == 0 || m == 0) && return true isone(getindex_value(AF)) || return false n == 1 && return true return false end # all(isempty, []) and any(isempty, []) have non-generic behavior. # We do not follow it here for Eye(0). function any(f::Function, IM::Eye{T}) where T d1, d2 = size(IM) (d1 < 1 || d2 < 1) && return false (d1 > 1 || d2 > 1) && return f(zero(T)) || f(one(T)) return any(f(one(T))) end function all(f::Function, IM::Eye{T}) where T d1, d2 = size(IM) (d1 < 1 || d2 < 1) && return false (d1 > 1 || d2 > 1) && return f(zero(T)) && f(one(T)) return all(f(one(T))) end # In particular, these make iszero(Eye(n)) efficient. # use any/all on scalar to get Boolean error message function any(f::Function, x::AbstractFill) isempty(x) && return false # If the condition is true for one value, then it's true for all fval = f(getindex_value(x)) any((fval,)) end function all(f::Function, x::AbstractFill) isempty(x) && return true # If the condition is true for one value, then it's true for all fval = f(getindex_value(x)) return all((fval,)) end any(x::AbstractFill) = any(identity, x) all(x::AbstractFill) = all(identity, x) count(x::AbstractOnes{Bool}) = length(x) count(x::AbstractZeros{Bool}) = 0 count(f, x::AbstractFill) = f(getindex_value(x)) ? length(x) : 0 ######### # in ######### in(x, A::AbstractFill) = x == getindex_value(A) function in(x, A::RectDiagonal{<:Number}) any(iszero, size(A)) && return false # Empty matrix all(isone, size(A)) && return x == A.diag[1] # A 1x1 matrix has only one element x == zero(eltype(A)) || x in A.diag end ######### # include ######### include("fillalgebra.jl") include("fillbroadcast.jl") include("trues.jl") ## # print ## Base.replace_in_print_matrix(::AbstractZeros, ::Integer, ::Integer, s::AbstractString) = Base.replace_with_centered_mark(s) # following support blocked fill array printing via # BlockArrays.jl axes_print_matrix_row(lay, io, X, A, i, cols, sep, idxlast::Integer=last(axes(X, 2))) = Base.invoke(Base.print_matrix_row, Tuple{IO,AbstractVecOrMat,Vector,Integer,AbstractVector,AbstractString,Integer}, io, X, A, i, cols, sep, idxlast) Base.print_matrix_row(io::IO, X::Union{AbstractFillVector, AbstractFillMatrix, Diagonal{<:Any,<:AbstractFillVector}, RectDiagonal, UpperOrLowerTriangular{<:Any,<:AbstractFillMatrix} }, A::Vector, i::Integer, cols::AbstractVector, sep::AbstractString, idxlast::Integer=last(axes(X, 2))) = axes_print_matrix_row(axes(X), io, X, A, i, cols, sep) # Display concise description of a Fill. function Base.show(io::IO, ::MIME"text/plain", x::Union{Eye, AbstractFill}) if get(IOContext(io), :compact, false) # for example [Fill(i==j,2,2) for i in 1:3, j in 1:4] return show(io, x) end summary(io, x) if !(x isa Union{AbstractZeros, AbstractOnes, Eye}) print(io, ", with ", length(x) > 1 ? "entries" : "entry", " equal to ") show(io, getindex_value(x)) end end function Base.show(io::IO, x::AbstractFill) # for example (Fill(π,3),) print(io, nameof(typeof(x))) sz = size(x) args = if x isa Union{AbstractZeros, AbstractOnes} T = eltype(x) if T != Float64 print(io,"{", T, "}") end print(io, "(") else # show, not print, to handle (Fill(1f0,2),) print(io, "(") show(io, getindex_value(x)) ndims(x) == 0 || print(io, ", ") end join(io, size(x), ", ") print(io, ")") end function Base.show(io::IO, x::Eye) print(io, "Eye(", size(x,1)) if size(x,1) != size(x,2) print(io, ",", size(x,2)) end print(io, ")") end Base.array_summary(io::IO, ::Zeros{T}, inds::Tuple{Vararg{Base.OneTo}}) where T = print(io, Base.dims2string(length.(inds)), " Zeros{$T}") Base.array_summary(io::IO, ::Ones{T}, inds::Tuple{Vararg{Base.OneTo}}) where T = print(io, Base.dims2string(length.(inds)), " Ones{$T}") Base.array_summary(io::IO, a::Fill{T}, inds::Tuple{Vararg{Base.OneTo}}) where T = print(io, Base.dims2string(length.(inds)), " Fill{$T}") Base.array_summary(io::IO, a::Eye{T}, inds::Tuple{Vararg{Base.OneTo}}) where T = print(io, Base.dims2string(length.(inds)), " Eye{$T}") ## # interface ## getindex_value(a::LinearAlgebra.Adjoint) = adjoint(getindex_value(parent(a))) getindex_value(a::LinearAlgebra.Transpose) = transpose(getindex_value(parent(a))) getindex_value(a::SubArray) = getindex_value(parent(a)) copy(a::LinearAlgebra.Adjoint{<:Any,<:AbstractFill}) = copy(parent(a))' copy(a::LinearAlgebra.Transpose{<:Any,<:AbstractFill}) = transpose(parent(a)) ## # view ## Base.@propagate_inbounds view(A::AbstractFill{<:Any,N}, kr::AbstractArray{Bool,N}) where N = _fill_getindex(A, kr) Base.@propagate_inbounds view(A::AbstractFill{<:Any,1}, kr::AbstractVector{Bool}) = _fill_getindex(A, kr) Base.@propagate_inbounds view(A::AbstractFill, I...) = _fill_getindex(A, Base.to_indices(A,I)...) # not getindex since we need array-like indexing Base.@propagate_inbounds function view(A::AbstractFill, I::Vararg{Real}) @boundscheck checkbounds(A, I...) fillsimilar(A) end # repeat _first(t::Tuple) = t[1] _first(t::Tuple{}) = 1 _maybetail(t::Tuple) = Base.tail(t) _maybetail(t::Tuple{}) = t _match_size(sz::Tuple{}, inner::Tuple{}, outer::Tuple{}) = () function _match_size(sz::Tuple, inner::Tuple, outer::Tuple) t1 = (_first(sz), _first(inner), _first(outer)) t2 = _match_size(_maybetail(sz), _maybetail(inner), _maybetail(outer)) (t1, t2...) end function _repeat_size(sz::Tuple, inner::Tuple, outer::Tuple) t = _match_size(sz, inner, outer) map(*, getindex.(t, 1), getindex.(t, 2), getindex.(t, 3)) end function _repeat(A; inner=ntuple(x->1, ndims(A)), outer=ntuple(x->1, ndims(A))) Base.require_one_based_indexing(A) length(inner) >= ndims(A) || throw(ArgumentError(LazyString("number of inner repetitions ", length(inner), " cannot be less than number of dimensions of input array ", ndims(A)))) length(outer) >= ndims(A) || throw(ArgumentError(LazyString("number of outer repetitions ", length(outer), " cannot be less than number of dimensions of input array ", ndims(A)))) sz = _repeat_size(size(A), Tuple(inner), Tuple(outer)) fillsimilar(A, sz) end repeat(A::AbstractFill, count::Integer...) = _repeat(A, outer=count) function repeat(A::AbstractFill; inner=ntuple(x->1, ndims(A)), outer=ntuple(x->1, ndims(A))) _repeat(A, inner=inner, outer=outer) end include("oneelement.jl") end # module ================================================ FILE: src/fillalgebra.jl ================================================ ## vec vec(a::AbstractFill) = fillsimilar(a, length(a)) ## Transpose/Adjoint # cannot do this for vectors since that would destroy scalar dot product for OP in (:transpose, :adjoint) @eval begin function $OP(a::AbstractZerosMatrix) v = getindex_value(a) T = typeof($OP(v)) Zeros{T}(reverse(axes(a))) end $OP(a::AbstractOnesMatrix) = fillsimilar(a, reverse(axes(a))) $OP(a::FillMatrix) = Fill($OP(a.value), reverse(a.axes)) end end permutedims(a::AbstractFillVector) = fillsimilar(a, (1, length(a))) permutedims(a::AbstractFillMatrix) = fillsimilar(a, reverse(axes(a))) function permutedims(B::AbstractFill, perm) dimsB = size(B) ndimsB = length(dimsB) (ndimsB == length(perm) && isperm(perm)) || throw(ArgumentError("no valid permutation of dimensions")) dimsP = ntuple(i->dimsB[perm[i]], ndimsB)::typeof(dimsB) fillsimilar(B, dimsP) end Base.@propagate_inbounds function reverse(A::AbstractFill, start::Integer, stop::Integer=lastindex(A)) @boundscheck checkbounds(A, start) @boundscheck checkbounds(A, stop) A end reverse(A::AbstractFill; dims=:) = A ## Algebraic identities # Default outputs, can overload to customize mult_fill(a, b, val, ax) = Fill(val, ax) mult_zeros(a, b, elt, ax) = Zeros{elt}(ax) mult_ones(a, b, elt, ax) = Ones{elt}(ax) function mult_fill(a::AbstractFill, b::AbstractFill, ax) val = getindex_value(a)*getindex_value(b)*size(a,2) return mult_fill(a, b, val, ax) end function mult_zeros(a, b, ax) # This is currently only used in contexts where zero is defined # might need a rethink elt = typeof(zero(eltype(a)) * zero(eltype(b))) return mult_zeros(a, b, elt, ax) end function mult_ones(a, b, ax) # This is currently only used in contexts where zero is defined # might need a rethink elt = typeof(zero(eltype(a)) * zero(eltype(b))) return mult_ones(a, b, elt, ax) end function mult_axes(a, b) Base.require_one_based_indexing(a, b) size(a, 2) ≠ size(b, 1) && throw(DimensionMismatch(LazyString("A has dimensions ", size(a), " but B has dimensions ", size(b)))) return (axes(a, 1), axes(b)[2:end]...) end mult_fill(a, b) = mult_fill(a, b, mult_axes(a, b)) # for arrays of numbers, we assume that zero is defined for the result # in this case, we may express the result as a Zeros mult_zeros(a::AbstractArray{<:Number}, b::AbstractArray{<:Number}) = mult_zeros(a, b, mult_axes(a, b)) # In general, we create a Fill that doesn't assume anything about the # properties of the element type mult_zeros(a, b) = mult_fill(a, b, mult_axes(a, b)) mult_ones(a, b) = mult_ones(a, b, mult_axes(a, b)) *(a::AbstractFillMatrix, b::AbstractFillMatrix) = mult_fill(a,b) *(a::AbstractFillMatrix, b::AbstractFillVector) = mult_fill(a,b) # this treats a size (n,) vector as a nx1 matrix, so b needs to have 1 row # special cased, as OnesMatrix * OnesMatrix isn't a Ones *(a::AbstractOnesVector, b::AbstractOnesMatrix) = mult_ones(a, b) *(a::AbstractZerosMatrix, b::AbstractZerosMatrix) = mult_zeros(a, b) *(a::AbstractZerosMatrix, b::AbstractZerosVector) = mult_zeros(a, b) *(a::AbstractZerosMatrix, b::AbstractFillMatrix) = mult_zeros(a, b) *(a::AbstractZerosMatrix, b::AbstractFillVector) = mult_zeros(a, b) *(a::AbstractFillMatrix, b::AbstractZerosMatrix) = mult_zeros(a, b) *(a::AbstractFillMatrix, b::AbstractZerosVector) = mult_zeros(a, b) for MT in (:AbstractMatrix, :AbstractTriangular) @eval *(a::AbstractZerosMatrix, b::$MT) = mult_zeros(a, b) @eval *(a::$MT, b::AbstractZerosMatrix) = mult_zeros(a, b) end # Odd way to deal with the type-parameters to avoid ambiguities for MT in (:(AbstractMatrix{T}), :(Transpose{<:Any, <:AbstractMatrix{T}}), :(Adjoint{<:Any, <:AbstractMatrix{T}}), :(AbstractTriangular{T})) @eval *(a::$MT, b::AbstractZerosVector) where {T} = mult_zeros(a, b) end for T in (:AbstractZerosMatrix, :AbstractFillMatrix) @eval begin *(a::Transpose{<:Any, <:AbstractVector}, b::$T) = transpose(transpose(b) * parent(a)) *(a::Adjoint{<:Any, <:AbstractVector}, b::$T) = adjoint(adjoint(b) * parent(a)) end end *(a::AbstractZerosMatrix, b::AbstractVector) = mult_zeros(a, b) function *(F::AbstractFillMatrix, v::AbstractVector) check_matmul_sizes(F, v) Fill(getindex_value(F) * sum(v), (axes(F,1),)) end function lmul_diag(a::Diagonal, b) size(a,2) == size(b,1) || throw(DimensionMismatch(LazyString("A has dimensions ", size(a), " but B has dimensions ", size(b)))) parent(a) .* b # use special broadcast end function rmul_diag(a, b::Diagonal) size(a,2) == size(b,1) || throw(DimensionMismatch(LazyString("A has dimensions ", size(a), " but B has dimensions ", size(b)))) a .* permutedims(parent(b)) # use special broadcast end *(a::AbstractZerosMatrix, b::Diagonal) = rmul_diag(a, b) *(a::Diagonal, b::AbstractZerosVector) = lmul_diag(a, b) *(a::Diagonal, b::AbstractZerosMatrix) = lmul_diag(a, b) *(a::Diagonal, b::AbstractFillMatrix) = lmul_diag(a, b) *(a::AbstractFillMatrix, b::Diagonal) = rmul_diag(a, b) @noinline function check_matmul_sizes(A::AbstractMatrix, x::AbstractVector) Base.require_one_based_indexing(A, x) size(A,2) == size(x,1) || throw(DimensionMismatch(LazyString("second dimension of A, ", size(A,2), ", does not match length of x, ", length(x)))) end @noinline function check_matmul_sizes(A::AbstractMatrix, B::AbstractMatrix) Base.require_one_based_indexing(A, B) size(A,2) == size(B,1) || throw(DimensionMismatch(LazyString("second dimension of A, ", size(A,2), ", does not match first dimension of B, ", size(B,1)))) end @noinline function check_matmul_sizes(y::AbstractVector, A::AbstractMatrix, x::AbstractVector) Base.require_one_based_indexing(A, x, y) size(A,2) == size(x,1) || throw(DimensionMismatch(LazyString("second dimension of A, ", size(A,2), ", does not match length of x, ", length(x)))) size(y,1) == size(A,1) || throw(DimensionMismatch(LazyString("first dimension of A, ", size(A,1), ", does not match length of y, ", length(y)))) end @noinline function check_matmul_sizes(C::AbstractMatrix, A::AbstractMatrix, B::AbstractMatrix) Base.require_one_based_indexing(A, B, C) size(A,2) == size(B,1) || throw(DimensionMismatch(LazyString("second dimension of A, ", size(A,2), ", does not match first dimension of B, ", size(B,1)))) size(C,1) == size(A,1) && size(C,2) == size(B,2) || throw(DimensionMismatch(LazyString("A has size ", size(A), ", B has size ", size(B), ", C has size ", size(C)))) end function mul!(y::AbstractVector, A::AbstractFillMatrix, b::AbstractFillVector, alpha::Number, beta::Number) check_matmul_sizes(y, A, b) Abα = Ref(getindex_value(A) * getindex_value(b) * alpha * length(b)) if iszero(beta) y .= Abα else y .= Abα .+ y .* beta end y end function mul!(y::StridedVector, A::StridedMatrix, b::AbstractFillVector, alpha::Number, beta::Number) check_matmul_sizes(y, A, b) bα = Ref(getindex_value(b) * alpha) if iszero(beta) y .= Ref(zero(eltype(y))) else rmul!(y, beta) end for Acol in eachcol(A) @. y += Acol * bα end y end function mul!(y::StridedVector, A::AbstractFillMatrix, b::StridedVector, alpha::Number, beta::Number) check_matmul_sizes(y, A, b) Abα = Ref(getindex_value(A) * sum(b) * alpha) if iszero(beta) y .= Abα else y .= Abα .+ y .* beta end y end function _mul_adjtrans!(y::AbstractVector, A::AbstractMatrix, b::AbstractFillVector, alpha, beta, f) bα = getindex_value(b) * alpha At = f(A) if iszero(beta) for (ind, Atcol) in zip(eachindex(y), eachcol(At)) y[ind] = f(sum(Atcol)) * bα end else for (ind, Atcol) in zip(eachindex(y), eachcol(At)) y[ind] = f(sum(Atcol)) * bα .+ y[ind] .* beta end end y end for (T, f) in ((:Adjoint, :adjoint), (:Transpose, :transpose)) @eval function mul!(y::StridedVector, A::$T{<:Any, <:StridedMatrix}, b::AbstractFillVector, alpha::Number, beta::Number) check_matmul_sizes(y, A, b) _mul_adjtrans!(y, A, b, alpha, beta, $f) end end # unnecessary indirection, added for ambiguity resolution function _mulfill!(C::AbstractMatrix, A::AbstractFillMatrix, B::AbstractFillMatrix, alpha, beta) check_matmul_sizes(C, A, B) ABα = getindex_value(A) * getindex_value(B) * alpha * size(B,1) if iszero(beta) C .= ABα else C .= ABα .+ C .* beta end return C end function mul!(C::AbstractMatrix, A::AbstractFillMatrix, B::AbstractFillMatrix, alpha::Number, beta::Number) _mulfill!(C, A, B, alpha, beta) return C end function copyfirstcol!(C) @views for i in axes(C,2)[2:end] C[:, i] .= C[:, 1] end return C end _firstcol(C::AbstractMatrix) = first(eachcol(C)) function copyfirstrow!(C) # C[begin+1:end, ind] .= permutedims(_firstrow(C)) # we loop here as the aliasing check isn't smart enough to # detect that the two sides don't alias, and ends up materializing the RHS for (ind, v) in pairs(_firstrow(C)) C[begin+1:end, ind] .= Ref(v) end return C end _firstrow(C::AbstractMatrix) = first(eachrow(C)) function _mulfill!(C::AbstractMatrix, A::AbstractMatrix, B::AbstractFillMatrix, alpha, beta) check_matmul_sizes(C, A, B) iszero(size(B,2)) && return C # no columns in B and C, empty matrix if iszero(beta) # the mat-vec product sums along the rows of A mul!(_firstcol(C), A, _firstcol(B), alpha, beta) copyfirstcol!(C) else # the mat-vec product sums along the rows of A, which produces the first column of ABα # allocate a temporary column vector to store the result v = A * (_firstcol(B) * alpha) C .= v .+ C .* beta end return C end function _mulfill!(C::AbstractMatrix, A::AbstractFillMatrix, B::AbstractMatrix, alpha, beta) check_matmul_sizes(C, A, B) iszero(size(A,1)) && return C # no rows in A and C, empty matrix Aval = getindex_value(A) if iszero(beta) Crow = _firstrow(C) # sum along the columns of B Crow .= Ref(Aval) .* sum.(eachcol(B)) .* alpha copyfirstrow!(C) else # sum along the columns of B, and allocate the result. # This is the first row of ABα ABα_row = Ref(Aval) .* sum.(eachcol(B)) .* alpha C .= permutedims(ABα_row) .+ C .* beta end return C end function mul!(C::StridedMatrix, A::StridedMatrix, B::AbstractFillMatrix, alpha::Number, beta::Number) _mulfill!(C, A, B, alpha, beta) return C end function mul!(C::StridedMatrix, A::AbstractFillMatrix, B::StridedMatrix, alpha::Number, beta::Number) _mulfill!(C, A, B, alpha, beta) return C end for T in (:Adjoint, :Transpose) @eval begin function mul!(C::StridedMatrix, A::$T{<:Any, <:StridedMatrix}, B::AbstractFillMatrix, alpha::Number, beta::Number) _mulfill!(C, A, B, alpha, beta) return C end function mul!(C::StridedMatrix, A::AbstractFillMatrix, B::$T{<:Any, <:StridedMatrix}, alpha::Number, beta::Number) _mulfill!(C, A, B, alpha, beta) return C end end end function _adjvec_mul_zeros(a, b) la, lb = length(a), length(b) if la ≠ lb throw(DimensionMismatch(LazyString("dot product arguments have lengths ", la, " and ", lb))) end # ensure that all the elements of `a` are of the same size, # so that ∑ᵢaᵢbᵢ = b₁∑ᵢaᵢ makes sense if la == 0 # this errors if a is a nested array, and zero isn't well-defined return zero(eltype(a)) * zero(eltype(b)) end a1 = a[1] sza1 = size(a1) all(x -> size(x) == sza1, a) || throw(DimensionMismatch(LazyString("not all elements of A are of size ", sza1))) # we replace b₁∑ᵢaᵢ by b₁a₁, as we know that b₁ is zero. # Each term in the summation is zero, so the sum is equal to the first term return a1 * b[1] end for MT in (:AbstractMatrix, :AbstractTriangular, :(Adjoint{<:Any,<:TransposeAbsVec}), :AbstractFillMatrix) @eval *(a::AdjointAbsVec{<:Any,<:AbstractZerosVector}, b::$MT) = (b' * a')' end # ambiguity function *(a::AdjointAbsVec{<:Any,<:AbstractZerosVector}, b::TransposeAbsVec{<:Any,<:AdjointAbsVec}) # change from Transpose ∘ Adjoint to Adjoint ∘ Transpose b2 = adjoint(transpose(adjoint(transpose(b)))) a * b2 end *(a::AdjointAbsVec{<:Any,<:AbstractZerosVector}, b::AbstractZerosMatrix) = (b' * a')' for MT in (:AbstractMatrix, :AbstractTriangular, :(Transpose{<:Any,<:AdjointAbsVec}), :AbstractFillMatrix) @eval *(a::TransposeAbsVec{<:Any,<:AbstractZerosVector}, b::$MT) = transpose(transpose(b) * transpose(a)) end *(a::TransposeAbsVec{<:Any,<:AbstractZerosVector}, b::AbstractZerosMatrix) = transpose(transpose(b) * transpose(a)) *(a::AbstractVector, b::AdjOrTransAbsVec{<:Any,<:AbstractZerosVector}) = a * permutedims(parent(b)) for MT in (:AbstractMatrix, :AbstractTriangular) @eval *(a::$MT, b::AdjOrTransAbsVec{<:Any,<:AbstractZerosVector}) = a * permutedims(parent(b)) end *(a::AbstractZerosVector, b::AdjOrTransAbsVec{<:Any,<:AbstractZerosVector}) = a * permutedims(parent(b)) *(a::AbstractZerosMatrix, b::AdjOrTransAbsVec{<:Any,<:AbstractZerosVector}) = a * permutedims(parent(b)) *(a::AdjointAbsVec, b::AbstractZerosVector) = _adjvec_mul_zeros(a, b) *(a::AdjointAbsVec{<:Number}, b::AbstractZerosVector{<:Number}) = _adjvec_mul_zeros(a, b) *(a::TransposeAbsVec, b::AbstractZerosVector) = _adjvec_mul_zeros(a, b) *(a::TransposeAbsVec{<:Number}, b::AbstractZerosVector{<:Number}) = _adjvec_mul_zeros(a, b) *(a::Adjoint{T, <:AbstractMatrix{T}} where T, b::AbstractZeros{<:Any, 1}) = mult_zeros(a, b) *(D::Diagonal, a::Adjoint{<:Any,<:AbstractZerosVector}) = (a' * D')' *(D::Diagonal, a::Transpose{<:Any,<:AbstractZerosVector}) = transpose(transpose(a) * transpose(D)) *(a::AdjointAbsVec{<:Any,<:AbstractZerosVector}, D::Diagonal) = (D' * a')' *(a::TransposeAbsVec{<:Any,<:AbstractZerosVector}, D::Diagonal) = transpose(D*transpose(a)) function _triple_zeromul(x, D::Diagonal, y) if !(length(x) == length(D.diag) == length(y)) throw(DimensionMismatch(LazyString("x has length ", length(x), ", D has size ", size(D), ", and y has ", length(y)))) end zero(promote_type(eltype(x), eltype(D), eltype(y))) end *(x::AdjointAbsVec{<:Any,<:AbstractZerosVector}, D::Diagonal, y::AbstractVector) = _triple_zeromul(x, D, y) *(x::TransposeAbsVec{<:Any,<:AbstractZerosVector}, D::Diagonal, y::AbstractVector) = _triple_zeromul(x, D, y) *(x::AdjointAbsVec, D::Diagonal, y::AbstractZerosVector) = _triple_zeromul(x, D, y) *(x::TransposeAbsVec, D::Diagonal, y::AbstractZerosVector) = _triple_zeromul(x, D, y) *(x::AdjointAbsVec{<:Any,<:AbstractZerosVector}, D::Diagonal, y::AbstractZerosVector) = _triple_zeromul(x, D, y) *(x::TransposeAbsVec{<:Any,<:AbstractZerosVector}, D::Diagonal, y::AbstractZerosVector) = _triple_zeromul(x, D, y) function *(a::Transpose{T, <:AbstractVector}, b::AbstractZerosVector{T}) where T<:Real la, lb = length(a), length(b) if la ≠ lb throw(DimensionMismatch(LazyString("dot product arguments have lengths ", la, " and ", lb))) end return zero(T) end *(a::Transpose{T, <:AbstractMatrix{T}}, b::AbstractZerosVector{T}) where T<:Real = mult_zeros(a, b) # support types with fast sum # infinite cases should be supported in InfiniteArrays.jl # type issues of Bool dot are ignored at present. function _fill_dot(a::AbstractFillVector{T}, b::AbstractVector{V}) where {T,V} axes(a) == axes(b) || throw(DimensionMismatch(LazyString("dot product arguments have lengths ", length(a), " and ", length(b)))) dot(getindex_value(a), sum(b)) end function _fill_dot_rev(a::AbstractVector{T}, b::AbstractFillVector{V}) where {T,V} axes(a) == axes(b) || throw(DimensionMismatch(LazyString("dot product arguments have lengths ", length(a), " and ", length(b)))) dot(sum(a), getindex_value(b)) end dot(a::AbstractFillVector, b::AbstractFillVector) = _fill_dot(a, b) dot(a::AbstractFillVector, b::AbstractVector) = _fill_dot(a, b) dot(a::AbstractVector, b::AbstractFillVector) = _fill_dot_rev(a, b) function dot(u::AbstractVector, E::Eye, v::AbstractVector) length(u) == size(E,1) && length(v) == size(E,2) || throw(DimensionMismatch(LazyString("dot product arguments have dimensions ", length(u), "×", size(E), "×", length(v)))) d = dot(u,v) T = typeof(one(eltype(E)) * d) convert(T, d) end function dot(u::AbstractVector, D::Diagonal{<:Any,<:Fill}, v::AbstractVector) length(u) == size(D,1) && length(v) == size(D,2) || throw(DimensionMismatch(LazyString("dot product arguments have dimensions ", length(u), "×", size(D), "×", length(v)))) D.diag.value*dot(u, v) end function dot(u::AbstractVector{T}, D::Diagonal{U,<:Zeros}, v::AbstractVector{V}) where {T,U,V} length(u) == size(D,1) && length(v) == size(D,2) || throw(DimensionMismatch(LazyString("dot product arguments have dimensions ", length(u), "×", size(D), "×", length(v)))) zero(promote_type(T,U,V)) end # Addition and Subtraction +(a::AbstractFill) = a -(a::AbstractZeros) = a -(a::AbstractFill) = Fill(-getindex_value(a), size(a)) # special-cased for type-stability, as Ones + Ones is not a Ones Base.reduce_first(::typeof(+), x::AbstractOnes) = Fill(Base.reduce_first(+, getindex_value(x)), axes(x)) function +(a::AbstractZeros{T}, b::AbstractZeros{V}) where {T, V} # for disambiguity promote_shape(a,b) return elconvert(promote_op(+,T,V),a) end function -(a::AbstractZeros{T}, b::AbstractZeros{V}) where {T, V} # for disambiguity promote_shape(a,b) return elconvert(promote_op(-,T,V),-b) end # AbstractFill and Array for disambiguity for TYPE in (:Array, :AbstractFill, :AbstractRange, :AbstractArray) @eval function +(a::$TYPE{T}, b::AbstractZeros{V}) where {T, V} promote_shape(a,b) return elconvert(promote_op(+,T,V),a) end @eval function -(a::$TYPE{T}, b::AbstractZeros{V}) where {T, V} promote_shape(a,b) return elconvert(promote_op(-,T,V),a) end @eval function -(a::AbstractZeros{T}, b::$TYPE{V}) where {T, V} promote_shape(a,b) return elconvert(promote_op(-,T,V),-b) end @eval +(a::AbstractZeros, b::$TYPE) = b + a end # for VERSION other than 1.6, could use ZerosMatrix only function +(a::AbstractFillMatrix{T}, b::UniformScaling) where {T} n = checksquare(a) return a + Diagonal(Fill(zero(T) + b.λ, n)) end # LinearAlgebra defines `-(a::AbstractMatrix, b::UniformScaling) = a + (-b)`, # so the implementation of `-(a::UniformScaling, b::AbstractFill{<:Any,2})` is sufficient -(a::UniformScaling, b::AbstractFill) = -b + a # @test I-Zeros(3,3) === Diagonal(Ones(3)) # TODO: How to do this conversion generically? -(a::AbstractOnes, b::AbstractOnes) = broadcasted_zeros(+, a, eltype(a), axes(a)) + broadcasted_zeros(-, b, eltype(a), axes(a)) # no AbstractArray. Otherwise incompatible with StaticArrays.jl for TYPE in (:Array, :AbstractRange) @eval begin +(a::$TYPE, b::AbstractFill) = fill_add(a, b) -(a::$TYPE, b::AbstractFill) = a + (-b) +(a::AbstractFill, b::$TYPE) = fill_add(b, a) -(a::AbstractFill, b::$TYPE) = a + (-b) end end +(a::AbstractFill, b::AbstractFill) = Fill(getindex_value(a) + getindex_value(b), promote_shape(a,b)) -(a::AbstractFill, b::AbstractFill) = a + (-b) @inline function fill_add(a::AbstractArray, b::AbstractFill) promote_shape(a, b) a .+ (getindex_value(b),) end @inline function fill_add(a::AbstractArray{<:Number}, b::AbstractFill) promote_shape(a, b) a .+ getindex_value(b) end # following needed since as of Julia v1.8 convert(AbstractArray{T}, ::AbstractRange) might return a Vector @inline elconvert(::Type{T}, A::AbstractRange) where T = T(first(A)):T(step(A)):T(last(A)) @inline elconvert(::Type{T}, A::AbstractUnitRange) where T<:Integer = AbstractUnitRange{T}(A) @inline elconvert(::Type{T}, A::AbstractArray) where T = AbstractArray{T}(A) #### # norm #### for op in (:norm1, :norm2, :normInf, :normMinusInf) @eval $op(a::AbstractZeros) = norm(getindex_value(a)) end normp(a::AbstractZeros, p) = norm(getindex_value(a)) norm1(a::AbstractFill) = length(a)*norm(getindex_value(a)) function norm2(a::AbstractFill) nrm1 = norm(getindex_value(a)) sqrt(oftype(nrm1, length(a)))*nrm1 end function normp(a::AbstractFill, p) nrm1 = norm(getindex_value(a)) (length(a))^(1/oftype(nrm1, p))*nrm1 end normInf(a::AbstractFill) = norm(getindex_value(a)) normMinusInf(a::AbstractFill) = norm(getindex_value(a)) ### # lmul!/rmul! ### function lmul!(x::Number, z::AbstractFill) λ = getindex_value(z) # Following check ensures consistency w/ lmul!(x, Array(z)) # for, e.g., lmul!(NaN, z) x*λ == λ || throw(ArgumentError(LazyString("Cannot scale by ", x))) z end function rmul!(z::AbstractFill, x::Number) λ = getindex_value(z) # Following check ensures consistency w/ lmul!(x, Array(z)) # for, e.g., lmul!(NaN, z) λ*x == λ || throw(ArgumentError(LazyString("Cannot scale by ", x))) z end fillzero(::Type{Fill{T,N,AXIS}}, n, m) where {T,N,AXIS} = Fill{T,N,AXIS}(zero(T), (n, m)) fillzero(::Type{<:AbstractZeros{T,N,AXIS}}, n, m) where {T,N,AXIS} = Zeros{T,N,AXIS}((n, m)) fillzero(::Type{F}, n, m) where F = throw(ArgumentError(LazyString("Cannot create a zero array of type ", F))) diagzero(D::Diagonal{F}, i, j) where F<:AbstractFill = fillzero(F, axes(D.diag[i], 1), axes(D.diag[j], 2)) # kron # Default outputs, can overload to customize kron_fill(a, b, val, ax) = Fill(val, ax) kron_zeros(a, b, elt, ax) = Zeros{elt}(ax) kron_ones(a, b, elt, ax) = Ones{elt}(ax) _kronsize(f::AbstractFillVector, g::AbstractFillVector) = (size(f,1)*size(g,1),) _kronsize(f::AbstractFillVecOrMat, g::AbstractFillVecOrMat) = (size(f,1)*size(g,1), size(f,2)*size(g,2)) function _kron(f::AbstractFill, g::AbstractFill, sz) v = getindex_value(f)*getindex_value(g) return kron_fill(f, g, v, sz) end function _kron(f::AbstractZeros, g::AbstractZeros, sz) elt = promote_type(eltype(f), eltype(g)) return kron_zeros(f, g, elt, sz) end function _kron(f::AbstractOnes, g::AbstractOnes, sz) elt = promote_type(eltype(f), eltype(g)) return kron_ones(f, g, elt, sz) end function kron(f::AbstractFillVecOrMat, g::AbstractFillVecOrMat) sz = _kronsize(f, g) return _kron(f, g, sz) end # bandedness function LinearAlgebra.istriu(A::AbstractFillMatrix, k::Integer = 0) iszero(A) || k <= -(size(A,1)-1) end function LinearAlgebra.istril(A::AbstractFillMatrix, k::Integer = 0) iszero(A) || k >= size(A,2)-1 end triu(A::AbstractZerosMatrix, k::Integer=0) = A tril(A::AbstractZerosMatrix, k::Integer=0) = A ================================================ FILE: src/fillbroadcast.jl ================================================ ### map map(f::Function, r::AbstractFill) = Fill(f(getindex_value(r)), axes(r)) function map(f::Function, v::AbstractFillVector, ws::AbstractFillVector...) stop = mapreduce(length, min, (v, ws...)) val = f(map(getindex_value, (v, ws...))...) Fill(val, stop) end function map(f::Function, q::AbstractFill, rs::AbstractFill...) if _maplinear(q, rs...) map(f, map(vec, (q, rs...))...) else val = f(map(getindex_value, (q, rs...))...) Fill(val, axes(q)) end end function _maplinear(rs...) # tries to match Base's behaviour, could perhaps hook in more deeply if any(ndims(r)==1 for r in rs) return true else r1 = axes(first(rs)) for r in rs axes(r) == r1 || throw(DimensionMismatch( LazyString("dimensions must match: a has dims ", r1, ", b has dims ", axes(r)))) end return false end end ### mapreduce function Base._mapreduce_dim(f, op, ::Base._InitialValue, A::AbstractFill, ::Colon) fval = f(getindex_value(A)) out = fval for _ in 2:length(A) out = op(out, fval) end out end function Base._mapreduce_dim(f, op, ::Base._InitialValue, A::AbstractFill, dims) fval = f(getindex_value(A)) red = *(ntuple(d -> d in dims ? size(A,d) : 1, ndims(A))...) out = fval for _ in 2:red out = op(out, fval) end Fill(out, ntuple(d -> d in dims ? Base.OneTo(1) : axes(A,d), ndims(A))) end function mapreduce(f, op, A::AbstractFill, B::AbstractFill; kw...) val(_...) = f(getindex_value(A), getindex_value(B)) reduce(op, map(val, A, B); kw...) end # These are particularly useful because mapreduce(*, +, A, B; dims) is slow in Base, # but can be re-written as some mapreduce(g, +, C; dims) which is fast. function mapreduce(f, op, A::AbstractFill, B::AbstractArray, Cs::AbstractArray...; kw...) g(b, cs...) = f(getindex_value(A), b, cs...) mapreduce(g, op, B, Cs...; kw...) end function mapreduce(f, op, A::AbstractArray, B::AbstractFill, Cs::AbstractArray...; kw...) h(a, cs...) = f(a, getindex_value(B), cs...) mapreduce(h, op, A, Cs...; kw...) end function mapreduce(f, op, A::AbstractFill, B::AbstractFill, Cs::AbstractArray...; kw...) gh(cs...) = f(getindex_value(A), getindex_value(B), cs...) mapreduce(gh, op, Cs...; kw...) end ### Unary broadcasting function broadcasted(::DefaultArrayStyle{N}, op, r::AbstractFill{T,N}) where {T,N} return Fill(op(getindex_value(r)), axes(r)) end broadcasted(::DefaultArrayStyle, ::typeof(+), r::AbstractZeros) = r broadcasted(::DefaultArrayStyle, ::typeof(-), r::AbstractZeros) = r broadcasted(::DefaultArrayStyle, ::typeof(+), r::AbstractOnes) = r broadcasted(::DefaultArrayStyle{N}, ::typeof(conj), r::AbstractZeros{T,N}) where {T,N} = r broadcasted(::DefaultArrayStyle{N}, ::typeof(conj), r::AbstractOnes{T,N}) where {T,N} = r broadcasted(::DefaultArrayStyle{N}, ::typeof(real), r::AbstractZeros{T,N}) where {T,N} = Zeros{real(T)}(axes(r)) broadcasted(::DefaultArrayStyle{N}, ::typeof(real), r::AbstractOnes{T,N}) where {T,N} = Ones{real(T)}(axes(r)) broadcasted(::DefaultArrayStyle{N}, ::typeof(imag), r::AbstractZeros{T,N}) where {T,N} = Zeros{real(T)}(axes(r)) broadcasted(::DefaultArrayStyle{N}, ::typeof(imag), r::AbstractOnes{T,N}) where {T,N} = Zeros{real(T)}(axes(r)) ### Binary broadcasting # Default outputs, can overload to customize broadcasted_fill(f, a, val, ax) = Fill(val, ax) broadcasted_fill(f, a, b, val, ax) = Fill(val, ax) broadcasted_zeros(f, a, elt, ax) = Zeros{elt}(ax) broadcasted_zeros(f, a, b, elt, ax) = Zeros{elt}(ax) broadcasted_ones(f, a, elt, ax) = Ones{elt}(ax) broadcasted_ones(f, a, b, elt, ax) = Ones{elt}(ax) function broadcasted(::DefaultArrayStyle, op, a::AbstractFill, b::AbstractFill) val = op(getindex_value(a), getindex_value(b)) ax = broadcast_shape(axes(a), axes(b)) return broadcasted_fill(op, a, b, val, ax) end function _broadcasted_zeros(f, a, b) elt = Base.Broadcast.combine_eltypes(f, (a, b)) ax = broadcast_shape(axes(a), axes(b)) return broadcasted_zeros(f, a, b, elt, ax) end function _broadcasted_ones(f, a, b) elt = Base.Broadcast.combine_eltypes(f, (a, b)) ax = broadcast_shape(axes(a), axes(b)) return broadcasted_ones(f, a, b, elt, ax) end function _broadcasted_nan(f, a, b) val = convert(Base.Broadcast.combine_eltypes(f, (a, b)), NaN) ax = broadcast_shape(axes(a), axes(b)) return broadcasted_fill(f, a, b, val, ax) end broadcasted(::DefaultArrayStyle, ::typeof(+), a::AbstractZeros, b::AbstractZeros) = _broadcasted_zeros(+, a, b) broadcasted(::DefaultArrayStyle, ::typeof(+), a::AbstractOnes, b::AbstractZeros) = _broadcasted_ones(+, a, b) broadcasted(::DefaultArrayStyle, ::typeof(+), a::AbstractZeros, b::AbstractOnes) = _broadcasted_ones(+, a, b) broadcasted(::DefaultArrayStyle, ::typeof(-), a::AbstractZeros, b::AbstractZeros) = _broadcasted_zeros(-, a, b) broadcasted(::DefaultArrayStyle, ::typeof(-), a::AbstractOnes, b::AbstractZeros) = _broadcasted_ones(-, a, b) broadcasted(::DefaultArrayStyle, ::typeof(-), a::AbstractOnes, b::AbstractOnes) = _broadcasted_zeros(-, a, b) broadcasted(::DefaultArrayStyle{1}, ::typeof(+), a::AbstractZerosVector, b::AbstractZerosVector) = _broadcasted_zeros(+, a, b) broadcasted(::DefaultArrayStyle{1}, ::typeof(+), a::AbstractOnesVector, b::AbstractZerosVector) = _broadcasted_ones(+, a, b) broadcasted(::DefaultArrayStyle{1}, ::typeof(+), a::AbstractZerosVector, b::AbstractOnesVector) = _broadcasted_ones(+, a, b) broadcasted(::DefaultArrayStyle{1}, ::typeof(-), a::AbstractZerosVector, b::AbstractZerosVector) = _broadcasted_zeros(-, a, b) broadcasted(::DefaultArrayStyle{1}, ::typeof(-), a::AbstractOnesVector, b::AbstractZerosVector) = _broadcasted_ones(-, a, b) broadcasted(::DefaultArrayStyle, ::typeof(*), a::AbstractZeros, b::AbstractZeros) = _broadcasted_zeros(*, a, b) # In following, need to restrict to <: Number as otherwise we cannot infer zero from type # TODO: generalise to things like SVector for op in (:*, :/) @eval begin broadcasted(::DefaultArrayStyle, ::typeof($op), a::AbstractZeros, b::AbstractOnes) = _broadcasted_zeros($op, a, b) broadcasted(::DefaultArrayStyle, ::typeof($op), a::AbstractZeros, b::AbstractFill{<:Number}) = _broadcasted_zeros($op, a, b) broadcasted(::DefaultArrayStyle, ::typeof($op), a::AbstractZeros, b::Number) = _broadcasted_zeros($op, a, b) broadcasted(::DefaultArrayStyle, ::typeof($op), a::AbstractZeros, b::AbstractRange) = _broadcasted_zeros($op, a, b) broadcasted(::DefaultArrayStyle, ::typeof($op), a::AbstractZeros, b::AbstractArray{<:Number}) = _broadcasted_zeros($op, a, b) broadcasted(::DefaultArrayStyle, ::typeof($op), a::AbstractZeros, b::Base.Broadcast.Broadcasted) = _broadcasted_zeros($op, a, b) broadcasted(::DefaultArrayStyle{1}, ::typeof($op), a::AbstractZeros, b::AbstractRange) = _broadcasted_zeros($op, a, b) end end for op in (:*, :\) @eval begin broadcasted(::DefaultArrayStyle, ::typeof($op), a::AbstractOnes, b::AbstractZeros) = _broadcasted_zeros($op, a, b) broadcasted(::DefaultArrayStyle, ::typeof($op), a::AbstractFill{<:Number}, b::AbstractZeros) = _broadcasted_zeros($op, a, b) broadcasted(::DefaultArrayStyle, ::typeof($op), a::Number, b::AbstractZeros) = _broadcasted_zeros($op, a, b) broadcasted(::DefaultArrayStyle, ::typeof($op), a::AbstractRange, b::AbstractZeros) = _broadcasted_zeros($op, a, b) broadcasted(::DefaultArrayStyle, ::typeof($op), a::AbstractArray{<:Number}, b::AbstractZeros) = _broadcasted_zeros($op, a, b) broadcasted(::DefaultArrayStyle, ::typeof($op), a::Base.Broadcast.Broadcasted, b::AbstractZeros) = _broadcasted_zeros($op, a, b) broadcasted(::DefaultArrayStyle{1}, ::typeof($op), a::AbstractRange, b::AbstractZeros) = _broadcasted_zeros($op, a, b) end end for op in (:*, :/, :\) @eval broadcasted(::DefaultArrayStyle, ::typeof($op), a::AbstractOnes, b::AbstractOnes) = _broadcasted_ones($op, a, b) end for op in (:/, :\) @eval broadcasted(::DefaultArrayStyle, ::typeof($op), a::AbstractZeros{<:Number}, b::AbstractZeros{<:Number}) = _broadcasted_nan($op, a, b) end # special case due to missing converts for ranges _range_convert(::Type{AbstractVector{T}}, a::AbstractRange{T}) where T = a _range_convert(::Type{AbstractVector{T}}, a::AbstractUnitRange) where T = convert(T,first(a)):convert(T,last(a)) _range_convert(::Type{AbstractVector{T}}, a::OneTo) where T = OneTo(convert(T, a.stop)) _range_convert(::Type{AbstractVector{T}}, a::AbstractRange) where T = convert(T,first(a)):step(a):convert(T,last(a)) _range_convert(::Type{AbstractVector{T}}, a::ZerosVector) where T = ZerosVector{T}(length(a)) # TODO: replacing with the following will support more general broadcasting. # function broadcasted(::DefaultArrayStyle{1}, ::typeof(*), a::AbstractFill, b::AbstractRange) # broadcast_shape(axes(a), axes(b)) # check axes # r1 = b[1] * getindex_value(a) # T = typeof(r1) # if length(b) == 1 # Need a fill, but for type stability use StepRangeLen # StepRangeLen{T}(r1, zero(T), length(a)) # else # StepRangeLen{T}(r1, convert(T, getindex_value(a) * step(b)), length(b)) # end # end # function broadcasted(::DefaultArrayStyle{1}, ::typeof(*), a::AbstractRange, b::AbstractFill) # broadcast_shape(axes(a), axes(b)) # check axes # r1 = a[1] * getindex_value(b) # T = typeof(r1) # if length(a) == 1 # Need a fill, but for type stability use StepRangeLen # StepRangeLen{T}(r1, zero(T), length(b)) # else # StepRangeLen{T}(r1, convert(T, step(a) * getindex_value(b)), length(a)) # end # end function broadcasted(::DefaultArrayStyle{1}, ::typeof(*), a::AbstractOnesVector, b::AbstractRange) broadcast_shape(axes(a), axes(b)) == axes(b) || throw(ArgumentError(LazyString("Cannot broadcast ", a, " and ", b, ". Convert ", b, " to a Vector first."))) TT = typeof(zero(eltype(a)) * zero(eltype(b))) return _range_convert(AbstractVector{TT}, b) end function broadcasted(::DefaultArrayStyle{1}, ::typeof(*), a::AbstractRange, b::AbstractOnesVector) broadcast_shape(axes(a), axes(b)) == axes(a) || throw(ArgumentError(LazyString("Cannot broadcast ", a, " and ", b, ". Convert ", b, " to a Vector first."))) TT = typeof(zero(eltype(a)) * zero(eltype(b))) return _range_convert(AbstractVector{TT}, a) end for op in (:+, :-) @eval begin function broadcasted(::DefaultArrayStyle{1}, ::typeof($op), a::AbstractVector, b::AbstractZerosVector) broadcast_shape(axes(a), axes(b)) == axes(a) || throw(ArgumentError(LazyString("Cannot broadcast ", a, " and ", b, ". Convert ", b, " to a Vector first."))) TT = typeof($op(zero(eltype(a)), zero(eltype(b)))) # Use `TT ∘ (+)` to fix AD issues with `broadcasted(TT, x)` eltype(a) === TT ? a : broadcasted(TT ∘ (+), a) end function broadcasted(::DefaultArrayStyle{1}, ::typeof($op), a::AbstractZerosVector, b::AbstractVector) broadcast_shape(axes(a), axes(b)) == axes(b) || throw(ArgumentError(LazyString("Cannot broadcast ", a, " and ", b, ". Convert ", a, " to a Vector first."))) TT = typeof($op(zero(eltype(a)), zero(eltype(b)))) $op === (+) && eltype(b) === TT ? b : broadcasted(TT ∘ ($op), b) end broadcasted(::DefaultArrayStyle{1}, ::typeof($op), a::AbstractFillVector, b::AbstractZerosVector) = Base.invoke(broadcasted, Tuple{DefaultArrayStyle, typeof($op), AbstractFill, AbstractFill}, DefaultArrayStyle{1}(), $op, a, b) broadcasted(::DefaultArrayStyle{1}, ::typeof($op), a::AbstractZerosVector, b::AbstractFillVector) = Base.invoke(broadcasted, Tuple{DefaultArrayStyle, typeof($op), AbstractFill, AbstractFill}, DefaultArrayStyle{1}(), $op, a, b) end end # Need to prevent array-valued fills from broadcasting over entry _broadcast_getindex_value(a::AbstractFill{<:Number}) = getindex_value(a) _broadcast_getindex_value(a::AbstractFill) = Ref(getindex_value(a)) function broadcasted(::DefaultArrayStyle{1}, ::typeof(*), a::AbstractFill, b::AbstractRange) broadcast_shape(axes(a), axes(b)) == axes(b) || throw(ArgumentError(LazyString("Cannot broadcast ", a, " and ", b, ". Convert ", b, " to a Vector first."))) return broadcasted(*, _broadcast_getindex_value(a), b) end function broadcasted(::DefaultArrayStyle{1}, ::typeof(*), a::AbstractRange, b::AbstractFill) broadcast_shape(axes(a), axes(b)) == axes(a) || throw(ArgumentError(LazyString("Cannot broadcast ", a, " and ", b, ". Convert ", b, " to a Vector first."))) return broadcasted(*, a, _broadcast_getindex_value(b)) end broadcasted(::DefaultArrayStyle{N}, op, r::AbstractFill{T,N}, x::Number) where {T,N} = broadcasted_fill(op, r, op(getindex_value(r),x), axes(r)) broadcasted(::DefaultArrayStyle{N}, op, x::Number, r::AbstractFill{T,N}) where {T,N} = broadcasted_fill(op, r, op(x, getindex_value(r)), axes(r)) broadcasted(::DefaultArrayStyle{N}, op, r::AbstractFill{T,N}, x::Ref) where {T,N} = broadcasted_fill(op, r, op(getindex_value(r),x[]), axes(r)) broadcasted(::DefaultArrayStyle{N}, op, x::Ref, r::AbstractFill{T,N}) where {T,N} = broadcasted_fill(op, r, op(x[], getindex_value(r)), axes(r)) # support AbstractFill .^ k broadcasted(::DefaultArrayStyle{N}, op::typeof(Base.literal_pow), ::Base.RefValue{typeof(^)}, r::AbstractFill{T,N}, ::Base.RefValue{Val{k}}) where {T,N,k} = broadcasted_fill(op, r, getindex_value(r)^k, axes(r)) broadcasted(::DefaultArrayStyle{N}, op::typeof(Base.literal_pow), ::Base.RefValue{typeof(^)}, r::AbstractOnes{T,N}, ::Base.RefValue{Val{k}}) where {T,N,k} = broadcasted_ones(op, r, T, axes(r)) broadcasted(::DefaultArrayStyle{N}, op::typeof(Base.literal_pow), ::Base.RefValue{typeof(^)}, r::AbstractZeros{T,N}, ::Base.RefValue{Val{0}}) where {T,N} = broadcasted_ones(op, r, T, axes(r)) broadcasted(::DefaultArrayStyle{N}, op::typeof(Base.literal_pow), ::Base.RefValue{typeof(^)}, r::AbstractZeros{T,N}, ::Base.RefValue{Val{k}}) where {T,N,k} = broadcasted_zeros(op, r, T, axes(r)) # supports structured broadcast if isdefined(LinearAlgebra, :fzero) LinearAlgebra.fzero(x::AbstractZeros) = zero(eltype(x)) end ================================================ FILE: src/oneelement.jl ================================================ """ OneElement(val, ind, axesorsize) <: AbstractArray Represents an array with the specified axes (if its a tuple of `AbstractUnitRange`s) or size (if its a tuple of `Integer`s), with a single entry set to `val` and all others equal to zero, specified by `ind``. """ struct OneElement{T,N,I,A} <: AbstractArray{T,N} val::T ind::I axes::A OneElement(val::T, ind::I, axes::A) where {T, I<:NTuple{N,Int}, A<:NTuple{N,AbstractUnitRange}} where {N} = new{T,N,I,A}(val, ind, axes) OneElement(val::T, ind::Tuple{}, axes::Tuple{}) where {T} = new{T,0,Tuple{},Tuple{}}(val, ind, axes) end const OneElementVector{T,I,A} = OneElement{T,1,I,A} const OneElementMatrix{T,I,A} = OneElement{T,2,I,A} const OneElementVecOrMat{T,I,A} = Union{OneElementVector{T,I,A}, OneElementMatrix{T,I,A}} OneElement(val, inds::NTuple{N,Int}, sz::NTuple{N,Integer}) where N = OneElement(val, inds, oneto.(sz)) """ OneElement(val, ind::Int, n::Integer) Creates a length `n` vector where the `ind` entry is equal to `val`, and all other entries are zero. """ OneElement(val, ind::Int, len::Integer) = OneElement(val, (ind,), (len,)) """ OneElement(ind::Int, n::Integer) Creates a length `n` vector where the `ind` entry is equal to `1`, and all other entries are zero. """ OneElement(inds::Int, sz::Integer) = OneElement(1, inds, sz) OneElement{T}(val, inds::NTuple{N,Int}, sz::NTuple{N,Integer}) where {T,N} = OneElement(convert(T,val), inds, oneto.(sz)) OneElement{T}(val, inds::Int, sz::Integer) where T = OneElement{T}(val, (inds,), (sz,)) """ OneElement{T}(ind::Int, n::Int) Creates a length `n` vector where the `ind` entry is equal to `one(T)`, and all other entries are zero. """ OneElement{T}(inds::Int, sz::Integer) where T = OneElement(one(T), inds, sz) Base.size(A::OneElement) = map(length, A.axes) Base.axes(A::OneElement) = A.axes Base.getindex(A::OneElement{T,0}) where {T} = getindex_value(A) Base.@propagate_inbounds function Base.getindex(A::OneElement{T,N}, kj::Vararg{Int,N}) where {T,N} @boundscheck checkbounds(A, kj...) ifelse(kj == A.ind, A.val, zero(T)) end const VectorInds = Union{AbstractUnitRange{<:Integer}, Integer} # no index is repeated for these indices const VectorIndsWithColon = Union{VectorInds, Colon} # retain the values from Ainds corresponding to the vector indices in inds _index_shape(Ainds, inds::Tuple{Integer, Vararg{Any}}) = _index_shape(Base.tail(Ainds), Base.tail(inds)) _index_shape(Ainds, inds::Tuple{AbstractVector, Vararg{Any}}) = (Ainds[1], _index_shape(Base.tail(Ainds), Base.tail(inds))...) _index_shape(::Tuple{}, ::Tuple{}) = () Base.@propagate_inbounds function Base.getindex(A::OneElement{T,N}, inds::Vararg{VectorIndsWithColon,N}) where {T,N} I = to_indices(A, inds) # handle Bool, and convert to compatible index types @boundscheck checkbounds(A, I...) shape = _index_shape(I, I) nzind = _index_shape(A.ind, I) .- first.(shape) .+ firstindex.(shape) containsval = all(in.(A.ind, I)) OneElement(getindex_value(A), containsval ? Int.(nzind) : Int.(lastindex.(shape,1)).+1, axes.(shape,1)) end """ nzind(A::OneElement{T,N}) -> CartesianIndex{N} Return the index where `A` contains a non-zero value. !!! note The indices are not guaranteed to lie within the valid index bounds for `A`, and if `FillArrays.nzind(A) ∉ CartesianIndices(A)` then `all(iszero, A)`. On the other hand, if `FillArrays.nzind(A) in CartesianIndices(A)` then `A[FillArrays.nzind(A)] == FillArrays.getindex_value(A)` # Examples ```jldoctest julia> A = OneElement(2, (1,2), (2,2)) 2×2 OneElement{Int64, 2, Tuple{Int64, Int64}, Tuple{Base.OneTo{Int64}, Base.OneTo{Int64}}}: ⋅ 2 ⋅ ⋅ julia> FillArrays.nzind(A) CartesianIndex(1, 2) julia> A[FillArrays.nzind(A)] 2 ``` """ nzind(f::OneElement) = CartesianIndex(f.ind) """ getindex_value(A::OneElement) Return the only non-zero value stored in `A`. !!! note If the index at which the value is stored doesn't lie within the valid indices of `A`, then this returns `zero(eltype(A))`. # Examples ```jldoctest julia> A = OneElement(2, 3) 3-element OneElement{Int64, 1, Tuple{Int64}, Tuple{Base.OneTo{Int64}}}: ⋅ 1 ⋅ julia> FillArrays.getindex_value(A) 1 ``` """ getindex_value(A::OneElement) = all(in.(A.ind, axes(A))) ? A.val : zero(eltype(A)) @inline function Base.isassigned(F::OneElement, i::Integer...) @boundscheck checkbounds(Bool, F, to_indices(F, i)...) || return false return true end Base.AbstractArray{T,N}(A::OneElement{<:Any,N}) where {T,N} = OneElement(T(A.val), A.ind, A.axes) Base.replace_in_print_matrix(o::OneElementVector, k::Integer, j::Integer, s::AbstractString) = o.ind == (k,) ? s : Base.replace_with_centered_mark(s) Base.replace_in_print_matrix(o::OneElementMatrix, k::Integer, j::Integer, s::AbstractString) = o.ind == (k,j) ? s : Base.replace_with_centered_mark(s) Base.@propagate_inbounds function Base.setindex(A::AbstractZeros{T,N}, v, kj::Vararg{Int,N}) where {T,N} @boundscheck checkbounds(A, kj...) OneElement(convert(T, v), kj, axes(A)) end zero(A::OneElement) = OneElement(zero(A.val), A.ind, A.axes) iszero(A::OneElement) = iszero(getindex_value(A)) function isone(A::OneElementMatrix) lenA = length(A) lenA == 0 && return true lenA > 1 && return false isone(getindex_value(A)) end -(O::OneElement) = OneElement(-O.val, O.ind, O.axes) *(x::OneElement, b::Number) = OneElement(x.val * b, x.ind, x.axes) *(b::Number, x::OneElement) = OneElement(b * x.val, x.ind, x.axes) /(x::OneElement, b::Number) = OneElement(x.val / b, x.ind, x.axes) \(b::Number, x::OneElement) = OneElement(b \ x.val, x.ind, x.axes) # matrix-vector and matrix-matrix multiplication # Fill and OneElement function *(A::OneElementMatrix, B::OneElementVecOrMat) check_matmul_sizes(A, B) valA = getindex_value(A) valB = getindex_value(B) val = valA * valB * (A.ind[2] == B.ind[1]) OneElement(val, (A.ind[1], B.ind[2:end]...), (axes(A,1), axes(B)[2:end]...)) end *(A::OneElementMatrix, x::AbstractZerosVector) = mult_zeros(A, x) function *(A::OneElementMatrix, B::AbstractFillVector) check_matmul_sizes(A, B) val = getindex_value(A) * getindex_value(B) OneElement(val, A.ind[1], size(A,1)) end # Special matrix types function *(A::OneElementMatrix, D::Diagonal) check_matmul_sizes(A, D) nzcol = A.ind[2] val = if nzcol in axes(D,1) A.val * D[nzcol, nzcol] else A.val * zero(eltype(D)) end OneElement(val, A.ind, size(A)) end function *(D::Diagonal, A::OneElementMatrix) check_matmul_sizes(D, A) nzrow = A.ind[1] val = if nzrow in axes(D,2) D[nzrow, nzrow] * A.val else zero(eltype(D)) * A.val end OneElement(val, A.ind, size(A)) end # Inplace multiplication # We use this for out overloads for _mul! for OneElement because its more efficient # due to how efficient 2 arg mul is when one or more of the args are OneElement function __mulonel!(C, A, B, alpha, beta) ABα = A * B * alpha if iszero(beta) C .= ABα else C .= ABα .+ C .* beta end return C end # These methods remove the ambituity in _mul!. This isn't strictly necessary, but this makes Aqua happy. function _mul!(C::AbstractVector, A::OneElementMatrix, B::OneElementVector, alpha, beta) __mulonel!(C, A, B, alpha, beta) end function _mul!(C::AbstractMatrix, A::OneElementMatrix, B::OneElementMatrix, alpha, beta) __mulonel!(C, A, B, alpha, beta) end function mul!(C::AbstractMatrix, A::OneElementMatrix, B::OneElementMatrix, alpha::Number, beta::Number) _mul!(C, A, B, alpha, beta) end function mul!(C::AbstractVector, A::OneElementMatrix, B::OneElementVector, alpha::Number, beta::Number) _mul!(C, A, B, alpha, beta) end @inline function __mul!(y, A::AbstractMatrix, x::OneElement, alpha, beta) xα = Ref(x.val * alpha) ind1 = x.ind[1] if iszero(beta) y .= view(A, :, ind1) .* xα else y .= view(A, :, ind1) .* xα .+ y .* beta end return y end function _mul!(y::AbstractVector, A::AbstractMatrix, x::OneElementVector, alpha, beta) check_matmul_sizes(y, A, x) if iszero(getindex_value(x)) mul!(y, A, Zeros{eltype(x)}(axes(x)), alpha, beta) return y end __mul!(y, A, x, alpha, beta) y end function _mul!(C::AbstractMatrix, A::AbstractMatrix, B::OneElementMatrix, alpha, beta) check_matmul_sizes(C, A, B) if iszero(getindex_value(B)) mul!(C, A, Zeros{eltype(B)}(axes(B)), alpha, beta) return C end nzrow, nzcol = B.ind if iszero(beta) C .= Ref(zero(eltype(C))) else view(C, :, 1:nzcol-1) .*= beta view(C, :, nzcol+1:size(C,2)) .*= beta end y = view(C, :, nzcol) __mul!(y, A, B, alpha, beta) C end function _mul!(C::AbstractMatrix, A::Diagonal, B::OneElementMatrix, alpha, beta) check_matmul_sizes(C, A, B) if iszero(getindex_value(B)) mul!(C, A, Zeros{eltype(B)}(axes(B)), alpha, beta) return C end nzrow, nzcol = B.ind ABα = A * B * alpha if iszero(beta) C .= Ref(zero(eltype(C))) C[nzrow, nzcol] = ABα[nzrow, nzcol] else view(C, :, 1:nzcol-1) .*= beta view(C, :, nzcol+1:size(C,2)) .*= beta y = view(C, :, nzcol) y .= view(ABα, :, nzcol) .+ y .* beta end C end function _mul!(C::AbstractMatrix, A::OneElementMatrix, B::AbstractMatrix, alpha, beta) check_matmul_sizes(C, A, B) if iszero(getindex_value(A)) mul!(C, Zeros{eltype(A)}(axes(A)), B, alpha, beta) return C end nzrow, nzcol = A.ind y = view(C, nzrow, :) Aval = A.val if iszero(beta) C .= Ref(zero(eltype(C))) y .= Ref(Aval) .* view(B, nzcol, :) .* alpha else view(C, 1:nzrow-1, :) .*= beta view(C, nzrow+1:size(C,1), :) .*= beta y .= Ref(Aval) .* view(B, nzcol, :) .* alpha .+ y .* beta end C end function _mul!(C::AbstractMatrix, A::OneElementMatrix, B::Diagonal, alpha, beta) check_matmul_sizes(C, A, B) if iszero(getindex_value(A)) mul!(C, Zeros{eltype(A)}(axes(A)), B, alpha, beta) return C end nzrow, nzcol = A.ind ABα = A * B * alpha if iszero(beta) C .= Ref(zero(eltype(C))) C[nzrow, nzcol] = ABα[nzrow, nzcol] else view(C, 1:nzrow-1, :) .*= beta view(C, nzrow+1:size(C,1), :) .*= beta y = view(C, nzrow, :) y .= view(ABα, nzrow, :) .+ y .* beta end C end function _mul!(C::AbstractVector, A::OneElementMatrix, B::AbstractVector, alpha, beta) check_matmul_sizes(C, A, B) if iszero(getindex_value(A)) mul!(C, Zeros{eltype(A)}(axes(A)), B, alpha, beta) return C end nzrow, nzcol = A.ind Aval = A.val if iszero(beta) C .= Ref(zero(eltype(C))) C[nzrow] = Aval * B[nzcol] * alpha else view(C, 1:nzrow-1) .*= beta view(C, nzrow+1:size(C,1)) .*= beta C[nzrow] = Aval * B[nzcol] * alpha + C[nzrow] * beta end C end for MT in (:StridedMatrix, :(Transpose{<:Any, <:StridedMatrix}), :(Adjoint{<:Any, <:StridedMatrix})) @eval function mul!(y::StridedVector, A::$MT, x::OneElementVector, alpha::Number, beta::Number) _mul!(y, A, x, alpha, beta) end end for MT in (:StridedMatrix, :(Transpose{<:Any, <:StridedMatrix}), :(Adjoint{<:Any, <:StridedMatrix}), :Diagonal) @eval function mul!(C::StridedMatrix, A::$MT, B::OneElementMatrix, alpha::Number, beta::Number) _mul!(C, A, B, alpha, beta) end @eval function mul!(C::StridedMatrix, A::OneElementMatrix, B::$MT, alpha::Number, beta::Number) _mul!(C, A, B, alpha, beta) end end function mul!(C::StridedVector, A::OneElementMatrix, B::StridedVector, alpha::Number, beta::Number) _mul!(C, A, B, alpha, beta) end function mul!(y::AbstractVector, A::AbstractFillMatrix, x::OneElementVector, alpha::Number, beta::Number) _mul!(y, A, x, alpha, beta) end function mul!(C::AbstractMatrix, A::AbstractFillMatrix, B::OneElementMatrix, alpha::Number, beta::Number) _mul!(C, A, B, alpha, beta) end function mul!(C::AbstractVector, A::OneElementMatrix, B::AbstractFillVector, alpha::Number, beta::Number) _mul!(C, A, B, alpha, beta) end function mul!(C::AbstractMatrix, A::OneElementMatrix, B::AbstractFillMatrix, alpha::Number, beta::Number) _mul!(C, A, B, alpha, beta) end # adjoint/transpose adjoint(A::OneElementMatrix) = OneElement(adjoint(A.val), reverse(A.ind), reverse(A.axes)) transpose(A::OneElementMatrix) = OneElement(transpose(A.val), reverse(A.ind), reverse(A.axes)) # isbanded function LinearAlgebra.isbanded(A::OneElementMatrix, kl::Integer, ku::Integer) iszero(getindex_value(A)) || kl <= A.ind[2] - A.ind[1] <= ku end # tril/triu function tril(A::OneElementMatrix, k::Integer=0) nzband = A.ind[2] - A.ind[1] OneElement(nzband > k ? zero(A.val) : A.val, A.ind, axes(A)) end function triu(A::OneElementMatrix, k::Integer=0) nzband = A.ind[2] - A.ind[1] OneElement(nzband < k ? zero(A.val) : A.val, A.ind, axes(A)) end # issymmetric issymmetric(O::OneElement) = axes(O,1) == axes(O,2) && isdiag(O) && issymmetric(getindex_value(O)) ishermitian(O::OneElement) = axes(O,1) == axes(O,2) && isdiag(O) && ishermitian(getindex_value(O)) # diag function diag(O::OneElementMatrix, k::Integer=0) Base.require_one_based_indexing(O) len = length(diagind(O, k)) ind = O.ind[2] - O.ind[1] == k ? (k >= 0 ? O.ind[2] - k : O.ind[1] + k) : len + 1 OneElement(getindex_value(O), ind, len) end # broadcast for f in (:abs, :abs2, :conj, :real, :imag) @eval function broadcasted(::DefaultArrayStyle{N}, ::typeof($f), r::OneElement{<:Any,N}) where {N} OneElement($f(r.val), r.ind, axes(r)) end end function broadcasted(::DefaultArrayStyle{N}, ::typeof(^), r::OneElement{<:Any,N}, x::Number) where {N} OneElement(r.val^x, r.ind, axes(r)) end function broadcasted(::DefaultArrayStyle{N}, ::typeof(*), r::OneElement{<:Any,N}, x::Number) where {N} OneElement(r.val*x, r.ind, axes(r)) end function broadcasted(::DefaultArrayStyle{N}, ::typeof(/), r::OneElement{<:Any,N}, x::Number) where {N} OneElement(r.val/x, r.ind, axes(r)) end function broadcasted(::DefaultArrayStyle{N}, ::typeof(\), x::Number, r::OneElement{<:Any,N}) where {N} OneElement(x \ r.val, r.ind, axes(r)) end # reshape function Base.reshape(A::OneElement, shape::Tuple{Vararg{Int}}) prod(shape) == length(A) || throw(DimensionMismatch(LazyString("new dimension ", shape, " must be consistent with array size ", length(A)))) if all(in.(A.ind, axes(A))) # we use the fact that the linear index of the non-zero value is preserved oldlinind = LinearIndices(A)[A.ind...] newcartind = CartesianIndices(shape)[oldlinind] else # arbitrarily set to some value outside the domain newcartind = shape .+ 1 end OneElement(A.val, Tuple(newcartind), shape) end #permute _permute(x, p) = ntuple(i -> x[p[i]], length(x)) permutedims(o::OneElementMatrix) = OneElement(o.val, reverse(o.ind), reverse(o.axes)) permutedims(o::OneElementVector) = reshape(o, (1, length(o))) permutedims(o::OneElement, dims) = OneElement(o.val, _permute(o.ind, dims), _permute(o.axes, dims)) # unique function unique(O::OneElement) v = getindex_value(O) len = iszero(v) ? 1 : min(2, length(O)) OneElement(getindex_value(O), len, len) end allunique(O::OneElement) = length(O) <= 1 || (length(O) < 3 && !iszero(getindex_value(O))) # show _maybesize(t::Tuple{Base.OneTo{Int}, Vararg{Base.OneTo{Int}}}) = size.(t,1) _maybesize(t) = t Base.show(io::IO, A::OneElement) = print(io, OneElement, "(", A.val, ", ", A.ind, ", ", _maybesize(axes(A)), ")") Base.show(io::IO, A::OneElement{<:Any,1,Tuple{Int},Tuple{Base.OneTo{Int}}}) = print(io, OneElement, "(", A.val, ", ", A.ind[1], ", ", size(A,1), ")") # mapreduce Base.sum(O::OneElement; dims=:, kw...) = _sum(O, dims; kw...) _sum(O::OneElement, ::Colon; kw...) = sum((getindex_value(O),); kw...) function _sum(O::OneElement, dims; kw...) v = _sum(O, :; kw...) ax = Base.reduced_indices(axes(O), dims) ind = ntuple(x -> x in dims ? first(ax[x]) + (O.ind[x] in axes(O)[x]) - 1 : O.ind[x], ndims(O)) OneElement(v, ind, ax) end ================================================ FILE: src/trues.jl ================================================ """ Trues = Ones{Bool, N, Axes} where {N, Axes} Lazy version of `trues` with axes. Typically created using `Trues(dims)` or `Trues(dims...)` # Example ```jldoctest julia> T = Trues(1,3) 1×3 Ones{Bool} julia> Array(T) 1×3 Matrix{Bool}: 1 1 1 ``` """ const Trues = Ones{Bool, N, Axes} where {N, Axes} """ Falses = Zeros{Bool, N, Axes} Lazy version of `falses` with axes. See also: [`Trues`](@ref) """ const Falses = Zeros{Bool, N, Axes} where {N, Axes} # y[mask] = x when mask isa Trues (cf y[:] = x) function Base.to_indices(A::AbstractArray{T,N}, inds, I::Tuple{Trues{N}}) where {T,N} @boundscheck axes(A) == axes(I[1]) || Base.throw_boundserror(A, I[1]) (vec(LinearIndices(A)),) end ================================================ FILE: test/aqua.jl ================================================ using Aqua using FillArrays using Test downstream_test = "--downstream_integration_test" in ARGS @testset "Project quality" begin Aqua.test_all(FillArrays; # https://github.com/JuliaArrays/FillArrays.jl/issues/105#issuecomment-1582516319 ambiguities=(; broken=true), stale_deps = !downstream_test, ) end ================================================ FILE: test/infinitearrays.jl ================================================ # Infinite Arrays implementation from # https://github.com/JuliaLang/julia/blob/master/test/testhelpers/InfiniteArrays.jl module InfiniteArrays using Infinities export OneToInf abstract type AbstractInfUnitRange{T<:Real} <: AbstractUnitRange{T} end Base.length(r::AbstractInfUnitRange) = ℵ₀ Base.size(r::AbstractInfUnitRange) = (ℵ₀,) Base.last(r::AbstractInfUnitRange) = ℵ₀ Base.axes(r::AbstractInfUnitRange) = (OneToInf(),) Base.IteratorSize(::Type{<:AbstractInfUnitRange}) = Base.IsInfinite() """ OneToInf(n) Define an `AbstractInfUnitRange` that behaves like `1:∞`, with the added distinction that the limits are guaranteed (by the type system) to be 1 and ∞. """ struct OneToInf{T<:Integer} <: AbstractInfUnitRange{T} end OneToInf() = OneToInf{Int}() Base.axes(r::OneToInf) = (r,) Base.first(r::OneToInf{T}) where {T} = oneunit(T) Base.oneto(::InfiniteCardinal{0}) = OneToInf() struct InfUnitRange{T<:Real} <: AbstractInfUnitRange{T} start::T end Base.first(r::InfUnitRange) = r.start InfUnitRange(a::InfUnitRange) = a InfUnitRange{T}(a::AbstractInfUnitRange) where T<:Real = InfUnitRange{T}(first(a)) InfUnitRange(a::AbstractInfUnitRange{T}) where T<:Real = InfUnitRange{T}(first(a)) Base.:(:)(start::T, stop::InfiniteCardinal{0}) where {T<:Integer} = InfUnitRange{T}(start) function getindex(v::InfUnitRange{T}, i::Integer) where T @boundscheck i > 0 || Base.throw_boundserror(v, i) convert(T, first(v) + i - 1) end end ================================================ FILE: test/runtests.jl ================================================ using FillArrays, LinearAlgebra, PDMats, SparseArrays, StaticArrays, ReverseDiff, Random, Test, Statistics, Quaternions import FillArrays: AbstractFill, RectDiagonal, SquareEye using Documenter DocMeta.setdocmeta!(FillArrays, :DocTestSetup, :(using FillArrays)) doctest(FillArrays; manual = false) include("aqua.jl") include("infinitearrays.jl") import .InfiniteArrays # we may use this instead of rand(n) to generate deterministic arrays oneton(T::Type, sz...) = reshape(T.(1:prod(sz)), sz) oneton(sz...) = oneton(Float64, sz...) stringmime(args...) = sprint(show, args...) @testset "fill array constructors and convert" begin for (Typ, funcs) in ((Zeros, zeros), (Ones, ones)) @test Typ((-1,5)) == Typ((0,5)) @test Typ(5) isa AbstractVector{Float64} @test Typ(5,5) isa AbstractMatrix{Float64} @test Typ(5) == Typ((5,)) @test Typ(5,5) == Typ((5,5)) @test eltype(Typ(5,5)) == Float64 for T in (Int, Float64) Z = Typ{T}(5) @test Typ(T, 5) ≡ Z @test eltype(Z) == T @test Array(Z) == funcs(T,5) @test Array{T}(Z) == funcs(T,5) @test Array{T,1}(Z) == funcs(T,5) @test convert(AbstractArray,Z) ≡ Z @test convert(AbstractArray{T},Z) ≡ AbstractArray{T}(Z) ≡ Z @test convert(AbstractVector{T},Z) ≡ AbstractVector{T}(Z) ≡ Z @test convert(AbstractFill{T},Z) ≡ AbstractFill{T}(Z) ≡ Z @test Typ{T,1}(2ones(T,5)) == Z @test Typ{T}(2ones(T,5)) == Z @test Typ(2ones(T,5)) == Z Z = Typ{T}(5, 5) @test Typ(T, 5, 5) ≡ Z @test eltype(Z) == T @test Array(Z) == funcs(T,5,5) @test Array{T}(Z) == funcs(T,5,5) @test Array{T,2}(Z) == funcs(T,5,5) @test convert(AbstractArray,Z) ≡ convert(AbstractFill,Z) ≡ Z @test convert(AbstractArray{T},Z) ≡ convert(AbstractFill{T},Z) ≡ AbstractArray{T}(Z) ≡ Z @test convert(AbstractMatrix{T},Z) ≡ convert(AbstractFill{T,2},Z) ≡ AbstractMatrix{T}(Z) ≡ Z @test_throws Exception convert(Fill{Float64}, [1,1,2]) @test_throws Exception convert(Fill, []) @test convert(Fill{Float64}, [1,1,1]) ≡ Fill(1.0, 3) @test convert(Fill, Float64[1,1,1]) ≡ Fill(1.0, 3) @test convert(Fill{Float64}, Fill(1.0,2)) ≡ Fill(1.0, 2) # was ambiguous @test convert(Fill{Int}, Ones(20)) ≡ Fill(1, 20) @test convert(Fill{Int,1}, Ones(20)) ≡ Fill(1, 20) @test convert(Fill{Int,1,Tuple{Base.OneTo{Int}}}, Ones(20)) ≡ Fill(1, 20) @test convert(AbstractFill{Int}, Ones(20)) ≡ AbstractFill{Int}(Ones(20)) ≡ Ones{Int}(20) @test convert(AbstractFill{Int,1}, Ones(20)) ≡ AbstractFill{Int,1}(Ones(20)) ≡ Ones{Int}(20) @test convert(AbstractFill{Int,1,Tuple{Base.OneTo{Int}}}, Ones(20)) ≡ AbstractFill{Int,1,Tuple{Base.OneTo{Int}}}(Ones(20)) ≡ Ones{Int}(20) @test Typ{T,2}(2ones(T,5,5)) ≡ Typ{T}(5,5) @test Typ{T}(2ones(T,5,5)) ≡ Typ{T}(5,5) @test Typ(2ones(T,5,5)) ≡ Typ{T}(5,5) z = Typ{T}()[] @test convert(Typ, Fill(z,2)) === Typ{T}(2) z = Typ{Int8}()[] @test convert(Typ{T}, Fill(z,2)) === Typ{T}(2) @test convert(Typ{T,1}, Fill(z,2)) === Typ{T}(2) @test convert(Typ{T,1,Tuple{Base.OneTo{Int}}}, Fill(z,2)) === Typ{T}(2) @test_throws ArgumentError convert(Typ, Fill(2,2)) @test Typ(Z) ≡ Typ{T}(Z) ≡ Typ{T,2}(Z) ≡ typeof(Z)(Z) ≡ Z @test AbstractArray{Float32}(Z) ≡ Typ{Float32}(5,5) @test AbstractArray{Float32,2}(Z) ≡ Typ{Float32}(5,5) end end @test Fill(1) ≡ Fill{Int}(1) ≡ Fill{Int,0}(1) ≡ Fill{Int,0,Tuple{}}(1,()) @test Fill(1,(-1,5)) ≡ Fill(1,(0,5)) @test Fill(1.0,5) isa AbstractVector{Float64} @test Fill(1.0,5,5) isa AbstractMatrix{Float64} @test Fill(1,5) ≡ Fill(1,(5,)) @test Fill(1,5,5) ≡ Fill(1,(5,5)) @test eltype(Fill(1.0,5,5)) == Float64 @test Matrix{Float64}(Zeros{ComplexF64}(10,10)) == zeros(10,10) @test_throws InexactError Matrix{Float64}(Fill(1.0+1.0im,10,10)) for T in (Int, Float64) F = Fill{T, 0}(2) @test size(F) == () @test F[] === T(2) F = Fill{T}(1, 5) @test eltype(F) == T @test Array(F) == fill(one(T),5) @test Array{T}(F) == fill(one(T),5) @test Array{T,1}(F) == fill(one(T),5) F = Fill{T}(1, 5, 5) @test eltype(F) == T @test Array(F) == fill(one(T),5,5) @test Array{T}(F) == fill(one(T),5,5) @test Array{T,2}(F) == fill(one(T),5,5) @test convert(AbstractArray,F) ≡ F @test convert(AbstractArray{T},F) ≡ AbstractArray{T}(F) ≡ F @test convert(AbstractMatrix{T},F) ≡ AbstractMatrix{T}(F) ≡ F @test convert(AbstractArray{Float32},F) ≡ AbstractArray{Float32}(F) ≡ Fill{Float32}(one(Float32),5,5) @test convert(AbstractMatrix{Float32},F) ≡ AbstractMatrix{Float32}(F) ≡ Fill{Float32}(one(Float32),5,5) @test Fill{T}(F) ≡ Fill{T,2}(F) ≡ typeof(F)(F) ≡ F show(devnull, MIME("text/plain"), F) # for codecov end @test Eye(5) isa Diagonal{Float64} @test SquareEye(5) isa Diagonal{Float64} @test Eye(5) == Eye{Float64}(5) == SquareEye(5) == SquareEye{Float64}(5) @test Eye(5,6) == Eye{Float64}(5,6) @test Eye(Ones(5,6)) == Eye{Float64}(5,6) @test eltype(Eye(5)) == Float64 @test eltype(Eye(5,6)) == Float64 @test Eye((Base.OneTo(5),)) ≡ SquareEye((Base.OneTo(5),)) ≡ Eye(5) @test Eye((Base.OneTo(5),Base.OneTo(6))) ≡ Eye(5,6) for T in (Int, Float64) E = Eye{T}(5) M = Matrix{T}(I, 5, 5) @test eltype(E) == T @test Array(E) == M @test Array{T}(E) == M @test Array{T,2}(E) == M @test convert(AbstractArray,E) === E @test convert(AbstractArray{T},E) === E @test convert(AbstractMatrix{T},E) === E @test AbstractArray{Float32}(E) == Eye{Float32}(5) @test AbstractArray{Float32}(E) == Eye{Float32}(5, 5) @test Eye{T}(randn(4,5)) ≡ Eye{T}(4,5) ≡ Eye{T}((Base.OneTo(4),Base.OneTo(5))) @test Eye{T}((Base.OneTo(5),)) ≡ SquareEye{T}((Base.OneTo(5),)) ≡ Eye{T}(5) end @testset "Bool should change type" begin x = Fill(true,5) y = x + x @test y isa Fill{Int,1} @test y[1] == 2 x = Ones{Bool}(5) y = x + x @test y isa Fill{Int,1} @test y[1] == 2 @test x + Zeros{Bool}(5) ≡ Ones{Int}(5) @test x - Zeros{Bool}(5) ≡ Ones{Int}(5) @test Zeros{Bool}(5) + x ≡ Ones{Int}(5) @test -x ≡ Fill(-1,5) end @testset "copy should return Fill" begin x = Fill(1.0,10) @test copy(x) ≡ x x = Zeros(10) @test copy(x) ≡ x x = Fill([1.,2.],10) @test copy(x) == x @test copy(x) === x # because isbits(x) @test copy(x) isa Fill @test copy(Fill(:a, 4)) == fill(:a, 4) # FillArrays#63 end @testset "vec" begin @test vec(Ones{Int}(5,10)) ≡ Ones{Int}(50) @test vec(Zeros{Int}(5,10)) ≡ Zeros{Int}(50) @test vec(Zeros{Int}(5,10,20)) ≡ Zeros{Int}(1000) @test vec(Fill(1,5,10)) ≡ Fill(1,50) end @testset "in" begin for T in [Zeros, Ones, Fill, Trues, Falses] A = T(4, 4) @test FillArrays.getindex_value(A) in A @test !(FillArrays.getindex_value(A) + 1 in A) end A = FillArrays.RectDiagonal([1, 2, 3]) @test 3 in A @test 0 in A @test !(4 in A) A = FillArrays.RectDiagonal([1]) @test 1 in A @test !(0 in A) A = FillArrays.RectDiagonal([2], (1:1, 1:4)) @test 2 in A @test 0 in A @test !(1 in A) @test !(Zeros(1,1) in A) A = FillArrays.RectDiagonal(Int[]) @test !(0 in A) A = FillArrays.RectDiagonal(Int[], (1:0, 1:4)) @test !(0 in A) end @testset "promotion" begin Z = Zeros{Int}(5) Zf = Zeros(5) @test promote_type(typeof(Z), typeof(Zf)) == typeof(Zf) O = Ones{Int}(5) Of = Ones{Float64}(5) @test promote_type(typeof(O), typeof(Of)) == typeof(Of) @test [Z,O] isa Vector{Fill{Int,1,Tuple{Base.OneTo{Int}}}} @test [Z,Of] isa Vector{Fill{Float64,1,Tuple{Base.OneTo{Int}}}} @test [O,O] isa Vector{Ones{Int,1,Tuple{Base.OneTo{Int}}}} @test [O,Of] isa Vector{Ones{Float64,1,Tuple{Base.OneTo{Int}}}} @test [Z,Zf] isa Vector{Zeros{Float64,1,Tuple{Base.OneTo{Int}}}} @test convert(Ones{Int}, Of) ≡ convert(Ones{Int,1}, Of) ≡ convert(typeof(O), Of) ≡ O @test convert(Zeros{Int}, Zf) ≡ convert(Zeros{Int,1}, Zf) ≡ convert(typeof(Z), Zf) ≡ Z F = Fill(1, 2) Ff = Fill(1.0, 2) @test promote_type(typeof(F), typeof(Ff)) == typeof(Ff) @test_throws MethodError convert(Zeros{SVector{2,Int}}, Zf) end end @testset "interface" begin struct Twos{T,N} <: FillArrays.AbstractFill{T,N,NTuple{N,Base.OneTo{Int}}} sz :: NTuple{N,Int} end Twos{T}(sz::NTuple{N,Int}) where {T,N} = Twos{T,N}(sz) Twos{T}(sz::Vararg{Int,N}) where {T,N} = Twos{T,N}(sz) Base.size(A::Twos) = A.sz FillArrays.getindex_value(A::Twos{T}) where {T} = oneunit(T) + oneunit(T) @testset "broadcasting ambiguities" begin A = Twos{Int}(3) B = Zeros{Int}(size(A)) @test A .* B === B @test B .* A === B @test B ./ A === Zeros{Float64}(size(A)) @test A .\ B === Zeros{Float64}(size(A)) @test A ./ B === Fill(Inf, size(A)) end end @testset "isassigned" begin for f in (Fill("", 3, 4), Zeros(3,4), Ones(3,4)) @test !isassigned(f, 0, 0) @test isassigned(f, 2, 2) @test !isassigned(f, 10, 10) @test_throws ArgumentError isassigned(f, true) end end @testset "indexing" begin A = Fill(3.0,5) @test A[1:3] ≡ Fill(3.0,3) @test A[1:3,1:1] ≡ Fill(3.0,3,1) @test_throws BoundsError A[1:3,2] @test_throws BoundsError A[1:26] @test A[[true, false, true, false, false]] ≡ Fill(3.0, 2) A = Fill(3.0, 2, 2) @test A[[true true; true false]] ≡ Fill(3.0, 3) @test_throws BoundsError A[[true, false]] A = Ones{Int}(5,5) @test A[1:3] ≡ Ones{Int}(3) @test A[1:3,1:2] ≡ Ones{Int}(3,2) @test A[1:3,2] ≡ Ones{Int}(3) @test_throws BoundsError A[1:26] A = Ones{Int}(2,2) @test A[[true false; true false]] ≡ Ones{Int}(2) @test A[[true, false, true, false]] ≡ Ones{Int}(2) @test_throws BoundsError A[[true false false; true false false]] A = Zeros{Int}(5,5) @test A[1:3] ≡ Zeros{Int}(3) @test A[1:3,1:2] ≡ Zeros{Int}(3,2) @test A[1:3,2] ≡ Zeros{Int}(3) @test_throws BoundsError A[1:26] A = Zeros{Int}(2,2) @test A[[true false; true false]] ≡ Zeros{Int}(2) @test A[[true, false, true, false]] ≡ Zeros{Int}(2) @test_throws BoundsError A[[true false false; true false false]] @testset "colon" begin @test Ones(2)[:] ≡ Ones(2)[Base.Slice(Base.OneTo(2))] ≡ Ones(2) @test Zeros(2)[:] ≡ Zeros(2)[Base.Slice(Base.OneTo(2))] ≡ Zeros(2) @test Fill(3.0,2)[:] ≡ Fill(3.0,2)[Base.Slice(Base.OneTo(2))] ≡ Fill(3.0,2) @test Ones(2,2)[:,:] ≡ Ones(2,2)[Base.Slice(Base.OneTo(2)),Base.Slice(Base.OneTo(2))] ≡ Ones(2,2) @test Zeros(2,2)[:,:] ≡ Zeros(2)[Base.Slice(Base.OneTo(2)),Base.Slice(Base.OneTo(2))] ≡ Zeros(2,2) @test Fill(3.0,2,2)[:,:] ≡ Fill(3.0,2,2)[Base.Slice(Base.OneTo(2)),Base.Slice(Base.OneTo(2))] ≡ Fill(3.0,2,2) end @testset "mixed integer / vector /colon" begin a = Fill(2.0,5) z = Zeros(5) @test a[1:5] ≡ a[:] ≡ a @test z[1:5] ≡ z[:] ≡ z A = Fill(2.0,5,6) Z = Zeros(5,6) @test A[:,1] ≡ A[1:5,1] ≡ Fill(2.0,5) @test A[1,:] ≡ A[1,1:6] ≡ Fill(2.0,6) @test A[:,:] ≡ A[1:5,1:6] ≡ A[1:5,:] ≡ A[:,1:6] ≡ A @test Z[:,1] ≡ Z[1:5,1] ≡ Zeros(5) @test Z[1,:] ≡ Z[1,1:6] ≡ Zeros(6) @test Z[:,:] ≡ Z[1:5,1:6] ≡ Z[1:5,:] ≡ Z[:,1:6] ≡ Z A = Fill(2.0,5,6,7) Z = Zeros(5,6,7) @test A[:,1,1] ≡ A[1:5,1,1] ≡ Fill(2.0,5) @test A[1,:,1] ≡ A[1,1:6,1] ≡ Fill(2.0,6) @test A[:,:,:] ≡ A[1:5,1:6,1:7] ≡ A[1:5,:,1:7] ≡ A[:,1:6,1:7] ≡ A end @testset "StepRangeLen convert" begin for (z,s) in ((Zeros{Int}(5), StepRangeLen(0, 0, 5)), (Ones{Int}(5), StepRangeLen(1, 0, 5)), (Fill(2,5), StepRangeLen(2, 0, 5))) @test s == z @test StepRangeLen(z) ≡ convert(StepRangeLen, z) ≡ convert(StepRangeLen{Int}, z) ≡ convert(typeof(s), z) ≡ convert(AbstractRange, z) ≡ s end end end @testset "RectDiagonal" begin data = 1:3 expected_size = (5, 3) expected_axes = Base.OneTo.(expected_size) expected_matrix = [1 0 0; 0 2 0; 0 0 3; 0 0 0; 0 0 0] expected = RectDiagonal{Int, UnitRange{Int}}(data, expected_axes) @test axes(expected) == expected_axes @test size(expected) == expected_size @test (axes(expected, 1), axes(expected, 2)) == expected_axes @test (size(expected, 1), size(expected, 2)) == expected_size @test expected == expected_matrix @test Matrix(expected) == expected_matrix @test expected[:, 2] == expected_matrix[:, 2] @test expected[2, :] == expected_matrix[2, :] @test expected[5, :] == expected_matrix[5, :] for Typ in (RectDiagonal, RectDiagonal{Int}, RectDiagonal{Int, UnitRange{Int}}) @test Typ(data) == expected[1:3, 1:3] @test Typ(data, expected_axes) == expected @test Typ(data, expected_axes...) == expected @test Typ(data, expected_size) == expected @test Typ(data, expected_size...) == expected end @test diag(expected) === expected.diag mut = RectDiagonal(collect(data), expected_axes) @test mut == expected @test mut == expected_matrix mut[1, 1] = 5 @test mut[1] == 5 @test diag(mut) == [5, 2, 3] mut[2, 1] = 0 @test_throws ArgumentError mut[2, 1] = 9 D = RectDiagonal([1.,2.], (Base.OneTo(3),Base.OneTo(2))) @test stringmime("text/plain", D) == "3×2 RectDiagonal{Float64, Vector{Float64}, Tuple{Base.OneTo{$Int}, Base.OneTo{$Int}}}:\n 1.0 ⋅ \n ⋅ 2.0\n ⋅ ⋅ " end # Check that all pair-wise combinations of + / - elements of As and Bs yield the correct # type, and produce numerically correct results. as_array(x::AbstractArray) = Array(x) as_array(x::UniformScaling) = x isapprox_or_undef(a::Number, b::Number) = (a ≈ b) || isequal(a, b) isapprox_or_undef(a, b) = all(((x,y),) -> isapprox_or_undef(x,y), zip(a, b)) function test_addition_subtraction_dot(As, Bs, Tout::Type) for A in As, B in Bs @testset "$(typeof(A)) and $(typeof(B))" begin @test @inferred(A + B) isa Tout{promote_type(eltype(A), eltype(B))} @test isapprox_or_undef(as_array(A + B), as_array(A) + as_array(B)) @test @inferred(A - B) isa Tout{promote_type(eltype(A), eltype(B))} @test isapprox_or_undef(as_array(A - B), as_array(A) - as_array(B)) @test @inferred(B + A) isa Tout{promote_type(eltype(B), eltype(A))} @test isapprox_or_undef(as_array(B + A), as_array(B) + as_array(A)) @test @inferred(B - A) isa Tout{promote_type(eltype(B), eltype(A))} @test isapprox_or_undef(as_array(B - A), as_array(B) - as_array(A)) d1 = dot(A, B) d2 = dot(as_array(A), as_array(B)) d3 = dot(B, A) d4 = dot(as_array(B), as_array(A)) @test d1 ≈ d2 || d1 ≡ d2 @test d3 ≈ d4 || d3 ≡ d4 end end end # Check that all permutations of + / - throw a `DimensionMismatch` exception. function test_addition_and_subtraction_dim_mismatch(a, b) @testset "$(typeof(a)) ± $(typeof(b))" begin @test_throws DimensionMismatch a + b @test_throws DimensionMismatch a - b @test_throws DimensionMismatch b + a @test_throws DimensionMismatch b - a end end @testset "FillArray addition and subtraction" begin test_addition_and_subtraction_dim_mismatch(Zeros(5), Zeros(6)) test_addition_and_subtraction_dim_mismatch(Zeros(5), Zeros{Int}(6)) test_addition_and_subtraction_dim_mismatch(Zeros(5), Zeros(6,6)) test_addition_and_subtraction_dim_mismatch(Zeros(5), Zeros{Int}(6,5)) # Construct FillArray for repeated use. rng = MersenneTwister(123456) A_fill, B_fill = Fill(randn(rng, Float64), 5), Fill(4, 5) # Unary +/- constructs a new FillArray. @test +A_fill === A_fill @test -A_fill === Fill(-A_fill.value, 5) # FillArray +/- FillArray should construct a new FillArray. test_addition_subtraction_dot((A_fill, B_fill), (A_fill, B_fill), Fill) test_addition_and_subtraction_dim_mismatch(A_fill, Fill(randn(rng), 5, 2)) # FillArray + Array (etc) should construct a new Array using `getindex`. B_dense = (randn(rng, 5), [5, 4, 3, 2, 1], fill(Inf, 5), fill(NaN, 5)) test_addition_subtraction_dot((A_fill, B_fill), B_dense, Array) test_addition_and_subtraction_dim_mismatch(A_fill, randn(rng, 5, 2)) # FillArray + StepLenRange / UnitRange (etc) should yield an AbstractRange. A_ur, B_ur = 1.0:5.0, 6:10 test_addition_subtraction_dot((A_fill, B_fill), (A_ur, B_ur), AbstractRange) test_addition_and_subtraction_dim_mismatch(A_fill, 1.0:6.0) test_addition_and_subtraction_dim_mismatch(A_fill, 5:10) # FillArray + UniformScaling should yield a Matrix in general As_fill_square = (Fill(randn(rng, Float64), 3, 3), Fill(5, 4, 4)) Bs_us = (UniformScaling(2.3), UniformScaling(3)) test_addition_subtraction_dot(As_fill_square, Bs_us, Matrix) As_fill_nonsquare = (Fill(randn(rng, Float64), 3, 2), Fill(5, 3, 4)) for A in As_fill_nonsquare, B in Bs_us test_addition_and_subtraction_dim_mismatch(A, B) end # FillArray + StaticArray should not have ambiguities A_svec, B_svec = SVector{5}(rand(5)), SVector(1, 2, 3, 4, 5) test_addition_subtraction_dot((A_fill, B_fill, Zeros(5)), (A_svec, B_svec), SVector{5}) # Issue #224 A_matmat, B_matmat = Fill(rand(3,3),5), [rand(3,3) for n=1:5] test_addition_subtraction_dot((A_matmat,), (A_matmat,), Fill) test_addition_subtraction_dot((B_matmat,), (A_matmat,), Vector) # Optimizations for Zeros and RectOrDiagonal{<:Any, <:AbstractFill} As_special_square = ( Zeros(3, 3), Zeros{Int}(4, 4), Eye(3), Eye{Int}(4), Eye(3, 3), Eye{Int}(4, 4), Diagonal(Fill(randn(rng, Float64), 3)), Diagonal(Fill(3, 4)), RectDiagonal(Fill(randn(rng, Float64), 3), 3, 3), RectDiagonal(Fill(3, 4), 4, 4) ) DiagonalAbstractFill{T} = Diagonal{T, <:AbstractFill{T, 1}} test_addition_subtraction_dot(As_special_square, Bs_us, DiagonalAbstractFill) As_special_nonsquare = ( Zeros(3, 2), Zeros{Int}(3, 4), Eye(3, 2), Eye{Int}(3, 4), RectDiagonal(Fill(randn(rng, Float64), 2), 3, 2), RectDiagonal(Fill(3, 3), 3, 4) ) for A in As_special_nonsquare, B in Bs_us test_addition_and_subtraction_dim_mismatch(A, B) end @testset "Zeros" begin As = ([1,2], Float64[1,2], Int8[1,2], ComplexF16[2,4]) Zs = (TZ -> Zeros{TZ}(2)).((Int, Float64, Int8, ComplexF64)) test_addition_subtraction_dot(As, Zs, Vector) for A in As, Z in (TZ -> Zeros{TZ}(3)).((Int, Float64, Int8, ComplexF64)) test_addition_and_subtraction_dim_mismatch(A, Z) end As = (@SArray([1,2]), @SArray(Float64[1,2]), @SArray(Int8[1,2]), @SArray(ComplexF16[2,4])) test_addition_subtraction_dot(As, Zs, SVector{2}) for A in As, Z in (TZ -> Zeros{TZ}(3)).((Int, Float64, Int8, ComplexF64)) test_addition_and_subtraction_dim_mismatch(A, Z) end # Zeros should act as an additive identity # Arbitrary AbstractMatrix D = Diagonal([1, 1]) Z = Zeros(2, 2) @test D + Z isa Diagonal @test D + Z == D @test D - Z == D @test Z - D == -D @test Z + Z isa Zeros @test Z + Z == Z @test Z - Z == Z end end @testset "Other matrix types" begin @test Diagonal(Zeros(5)) == Diagonal(zeros(5)) @test Diagonal(Zeros(8,5)) == Diagonal(zeros(5)) @test convert(Diagonal, Zeros(5,5)) == Diagonal(zeros(5)) @test_throws DimensionMismatch convert(Diagonal, Zeros(8,5)) @test convert(Diagonal{Int}, Zeros(5,5)) == Diagonal(zeros(Int,5)) @test_throws DimensionMismatch convert(Diagonal{Int}, Zeros(8,5)) @test Diagonal(Eye(8,5)) == Diagonal(ones(5)) @test convert(Diagonal, Eye(5)) == Diagonal(ones(5)) @test convert(Diagonal{Int}, Eye(5)) == Diagonal(ones(Int,5)) for E in (Eye(2,4), Eye(3)) M = collect(E) for i in -5:5 @test diag(E, i) == diag(M, i) end end end @testset "one" begin @testset for A in Any[Eye(4), Zeros(4,4), Ones(4,4), Fill(3,4,4)] B = one(A) @test B * A == A * B == A end @test_throws ArgumentError one(Ones(3,4)) @test_throws ArgumentError one(Ones((3:5,4:5))) end @testset "Sparse vectors and matrices" begin @test SparseVector(Zeros(5)) == SparseVector{Int}(Zeros(5)) == SparseVector{Float64}(Zeros(5)) == SparseVector{Float64,Int}(Zeros(5)) == convert(AbstractSparseArray,Zeros(5)) == convert(AbstractSparseVector,Zeros(5)) == convert(AbstractSparseArray{Float64},Zeros(5)) == convert(AbstractSparseVector{Float64},Zeros(5)) == convert(AbstractSparseVector{Float64,Int},Zeros(5)) == spzeros(5) for (Mat, SMat) in ((Zeros(5,5), spzeros(5,5)), (Zeros(6,5), spzeros(6,5)), (Eye(5), sparse(I,5,5)), (Eye(6,5), sparse(I,6,5))) @test SparseMatrixCSC(Mat) == SparseMatrixCSC{Int}(Mat) == SparseMatrixCSC{Float64}(Mat) == SparseMatrixCSC{Float64,Int}(Mat) == convert(AbstractSparseArray,Mat) == convert(AbstractSparseMatrix,Mat) == convert(AbstractSparseArray{Float64},Mat) == convert(AbstractSparseArray{Float64,Int},Mat) == convert(AbstractSparseMatrix{Float64},Mat) == convert(AbstractSparseMatrix{Float64,Int},Mat) == SMat end function testsparsediag(E) S = @inferred SparseMatrixCSC(E) @test S == E S = @inferred SparseMatrixCSC{Float64}(E) @test S == E @test S isa SparseMatrixCSC{Float64} @test convert(SparseMatrixCSC{Float64}, E) == S S = @inferred SparseMatrixCSC{Float64,Int32}(E) @test S == E @test S isa SparseMatrixCSC{Float64,Int32} @test convert(SparseMatrixCSC{Float64,Int32}, E) == S end for f in (Fill(Int8(4),3), Ones{Int8}(3), Zeros{Int8}(3)) E = Diagonal(f) testsparsediag(E) for sz in ((3,6), (6,3), (3,3)) E = RectDiagonal(f, sz) testsparsediag(E) end end end @testset "==" begin @test Zeros(5,4) == Fill(0,5,4) @test Zeros(5,4) ≠ Zeros(3) @test Ones(5,4) == Fill(1,5,4) end @testset "Rank" begin @test rank(Zeros(5,4)) == 0 @test rank(Ones(5,4)) == 1 @test rank(Fill(2,5,4)) == 1 @test rank(Fill(0,5,4)) == 0 @test rank(Eye(2)) == 2 end @testset "ishermitian" begin @testset for el in (2, 3+0im, 4+5im, [1 2; 3 4], fill(2, 2, 2)), size in [(3,3), (3,4), (0,0), (0,1)] @test issymmetric(Fill(el, size...)) == issymmetric(fill(el, size...)) @test ishermitian(Fill(el, size...)) == ishermitian(fill(el, size...)) end end @testset "BigInt indices" begin for A in (Zeros(BigInt(100)), Ones(BigInt(100)), Fill(2, BigInt(100))) @test length(A) isa BigInt @test axes(A) == tuple(Base.OneTo{BigInt}(BigInt(100))) @test size(A) isa Tuple{BigInt} end for A in (Eye(BigInt(100)), Eye(BigInt(100), BigInt(100))) @test length(A) isa BigInt @test axes(A) == tuple(Base.OneTo{BigInt}(BigInt(100)),Base.OneTo{BigInt}(BigInt(100))) @test size(A) isa Tuple{BigInt,BigInt} end for A in (Zeros(BigInt(10), 10), Ones(BigInt(10), 10), Fill(2.0, (BigInt(10), 10)), Eye(BigInt(10), 8)) @test size(A) isa Tuple{BigInt,Int} end end @testset "IndexStyle" begin @test IndexStyle(Zeros(5,5)) == IndexStyle(typeof(Zeros(5,5))) == IndexLinear() end @testset "Identities" begin @test Zeros(3,4) * randn(4,5) === randn(3,4) * Zeros(4,5) === Zeros(3, 5) @test_throws DimensionMismatch randn(3,4) * Zeros(3, 3) @test eltype(Zeros{Int}(3,4) * fill(1, 4, 5)) == Int @test eltype(Zeros{Int}(3,4) * fill(3.4, 4, 5)) == Float64 @test Zeros(3, 4) * randn(4) == Zeros(3, 4) * Zeros(4) == Zeros(3) @test Zeros(3, 4) * Zeros(4, 5) === Zeros(3, 5) @test_throws MethodError [1,2,3]*Zeros(1) # Not defined for [1,2,3]*[0] either @test [1,2,3]*Zeros(1,3) ≡ Zeros(3,3) @test_throws MethodError [1,2,3]*Zeros(3) # Not defined for [1,2,3]*[0,0,0] either @testset "Matrix multiplication with array elements" begin x = [1 2; 3 4] z = zero(SVector{2,Int}) ZV = Zeros{typeof(z)}(2) A = Fill(x, 3, 2) * ZV @test A isa Fill @test size(A) == (3,) @test A[1] == x * z @test_throws DimensionMismatch Fill(x, 1, 1) * ZV @test_throws DimensionMismatch Fill(oneton(1,1), 1, length(ZV)) * ZV @test_throws DimensionMismatch Ones(SMatrix{3,3,Int,9},2) * Ones(SMatrix{2,2,Int,4},1,2) end @testset "Check multiplication by Adjoint vectors works as expected." begin @test @inferred(oneton(4, 3)' * Zeros(4)) ≡ Zeros(3) @test @inferred(oneton(4)' * Zeros(4)) ≡ @inferred(transpose(oneton(4)) * Zeros(4)) == 0.0 @test [1, 2, 3]' * Zeros{Int}(3) ≡ zero(Int) @test [SVector(1,2)', SVector(2,3)', SVector(3,4)']' * Zeros{Int}(3) === SVector(0,0) @test_throws DimensionMismatch oneton(4)' * Zeros(3) @test Zeros(5)' * oneton(5,3) ≡ Zeros(5)'*Zeros(5,3) ≡ Zeros(5)'*Ones(5,3) ≡ Zeros(3)' @test abs(Zeros(5)' * oneton(5)) == abs(Zeros(5)' * Zeros(5)) ≡ abs(Zeros(5)' * Ones(5)) == 0.0 @test Zeros(5) * Zeros(6)' ≡ Zeros(5,1) * Zeros(6)' ≡ Zeros(5,6) @test oneton(5) * Zeros(6)' ≡ oneton(5,1) * Zeros(6)' ≡ Zeros(5,6) @test Zeros(5) * oneton(6)' ≡ Zeros(5,6) @test @inferred(Zeros{Int}(0)' * Zeros{Int}(0)) === zero(Int) @test Any[1,2.0]' * Zeros{Int}(2) == 0 @test Real[1,2.0]' * Zeros{Int}(2) == 0 @test @inferred(([[1,2]])' * Zeros{SVector{2,Int}}(1)) ≡ 0 @test ([[1,2], [1,2]])' * Zeros{SVector{2,Int}}(2) ≡ 0 @test_throws DimensionMismatch ([[1,2,3]])' * Zeros{SVector{2,Int}}(1) @test_throws DimensionMismatch ([[1,2,3], [1,2]])' * Zeros{SVector{2,Int}}(2) A = SMatrix{2,1,Int,2}[]' B = Zeros(SVector{2,Int},0) C = collect(B) @test @inferred(A * B) == @inferred(A * C) end @testset "Check multiplication by Transpose-d vectors works as expected." begin @test transpose(oneton(4, 3)) * Zeros(4) === Zeros(3) @test transpose(oneton(4)) * Zeros(4) == 0.0 @test transpose([1, 2, 3]) * Zeros{Int}(3) === zero(Int) @test_throws DimensionMismatch transpose(oneton(4)) * Zeros(3) @test transpose(Zeros(5)) * oneton(5,3) ≡ transpose(Zeros(5))*Zeros(5,3) ≡ transpose(Zeros(5))*Ones(5,3) ≡ transpose(Zeros(3)) @test abs(transpose(Zeros(5)) * oneton(5)) ≡ abs(transpose(Zeros(5)) * Zeros(5)) ≡ abs(transpose(Zeros(5)) * Ones(5)) ≡ 0.0 @test oneton(5) * transpose(Zeros(6)) ≡ oneton(5,1) * transpose(Zeros(6)) ≡ Zeros(5,6) @test Zeros(5) * transpose(oneton(6)) ≡ Zeros(5,6) @test transpose(oneton(5)) * Zeros(5) == 0.0 @test transpose(oneton(5) .+ im) * Zeros(5) == 0.0 + 0im @test @inferred(transpose(Zeros{Int}(0)) * Zeros{Int}(0)) === zero(Int) @test transpose(Any[1,2.0]) * Zeros{Int}(2) == 0 @test transpose(Real[1,2.0]) * Zeros{Int}(2) == 0 @test @inferred(transpose([[1,2]]) * Zeros{SVector{2,Int}}(1)) ≡ 0 @test transpose([[1,2], [1,2]]) * Zeros{SVector{2,Int}}(2) ≡ 0 @test_throws DimensionMismatch transpose([[1,2,3]]) * Zeros{SVector{2,Int}}(1) @test_throws DimensionMismatch transpose([[1,2,3], [1,2]]) * Zeros{SVector{2,Int}}(2) A = transpose(SMatrix{2,1,Int,2}[]) B = Zeros(SVector{2,Int},0) C = collect(B) @test @inferred(A * B) == @inferred(A * C) @testset "Diagonal mul introduced in v1.9" begin @test Zeros(5)'*Diagonal(1:5) ≡ Zeros(5)' @test transpose(Zeros(5))*Diagonal(1:5) ≡ transpose(Zeros(5)) @test Zeros(5)'*Diagonal(1:5)*(1:5) == (1:5)'*Diagonal(1:5)*Zeros(5) == transpose(1:5)*Diagonal(1:5)*Zeros(5) == Zeros(5)'*Diagonal(1:5)*Zeros(5) == transpose(Zeros(5))*Diagonal(1:5)*Zeros(5) == transpose(Zeros(5))*Diagonal(1:5)*(1:5) @test_throws DimensionMismatch Zeros(6)'*Diagonal(1:5)*Zeros(5) @test_throws DimensionMismatch Zeros(5)'*Diagonal(1:6)*Zeros(5) @test_throws DimensionMismatch Zeros(5)'*Diagonal(1:5)*Zeros(6) end end z1, z2 = Zeros{Float64}(4), Zeros{Int}(4) @testset "`Zeros` are closed under addition and subtraction (both unary and binary)." begin @test +(Zeros{Float64}(3, 5)) === Zeros{Float64}(3, 5) @test -(Zeros{Float32}(5, 2)) === Zeros{Float32}(5, 2) @test +(z1) === z1 @test -(z1) === z1 test_addition_subtraction_dot((z1, z2), (z1, z2), Zeros) test_addition_and_subtraction_dim_mismatch(z1, Zeros{Float64}(4, 2)) end # `Zeros` +/- `Fill`s should yield `Fills`. fill1, fill2 = Fill(5.0, 4), Fill(5, 4) test_addition_subtraction_dot((z1, z2), (fill1, fill2), Fill) test_addition_and_subtraction_dim_mismatch(z1, Fill(5, 5)) X = randn(3, 5) for op in [+, -] # Addition / subtraction with same eltypes. @test op(Zeros(6, 4), Zeros(6, 4)) === Zeros(6, 4) @test_throws DimensionMismatch op(X, Zeros(4, 6)) @test eltype(op(Zeros(3, 5), X)) == Float64 # Different eltypes, the other way around. @test op(X, Zeros{Float32}(3, 5)) isa Matrix{Float64} @test !(op(X, Zeros{Float32}(3, 5)) === X) @test op(X, Zeros{Float32}(3, 5)) == X @test !(op(X, Zeros{ComplexF64}(3, 5)) === X) @test op(X, Zeros{ComplexF64}(3, 5)) == X # Addition / subtraction of Zeros. @test eltype(op(Zeros{Float64}(4, 5), Zeros{Int}(4, 5))) == Float64 @test eltype(op(Zeros{Int}(5, 4), Zeros{Float32}(5, 4))) == Float32 @test op(Zeros{Float64}(4, 5), Zeros{Int}(4, 5)) isa Zeros{Float64} @test op(Zeros{Float64}(4, 5), Zeros{Int}(4, 5)) === Zeros{Float64}(4, 5) end @testset "Zeros +/- dense where + / - have different results." begin @test +(Zeros(3, 5), X) == X && +(X, Zeros(3, 5)) == X @test !(Zeros(3, 5) + X === X) && !(X + Zeros(3, 5) === X) @test -(Zeros(3, 5), X) == -X end @testset "Addition with different eltypes." begin @test +(Zeros{Float32}(3, 5), X) isa Matrix{Float64} @test !(+(Zeros{Float32}(3, 5), X) === X) @test +(Zeros{Float32}(3, 5), X) == X @test !(+(Zeros{ComplexF64}(3, 5), X) === X) @test +(Zeros{ComplexF64}(3, 5), X) == X end @testset "Subtraction with different eltypes." begin @test -(Zeros{Float32}(3, 5), X) isa Matrix{Float64} @test -(Zeros{Float32}(3, 5), X) == -X @test -(Zeros{ComplexF64}(3, 5), X) == -X end @testset "Tests for ranges." begin X = randn(5) @test !(Zeros(5) + X ≡ X) @test Zeros{Int}(5) + (1:5) ≡ (1:5) + Zeros{Int}(5) ≡ (1:5) @test Zeros(5) + (1:5) ≡ (1:5) + Zeros(5) ≡ (1.0:1.0:5.0) @test (1:5) - Zeros{Int}(5) ≡ (1:5) @test Zeros{Int}(5) - (1:5) ≡ -1:-1:-5 @test Zeros(5) - (1:5) ≡ -1.0:-1.0:-5.0 @test Zeros{Int}(5) + (1.0:5) ≡ (1.0:5) + Zeros{Int}(5) ≡ 1.0:5 end @testset "test Base.zero" begin @test zero(Zeros(10)) == Zeros(10) @test zero(Ones(10,10)) == Zeros(10,10) @test zero(Fill(0.5, 10, 10)) == Zeros(10,10) end @testset "Matrix ±" begin x = Fill([1,2], 5) z = Zeros{SVector{2,Int}}(5) @test +(z) ≡ -(z) ≡ z @test +(x) == x @test -(x) == Fill(-[1,2], 5) end end @testset "maximum/minimum/svd/sort" begin @test maximum(Fill(1, 1_000_000_000)) == minimum(Fill(1, 1_000_000_000)) == 1 @test svdvals(fill(2,5,6)) ≈ svdvals(Fill(2,5,6)) @test svdvals(Eye(5)) === Fill(1.0,5) @test sort(Ones(5)) == sort!(Ones(5)) @test_throws MethodError issorted(Fill(im, 2)) @test_throws MethodError sort(Fill(im, 2)) @test_throws MethodError sort!(Fill(im, 2)) end @testset "Cumsum, accumulate and diff" begin @test @inferred(sum(Fill(3,10))) ≡ 30 @test @inferred(reduce(+, Fill(3,10))) ≡ 30 @test @inferred(sum(x -> x + 1, Fill(3,10))) ≡ 40 @test @inferred(cumsum(Fill(3,10))) ≡ @inferred(accumulate(+, Fill(3,10))) ≡ StepRangeLen(3,3,10) @test @inferred(accumulate(-, Fill(3,10))) ≡ StepRangeLen(3,-3,10) @test @inferred(sum(Ones(10))) ≡ 10.0 @test @inferred(sum(x -> x + 1, Ones(10))) ≡ 20.0 @test @inferred(cumsum(Ones(10))) ≡ @inferred(accumulate(+, Ones(10))) ≡ StepRangeLen(1.0, 1.0, 10) @test @inferred(accumulate(-, Ones(10))) ≡ StepRangeLen(1.0,-1.0,10) @test sum(Ones{Int}(10)) ≡ 10 @test sum(x -> x + 1, Ones{Int}(10)) ≡ 20 @test cumsum(Ones{Int}(10)) ≡ accumulate(+,Ones{Int}(10)) ≡ Base.OneTo(10) @test accumulate(-, Ones{Int}(10)) ≡ StepRangeLen(1,-1,10) @test sum(Zeros(10)) ≡ 0.0 @test sum(x -> x + 1, Zeros(10)) ≡ 10.0 @test cumsum(Zeros(10)) ≡ accumulate(+,Zeros(10)) ≡ accumulate(-,Zeros(10)) ≡ Zeros(10) @test sum(Zeros{Int}(10)) ≡ 0 @test sum(x -> x + 1, Zeros{Int}(10)) ≡ 10 @test cumsum(Zeros{Int}(10)) ≡ accumulate(+,Zeros{Int}(10)) ≡ accumulate(-,Zeros{Int}(10)) ≡ Zeros{Int}(10) # we want cumsum of fills to match the types of the standard cusum @test all(cumsum(Zeros{Bool}(10)) .≡ cumsum(zeros(Bool,10))) @test all(accumulate(+, Zeros{Bool}(10)) .≡ accumulate(+, zeros(Bool,10)) .≡ accumulate(-, zeros(Bool,10))) @test cumsum(Zeros{Bool}(10)) ≡ accumulate(+, Zeros{Bool}(10)) ≡ accumulate(-, Zeros{Bool}(10)) ≡ Zeros{Int}(10) @test cumsum(Ones{Bool}(10)) ≡ accumulate(+, Ones{Bool}(10)) ≡ Base.OneTo{Int}(10) @test all(cumsum(Fill(true,10)) .≡ cumsum(fill(true,10))) @test cumsum(Fill(true,10)) ≡ StepRangeLen(1, true, 10) @test all(cumsum(Zeros{UInt8}(10)) .≡ cumsum(zeros(UInt8,10))) @test all(accumulate(+, Zeros{UInt8}(10)) .≡ accumulate(+, zeros(UInt8,10))) @test cumsum(Zeros{UInt8}(10)) ≡ Zeros{UInt64}(10) @test accumulate(+, Zeros{UInt8}(10)) ≡ accumulate(-, Zeros{UInt8}(10)) ≡ Zeros{UInt8}(10) @test all(cumsum(Ones{UInt8}(10)) .≡ cumsum(ones(UInt8,10))) @test all(accumulate(+, Ones{UInt8}(10)) .≡ accumulate(+, ones(UInt8,10))) @test cumsum(Ones{UInt8}(10)) ≡ Base.OneTo(UInt64(10)) @test accumulate(+, Ones{UInt8}(10)) ≡ Base.OneTo(UInt8(10)) @test all(cumsum(Fill(UInt8(2),10)) .≡ cumsum(fill(UInt8(2),10))) @test all(accumulate(+, Fill(UInt8(2))) .≡ accumulate(+, fill(UInt8(2)))) @test cumsum(Fill(UInt8(2),10)) ≡ StepRangeLen(UInt64(2), UInt8(2), 10) @test accumulate(+, Fill(UInt8(2),10)) ≡ StepRangeLen(UInt8(2), UInt8(2), 10) @test diff(Fill(1,10)) ≡ Zeros{Int}(9) @test diff(Ones{Float64}(10)) ≡ Zeros{Float64}(9) @test_throws UndefKeywordError cumsum(Fill(1,1,5)) @test @inferred(sum([Ones(4)])) ≡ Fill(1.0, 4) @test @inferred(sum([Trues(4)])) ≡ Fill(1, 4) @testset "infinite arrays" begin r = InfiniteArrays.OneToInf() A = Ones{Int}((r,)) @test isinf(sum(A)) @test sum(A) == length(A) @test sum(x->x^2, A) == sum(A.^2) @testset "IteratorSize" begin @test (@inferred Base.IteratorSize(Ones())) == Base.IteratorSize(ones()) @test (@inferred Base.IteratorSize(Ones(2))) == Base.IteratorSize(ones(2)) @test (@inferred Base.IteratorSize(Ones(r))) == Base.IsInfinite() @test (@inferred Base.IteratorSize(Fill(2, (1:2, 1:2)))) == Base.HasShape{2}() @test (@inferred Base.IteratorSize(Fill(2, (1:2, r)))) == Base.IsInfinite() @test (@inferred Base.IteratorSize(Fill(2, (r, 1:2)))) == Base.IsInfinite() @test (@inferred Base.IteratorSize(Fill(2, (r, r)))) == Base.IsInfinite() end @test issorted(Fill(2, (InfiniteArrays.OneToInf(),))) end end @testset "iterators" begin @testset "invalid state" begin @test isnothing(iterate(Ones(4), (1,-3))) @test isempty(Iterators.rest(Ones(4), (1,-3))) end @testset "Iterators.rest" begin @test Iterators.rest(Fill(4, 10), (4, 3)) === Fill(4, 7) # Base.rest a, b... = Fill(3, 4) @test a === 3 @test b === Fill(3, 3) a, b... = Ones(3, 4) @test a === 1.0 @test b === Ones(11) end @testset "Iterators.drop/take" begin @test Iterators.drop(Fill(4, 10), 3) === Fill(4, 7) @test Iterators.take(Fill(4, 10), 3) === Fill(4, 3) @test Iterators.drop(Fill(4, 10), 0) === Fill(4, 10) @test Iterators.take(Fill(4, 10), 0) === Fill(4, 0) @test Iterators.drop(Fill(4, 10), 11) === Fill(4, 0) @test Iterators.take(Fill(4, 10), 11) === Fill(4, 10) @test_throws ArgumentError Iterators.drop(Fill(4, 10), -11) @test_throws ArgumentError Iterators.take(Fill(4, 10), -11) @test Iterators.drop(Ones(4, 10), 3) === Ones(37) @test Iterators.take(Ones(4, 10), 3) === Ones(3) end end @testset "Broadcast" begin x = Fill(5,5) @test (.+)(x) ≡ x @test (.-)(x) ≡ -x @test exp.(x) ≡ Fill(exp(5),5) @test x .+ 1 ≡ Fill(6,5) @test 1 .+ x ≡ Fill(6,5) @test x .+ x ≡ Fill(10,5) @test x .+ Ones(5) ≡ Fill(6.0,5) f = (x,y) -> cos(x*y) @test f.(x, Ones(5)) ≡ Fill(f(5,1.0),5) @test x .^ 1 ≡ Fill(5,5) y = Ones(5,5) @test (.+)(y) ≡ Ones(5,5) @test (.-)(y) ≡ Fill(-1.0,5,5) @test exp.(y) ≡ Fill(exp(1),5,5) @test y .+ 1 ≡ Fill(2.0,5,5) @test y .+ y ≡ Fill(2.0,5,5) @test y .* y ≡ y ./ y ≡ y .\ y ≡ y @test y .^ 1 ≡ y .^ 0 ≡ Ones(5,5) rng = MersenneTwister(123456) sizes = [(5, 4), (5, 1), (1, 4), (1, 1), (5,)] for sx in sizes, sy in sizes x, y = Fill(randn(rng), sx), Fill(randn(rng), sy) x_one, y_one = Ones(sx), Ones(sy) x_zero, y_zero = Zeros(sx), Zeros(sy) x_dense, y_dense = randn(rng, sx), randn(rng, sy) for x in [x, x_one, x_zero, x_dense], y in [y, y_one, y_zero, y_dense] @test x .+ y == collect(x) .+ collect(y) end @test x_zero .+ y_zero isa Zeros @test x_zero .+ y_one isa Ones @test x_one .+ y_zero isa Ones for x in [x, x_one, x_zero, x_dense], y in [y, y_one, y_zero, y_dense] @test x .* y == collect(x) .* collect(y) end for x in [x, x_one, x_zero, x_dense] @test x .* y_zero isa Zeros end for y in [y, y_one, y_zero, y_dense] @test x_zero .* y isa Zeros end end @test Zeros{Int}(5) .^ 0 ≡ Ones{Int}(5) @test Zeros{Int}(5) .^ 1 ≡ Zeros{Int}(5) @test Zeros{Int}(5) .+ Zeros(5) isa Zeros{Float64} # Test for conj, real and imag with complex element types @test conj(Zeros{ComplexF64}(10)) isa Zeros{ComplexF64} @test conj(Zeros{ComplexF64}(10,10)) isa Zeros{ComplexF64} @test conj(Ones{ComplexF64}(10)) isa Ones{ComplexF64} @test conj(Ones{ComplexF64}(10,10)) isa Ones{ComplexF64} @test real(Zeros{Float64}(10)) isa Zeros{Float64} @test real(Zeros{Float64}(10,10)) isa Zeros{Float64} @test real(Zeros{ComplexF64}(10)) isa Zeros{Float64} @test real(Zeros{ComplexF64}(10,10)) isa Zeros{Float64} @test real(Ones{Float64}(10)) isa Ones{Float64} @test real(Ones{Float64}(10,10)) isa Ones{Float64} @test real(Ones{ComplexF64}(10)) isa Ones{Float64} @test real(Ones{ComplexF64}(10,10)) isa Ones{Float64} @test imag(Zeros{Float64}(10)) isa Zeros{Float64} @test imag(Zeros{Float64}(10,10)) isa Zeros{Float64} @test imag(Zeros{ComplexF64}(10)) isa Zeros{Float64} @test imag(Zeros{ComplexF64}(10,10)) isa Zeros{Float64} @test imag(Ones{Float64}(10)) isa Zeros{Float64} @test imag(Ones{Float64}(10,10)) isa Zeros{Float64} @test imag(Ones{ComplexF64}(10)) isa Zeros{Float64} @test imag(Ones{ComplexF64}(10,10)) isa Zeros{Float64} @testset "range broadcast" begin rnge = range(-5.0, step=1.0, length=10) @test broadcast(*, Fill(5.0, 10), rnge) == broadcast(*, 5.0, rnge) @test broadcast(*, Zeros(10, 10), rnge) ≡ Zeros{Float64}(10, 10) @test broadcast(*, rnge, Zeros(10, 10)) ≡ Zeros{Float64}(10, 10) @test broadcast(*, Ones{Int}(10), rnge) ≡ rnge @test broadcast(*, rnge, Ones{Int}(10)) ≡ rnge @test broadcast(*, Ones(10), -5:4) ≡ broadcast(*, -5:4, Ones(10)) ≡ rnge @test broadcast(*, Ones(10), -5:1:4) ≡ broadcast(*, -5:1:4, Ones(10)) ≡ rnge @test_throws DimensionMismatch broadcast(*, Fill(5.0, 11), rnge) @test broadcast(*, rnge, Fill(5.0, 10)) == broadcast(*, rnge, 5.0) @test_throws DimensionMismatch broadcast(*, rnge, Fill(5.0, 11)) # following should pass using alternative implementation in code deg = 5:5 @test_throws ArgumentError @inferred(broadcast(*, Fill(5.0, 10), deg)) == broadcast(*, fill(5.0,10), deg) @test_throws ArgumentError @inferred(broadcast(*, deg, Fill(5.0, 10))) == broadcast(*, deg, fill(5.0,10)) @test rnge .+ Zeros(10) ≡ rnge .- Zeros(10) ≡ Zeros(10) .+ rnge ≡ rnge @test_throws DimensionMismatch rnge .+ Zeros(5) @test_throws DimensionMismatch rnge .- Zeros(5) @test_throws DimensionMismatch Zeros(5) .+ rnge @test Fill(2,10) + (1:10) isa UnitRange @test (1:10) + Fill(2,10) isa UnitRange f = Fill(1+im,10) @test f + rnge isa AbstractRange @test f + rnge == rnge + f @test f + (1:10) isa AbstractRange end @testset "Special Zeros/Ones" begin @test broadcast(+,Zeros(5)) ≡ broadcast(-,Zeros(5)) ≡ Zeros(5) @test broadcast(+,Ones(5)) ≡ Ones(5) @test Zeros(5) .* Ones(5) ≡ Zeros(5) .* 1 ≡ Zeros(5) @test Zeros(5) .* Fill(5.0, 5) ≡ Zeros(5) .* 5.0 ≡ Zeros(5) @test Ones(5) .* Zeros(5) ≡ 1 .* Zeros(5) ≡ Zeros(5) @test Fill(5.0, 5) .* Zeros(5) ≡ 5.0 .* Zeros(5) ≡ Zeros(5) @test Zeros(5) ./ Ones(5) ≡ Zeros(5) ./ 1 ≡ Zeros(5) @test Zeros(5) ./ Fill(5.0, 5) ≡ Zeros(5) ./ 5.0 ≡ Zeros(5) @test Ones(5) .\ Zeros(5) ≡ 1 .\ Zeros(5) ≡ Zeros(5) @test Fill(5.0, 5) .\ Zeros(5) ≡ 5.0 .\ Zeros(5) ≡ Zeros(5) @test conj.(Zeros(5)) ≡ Zeros(5) @test conj.(Zeros{ComplexF64}(5)) ≡ Zeros{ComplexF64}(5) @test_throws DimensionMismatch broadcast(*, Ones(3), 1:6) @test_throws DimensionMismatch broadcast(*, 1:6, Ones(3)) @test_throws DimensionMismatch broadcast(*, Fill(1,3), 1:6) @test_throws DimensionMismatch broadcast(*, 1:6, Fill(1,3)) @testset "Number" begin @test broadcast(*, Zeros(5), 2) ≡ broadcast(*, 2, Zeros(5)) ≡ Zeros(5) end @testset "Nested" begin @test randn(5) .\ rand(5) .* Zeros(5) ≡ Zeros(5) @test broadcast(*, Zeros(5), Base.Broadcast.broadcasted(\, randn(5), rand(5))) ≡ Zeros(5) end @testset "array-valued" begin @test broadcast(*, Fill([1,2],3), 1:3) == broadcast(*, 1:3, Fill([1,2],3)) == broadcast(*, 1:3, fill([1,2],3)) @test broadcast(*, Fill([1,2],3), Zeros(3)) == broadcast(*, Zeros(3), Fill([1,2],3)) == broadcast(*, zeros(3), fill([1,2],3)) @test broadcast(*, Fill([1,2],3), Zeros(3)) isa Fill{Vector{Float64}} @test broadcast(*, [[1,2], [3,4,5]], Zeros(2)) == broadcast(*, Zeros(2), [[1,2], [3,4,5]]) == broadcast(*, zeros(2), [[1,2], [3,4,5]]) end @testset "NaN" begin @test Zeros(5) ./ Zeros(5) ≡ Zeros(5) .\ Zeros(5) ≡ Fill(NaN,5) @test Zeros{Int}(5,6) ./ Zeros{Int}(5) ≡ Zeros{Int}(5) .\ Zeros{Int}(5,6) ≡ Fill(NaN,5,6) end @testset "Addition/Subtraction" begin @test Zeros{Int}(5) .+ (1:5) ≡ (1:5) .+ Zeros{Int}(5) ≡ (1:5) .- Zeros{Int}(5) ≡ 1:5 @test Zeros{Int}(1) .+ (1:5) ≡ (1:5) .+ Zeros{Int}(1) ≡ (1:5) .- Zeros{Int}(1) ≡ 1:5 @test Zeros(5) .+ (1:5) == (1:5) .+ Zeros(5) == (1:5) .- Zeros(5) == 1:5 @test Zeros{Int}(5) .+ Fill(1,5) ≡ Fill(1,5) .+ Zeros{Int}(5) ≡ Fill(1,5) .- Zeros{Int}(5) ≡ Fill(1,5) @test_throws DimensionMismatch Zeros{Int}(2) .+ (1:5) @test_throws DimensionMismatch (1:5) .+ Zeros{Int}(2) for v in (rand(Bool, 5), [1:5;], SVector{5}(1:5), SVector{5,ComplexF16}(1:5)), T in (Bool, Int, Float64) TT = eltype(v + zeros(T, 5)) S = v isa SVector ? SVector{5,TT} : Vector{TT} a = @inferred(Zeros{T}(5) .+ v) b = @inferred(v .+ Zeros{T}(5)) c = @inferred(v .- Zeros{T}(5)) @test a == b == c == v d = @inferred(Zeros{T}(5) .- v) @test d == -v @test all(Base.Fix2(isa, S), (a,b,c,d)) end end end @testset "support Ref" begin @test Fill(1,10) .- 1 ≡ Fill(1,10) .- Ref(1) ≡ Fill(1,10) .- Ref(1I) @test Fill([1 2; 3 4],10) .- Ref(1I) == Fill([0 2; 3 3],10) @test Ref(1I) .+ Fill([1 2; 3 4],10) == Fill([2 2; 3 5],10) end @testset "Special Ones" begin @test Ones{Int}(5) .* (1:5) ≡ (1:5) .* Ones{Int}(5) ≡ 1:5 @test Ones(5) .* (1:5) ≡ (1:5) .* Ones(5) ≡ 1.0:5 @test Ones{Int}(5) .* Ones{Int}(5) ≡ Ones{Int}(5) @test Ones{Int}(5,2) .* (1:5) == Array(Ones{Int}(5,2)) .* Array(1:5) @test (1:5) .* Ones{Int}(5,2) == Array(1:5) .* Array(Ones{Int}(5,2)) @test (1:0.5:5) .* Ones{Int}(9,2) == Array(1:0.5:5) .* Array(Ones{Int}(9,2)) @test Ones{Int}(9,2) .* (1:0.5:5) == Array(Ones{Int}(9,2)) .* Array(1:0.5:5) @test_throws DimensionMismatch Ones{Int}(6) .* (1:5) @test_throws DimensionMismatch (1:5) .* Ones{Int}(6) @test_throws DimensionMismatch Ones{Int}(5) .* Ones{Int}(6) end @testset "Zeros -" begin @test Zeros(10) - Zeros(10) ≡ Zeros(10) @test Ones(10) - Zeros(10) ≡ Ones(10) @test Ones(10) - Ones(10) ≡ Zeros(10) @test Fill(1,10) - Zeros(10) ≡ Fill(1.0,10) @test Zeros(10) .- Zeros(10) ≡ Zeros(10) @test Ones(10) .- Zeros(10) ≡ Ones(10) @test Ones(10) .- Ones(10) ≡ Zeros(10) @test Fill(1,10) .- Zeros(10) ≡ Fill(1.0,10) @test Zeros(10) .- Zeros(1,9) ≡ Zeros(10,9) @test Ones(10) .- Zeros(1,9) ≡ Ones(10,9) @test Ones(10) .- Ones(1,9) ≡ Zeros(10,9) end @testset "issue #208" begin TS = (Bool, Int, Float32, Float64) for S in TS, T in TS u = rand(S, 2) v = Zeros(T, 2) if zero(S) + zero(T) isa S @test @inferred(Broadcast.broadcasted(-, u, v)) === u @test @inferred(Broadcast.broadcasted(+, u, v)) === u @test @inferred(Broadcast.broadcasted(+, v, u)) === u else @test @inferred(Broadcast.broadcasted(-, u, v)) isa Broadcast.Broadcasted @test @inferred(Broadcast.broadcasted(+, u, v)) isa Broadcast. Broadcasted @test @inferred(Broadcast.broadcasted(+, v, u)) isa Broadcast.Broadcasted end @test @inferred(Broadcast.broadcasted(-, v, u)) isa Broadcast.Broadcasted end end @testset "Zero .*" begin TS = (Bool, Int, Float32, Float64) for S in TS, T in TS U = typeof(zero(S) * zero(T)) @test Zeros{S}(10) .* Zeros{T}(10) ≡ Zeros{U}(10) @test rand(S, 10) .* Zeros(T, 10) ≡ Zeros(U, 10) @test Zeros(S, 10) .* rand(T, 10) ≡ Zeros(U, 10) if S !== Bool @test (S(1):S(10)) .* Zeros(T, 10) ≡ Zeros(U, 10) @test_throws DimensionMismatch (S(1):S(11)) .* Zeros(T, 10) end if T !== Bool @test Zeros(S, 10) .* (T(1):T(10)) ≡ Zeros(U, 10) @test_throws DimensionMismatch Zeros(S, 10) .* (T(1):T(11)) end end end end @testset "map" begin x1 = Ones(5) @test map(exp,x1) === Fill(exp(1.0),5) @test map(isone,x1) === Fill(true,5) x0 = Zeros(5) @test map(exp,x0) === exp.(x0) x2 = Fill(2,5,3) @test map(exp,x2) === Fill(exp(2),5,3) @test map(+, x1, x2) === Fill(3.0, 5) @test map(+, x2, x2) === x2 .+ x2 @test_throws DimensionMismatch map(+, x2', x2) # Issue https://github.com/JuliaArrays/FillArrays.jl/issues/179 if VERSION < v"1.11.0-" # In 1.11, 1-arg map & mapreduce was removed @test map(() -> "ok") == "ok" # was MethodError: reducing over an empty collection is not allowed @test mapreduce(() -> "ok", *) == "ok" else @test_throws "no method matching map" map(() -> "ok") @test_throws "no method matching map" mapreduce(() -> "ok", *) end end @testset "mapreduce" begin x = rand(3, 4) y = fill(1.0, 3, 4) Y = Fill(1.0, 3, 4) O = Ones(3, 4) @test mapreduce(exp, +, Y) == mapreduce(exp, +, y) @test mapreduce(exp, +, Y; dims=2) == mapreduce(exp, +, y; dims=2) @test mapreduce(identity, +, Y) == sum(y) == sum(Y) @test mapreduce(identity, +, Y, dims=1) == sum(y, dims=1) == sum(Y, dims=1) @test mapreduce(exp, +, Y; dims=(1,), init=5.0) == mapreduce(exp, +, y; dims=(1,), init=5.0) # Two arrays @test mapreduce(*, +, x, Y) == mapreduce(*, +, x, y) @test mapreduce(*, +, Y, x) == mapreduce(*, +, y, x) @test mapreduce(*, +, x, O) == mapreduce(*, +, x, y) @test mapreduce(*, +, Y, O) == mapreduce(*, +, y, y) f2(x,y) = 1 + x/y op2(x,y) = x^2 + 3y @test mapreduce(f2, op2, x, Y) == mapreduce(f2, op2, x, y) @test mapreduce(f2, op2, x, Y, dims=1, init=5.0) == mapreduce(f2, op2, x, y, dims=1, init=5.0) @test mapreduce(f2, op2, Y, x, dims=1, init=5.0) == mapreduce(f2, op2, y, x, dims=1, init=5.0) @test mapreduce(f2, op2, x, O, dims=1, init=5.0) == mapreduce(f2, op2, x, y, dims=1, init=5.0) @test mapreduce(f2, op2, Y, O, dims=1, init=5.0) == mapreduce(f2, op2, y, y, dims=1, init=5.0) # More than two @test mapreduce(+, +, x, Y, x) == mapreduce(+, +, x, y, x) @test mapreduce(+, +, Y, x, x) == mapreduce(+, +, y, x, x) @test mapreduce(+, +, x, O, Y) == mapreduce(+, +, x, y, y) @test mapreduce(+, +, Y, O, Y) == mapreduce(+, +, y, y, y) @test mapreduce(+, +, Y, O, Y, x) == mapreduce(+, +, y, y, y, x) end @testset "Offset indexing" begin A = Fill(3, (Base.Slice(-1:1),)) @test axes(A) == (Base.Slice(-1:1),) @test A[0] == 3 @test_throws BoundsError A[2] @test_throws BoundsError A[-2] A = Zeros((Base.Slice(-1:1),)) @test axes(A) == (Base.Slice(-1:1),) @test A[0] == 0 @test_throws BoundsError A[2] @test_throws BoundsError A[-2] end @testset "0-dimensional" begin A = Fill{Int,0,Tuple{}}(3, ()) @test A[] ≡ A[1] ≡ 3 @test A ≡ Fill{Int,0}(3, ()) ≡ Fill(3, ()) ≡ Fill(3) @test size(A) == () @test axes(A) == () A = Ones{Int,0,Tuple{}}(()) @test A[] ≡ A[1] ≡ 1 @test A ≡ Ones{Int,0}(()) ≡ Ones{Int}(()) ≡ Ones{Int}() A = Zeros{Int,0,Tuple{}}(()) @test A[] ≡ A[1] ≡ 0 @test A ≡ Zeros{Int,0}(()) ≡ Zeros{Int}(()) ≡ Zeros{Int}() end @testset "unique" begin @test unique(Fill(12, 20)) == unique(fill(12, 20)) @test unique(Fill(1, 0)) == [] @test unique(Zeros(0)) == Zeros(0) @test !allunique(Fill("a", 2)) @test allunique(Ones(0)) end @testset "iterate" begin for d in (0, 1, 2, 100) for T in (Float64, Int) m = Eye(d) mcp = [x for x in m] @test mcp == m @test eltype(mcp) == eltype(m) end end end @testset "properties" begin for d in (0, 1, 2, 100) @test isone(Eye(d)) end end @testset "any all iszero isone" begin for T in (Int, Float64, ComplexF64) for m in (Eye{T}(0), Eye{T}(0, 0), Eye{T}(0, 1), Eye{T}(1, 0)) @test ! any(isone, m) @test ! any(iszero, m) @test ! all(iszero, m) @test ! all(isone, m) end @testset for d in (0, 1) for m in (Eye{T}(d), Eye{T}(d, d)) M = Array(m) @test ! any(iszero, m) @test ! all(iszero, m) @test any(isone, m) == !isempty(m) @test all(isone, m) == !isempty(m) if !isempty(m) @test ! any(iszero, m) == ! any(iszero, M) @test ! all(iszero, m) == ! all(iszero, M) @test any(isone, m) == any(isone, M) @test all(isone, m) == all(isone, M) end end for m in (Eye{T}(d, d + 1), Eye{T}(d + 1, d)) M = Array(m) @test any(iszero, m) == !isempty(m) @test ! all(iszero, m) @test any(isone, m) == !isempty(m) @test ! all(isone, m) if !isempty(M) @test any(iszero, m) == any(iszero, M) @test ! all(iszero, m) == ! all(iszero, M) @test any(isone, m) == any(isone, M) @test ! all(isone, m) == ! all(isone, M) end end onem = Ones{T}(d, d) @test isone(onem) == isone(Array(onem)) @test iszero(onem) == isempty(onem) == iszero(Array(onem)) if d > 0 @test !isone(Ones{T}(d, 2d)) end zerom = Zeros{T}(d, d) @test isone(zerom) == isempty(zerom) == isone(Array(zerom)) @test iszero(zerom) == iszero(Array(zerom)) if d > 0 @test iszero(Zeros{T}(d, 2d)) end fillm0 = Fill(T(0), d, d) @test isone(fillm0) == isempty(fillm0) == isone(Array(fillm0)) @test iszero(fillm0) == iszero(Array(fillm0)) fillm1 = Fill(T(1), d, d) @test isone(fillm1) == isone(Array(fillm1)) @test iszero(fillm1) == isempty(fillm1) == iszero(Array(fillm1)) fillm2 = Fill(T(2), d, d) @test isone(fillm2) == isempty(fillm2) == isone(Array(fillm2)) @test iszero(fillm2) == isempty(fillm2) == iszero(Array(fillm2)) end for d in (2, 3) for m in (Eye{T}(d), Eye{T}(d, d), Eye{T}(d, d + 2), Eye{T}(d + 2, d)) @test any(iszero, m) @test ! all(iszero, m) @test any(isone, m) @test ! all(isone, m) end m1 = Ones{T}(d, d) @test ! isone(m1) @test ! iszero(m1) @test all(isone, m1) @test ! all(iszero, m1) m2 = Zeros{T}(d, d) @test ! isone(m2) @test iszero(m2) @test ! all(isone, m2) @test all(iszero, m2) m3 = Fill(T(2), d, d) @test ! isone(m3) @test ! iszero(m3) @test ! all(isone, m3) @test ! all(iszero, m3) @test ! any(iszero, m3) m4 = Fill(T(1), d, d) @test ! isone(m4) @test ! iszero(m4) end end @test iszero(Zeros{SMatrix{2,2,Int,4}}(2)) @test iszero(Fill(SMatrix{2,2}(0,0,0,0), 2)) @test iszero(Fill(SMatrix{2,2}(0,0,0,1), 0)) # compile-time evaluation @test @inferred((Z -> Val(iszero(Z)))(Zeros(3,3))) == Val(true) @testset "all/any" begin @test any(Ones{Bool}(10)) === all(Ones{Bool}(10)) === any(Fill(true,10)) === all(Fill(true,10)) === true @test any(Zeros{Bool}(10)) === all(Zeros{Bool}(10)) === any(Fill(false,10)) === all(Fill(false,10)) === false @test all(b -> ndims(b) == 1, Fill([1,2],10)) @test any(b -> ndims(b) == 1, Fill([1,2],10)) @test all(Fill(2,0)) @test !any(Fill(2,0)) @test any(Trues(2,0)) == any(trues(2,0)) @test_throws TypeError all(Fill(2,2)) @test all(iszero, Fill(missing,0)) === all(iszero, fill(missing,0)) === true @test all(iszero, Fill(missing,2)) === all(iszero, fill(missing,2)) === missing @test any(iszero, Fill(missing,0)) === any(iszero, fill(missing,0)) === false @test any(iszero, Fill(missing,2)) === any(iszero, fill(missing,2)) === missing end @testset "Error" begin @test_throws TypeError any(exp, Fill(1,5)) @test_throws TypeError all(exp, Fill(1,5)) @test_throws TypeError any(exp, Eye(5)) @test_throws TypeError all(exp, Eye(5)) @test_throws TypeError any(Fill(1,5)) @test_throws TypeError all(Fill(1,5)) @test_throws TypeError any(Zeros(5)) @test_throws TypeError all(Zeros(5)) @test_throws TypeError any(Ones(5)) @test_throws TypeError all(Ones(5)) @test_throws TypeError any(Eye(5)) @test_throws TypeError all(Eye(5)) end end @testset "Eye identity ops" begin m = Eye(10) D = Diagonal(Fill(2,10)) for op in (permutedims, inv) @test op(m) === m end @test permutedims(D) ≡ D @test inv(D) ≡ Diagonal(Fill(1/2,10)) for m in (Eye(10), Eye(10, 10), Eye(10, 8), Eye(8, 10), D) for op in (tril, triu, tril!, triu!) @test op(m) === m end end @test copy(m) ≡ m @test copy(D) ≡ D end @testset "Eye broadcast" begin E = Eye(2,3) M = Matrix(E) F = E .+ E @test F isa FillArrays.RectDiagonal @test F == M + M F = E .+ 1 @test F == M .+ 1 E = Eye((SOneTo(2), SOneTo(2))) @test axes(E .+ E) === axes(E) end @testset "Issues" begin @testset "#31" begin @test convert(SparseMatrixCSC{Float64,Int64}, Zeros{Float64}(3, 3)) == spzeros(3, 3) @test sparse(Zeros(4, 2)) == spzeros(4, 2) end @testset "#178" begin @test Zeros(10)'*spzeros(10) == 0 end end @testset "Adjoint/Transpose/permutedims" begin @test Ones{ComplexF64}(5,6)' ≡ transpose(Ones{ComplexF64}(5,6)) ≡ Ones{ComplexF64}(6,5) @test Zeros{ComplexF64}(5,6)' ≡ transpose(Zeros{ComplexF64}(5,6)) ≡ Zeros{ComplexF64}(6,5) @test Fill(1+im, 5, 6)' ≡ Fill(1-im, 6,5) @test transpose(Fill(1+im, 5, 6)) ≡ Fill(1+im, 6,5) @test Ones(5)' isa Adjoint # Vectors still need special dot product @test copy(Ones(5)') ≡ Ones(5)' @test copy(transpose(Ones(5))) ≡ transpose(Ones(5)) @test Fill([1+im 2; 3 4; 5 6], 2,3)' == Fill([1+im 2; 3 4; 5 6]', 3,2) @test transpose(Fill([1+im 2; 3 4; 5 6], 2,3)) == Fill(transpose([1+im 2; 3 4; 5 6]), 3,2) @test permutedims(Ones(10)) ≡ Ones(1,10) @test permutedims(Zeros(10)) ≡ Zeros(1,10) @test permutedims(Fill(2.0,10)) ≡ Fill(2.0,1,10) @test permutedims(Ones(10,3)) ≡ Ones(3,10) @test permutedims(Zeros(10,3)) ≡ Zeros(3,10) @test permutedims(Fill(2.0,10,3)) ≡ Fill(2.0,3,10) @test permutedims(Ones(2,4,5), [3,2,1]) == permutedims(Array(Ones(2,4,5)), [3,2,1]) @test permutedims(Ones(2,4,5), [3,2,1]) ≡ Ones(5,4,2) @test permutedims(Zeros(2,4,5), [3,2,1]) ≡ Zeros(5,4,2) @test permutedims(Fill(2.0,2,4,5), [3,2,1]) ≡ Fill(2.0,5,4,2) @testset "recursive" begin S = SMatrix{2,3}(1:6) Z = Zeros(typeof(S), 2, 3) Y = zeros(typeof(S), 2, 3) @test Z' == Y' @test transpose(Z) == transpose(Y) F = Fill(S, 2, 3) G = fill(S, 2, 3) @test F' == G' @test transpose(F) == transpose(G) end end @testset "reverse" begin for A in (Zeros{Int}(6), Ones(2,3), Fill("abc", 2, 3, 4)) @test reverse(A) == reverse(Array(A)) @test reverse(A, dims=1) == reverse(Array(A), dims=1) end A = Ones{Int}(6) @test reverse(A, 2, 4) == reverse(Array(A), 2, 4) @test_throws BoundsError reverse(A, 1, 10) end @testset "setindex!/fill!" begin F = Fill(1,10) @test (F[1] = 1) == 1 @test_throws BoundsError (F[11] = 1) @test_throws ArgumentError (F[10] = 2) F = Fill(1,10,5) @test (F[1] = 1) == 1 @test (F[3,3] = 1) == 1 @test_throws BoundsError (F[51] = 1) @test_throws BoundsError (F[1,6] = 1) @test_throws ArgumentError (F[10] = 2) @test_throws ArgumentError (F[10,1] = 2) @test (F[:,1] .= 1) == fill(1,10) @test_throws ArgumentError (F[:,1] .= 2) @test fill!(F,1) == F @test_throws ArgumentError fill!(F,2) end @testset "mult" begin @test Fill(2,10)*Fill(3,1,12) == Vector(Fill(2,10))*Matrix(Fill(3,1,12)) @test Fill(2,10)*Fill(3,1,12) ≡ Fill(6,10,12) @test Fill(2,3,10)*Fill(3,10,12) ≡ Fill(60,3,12) @test Fill(2,3,10)*Fill(3,10) ≡ Fill(60,3) @test_throws DimensionMismatch Fill(2,10)*Fill(3,2,12) @test_throws DimensionMismatch Fill(2,3,10)*Fill(3,2,12) f = Fill(1, (Base.IdentityUnitRange(1:3), Base.IdentityUnitRange(1:3))) @test f * f === Fill(size(f,2), axes(f)) f = Fill(2, (Base.IdentityUnitRange(2:3), Base.IdentityUnitRange(2:3))) @test_throws ArgumentError f * f @test Ones(10)*Fill(3,1,12) ≡ Fill(3.0,10,12) @test Ones(10,3)*Fill(3,3,12) ≡ Fill(9.0,10,12) @test Ones(10,3)*Fill(3,3) ≡ Fill(9.0,10) @test Fill(2,10)*Ones(1,12) ≡ Fill(2.0,10,12) @test Fill(2,3,10)*Ones(10,12) ≡ Fill(20.0,3,12) @test Fill(2,3,10)*Ones(10) ≡ Fill(20.0,3) @test Ones(10)*Ones(1,12) ≡ Ones(10,12) @test Ones(3,10)*Ones(10,12) ≡ Fill(10.0,3,12) @test Ones(3,10)*Ones(10) ≡ Fill(10.0,3) @test Zeros(10)*Fill(3,1,12) ≡ Zeros(10,12) @test Zeros(10,3)*Fill(3,3,12) ≡ Zeros(10,12) @test Zeros(10,3)*Fill(3,3) ≡ Zeros(10) @test Fill(2,10)* Zeros(1,12) ≡ Zeros(10,12) @test Fill(2,3,10)*Zeros(10,12) ≡ Zeros(3,12) @test Fill(2,3,10)*Zeros(10) ≡ Zeros(3) @test Zeros(10)*Zeros(1,12) ≡ Zeros(10,12) @test Zeros(3,10)*Zeros(10,12) ≡ Zeros(3,12) @test Zeros(3,10)*Zeros(10) ≡ Zeros(3) f = Zeros((Base.IdentityUnitRange(1:4), Base.IdentityUnitRange(1:4))) @test f * f === f f = Zeros((Base.IdentityUnitRange(3:4), Base.IdentityUnitRange(3:4))) @test_throws ArgumentError f * f @testset "Arrays as elements" begin SMT = SMatrix{2,2,Int,4} SVT = SVector{2,Int} @test @inferred(Zeros{SMT}(0,0) * Fill([1 2; 3 4], 0, 0)) == Zeros{SMT}(0,0) @test @inferred(Zeros{SMT}(4,2) * Fill([1 2; 3 4], 2, 3)) == Zeros{SMT}(4,3) @test @inferred(Fill([1 2; 3 4], 2, 3) * Zeros{SMT}(3, 4)) == Zeros{SMT}(2,4) @test @inferred(Zeros{SMT}(4,2) * Fill([1, 2], 2, 3)) == Zeros{SVT}(4,3) @test @inferred(Fill([1 2], 2, 3) * Zeros{SMT}(3,4)) == Zeros{SMatrix{1,2,Int,2}}(2,4) TSM = SMatrix{2,2,Int,4} s = TSM(1:4) for n in 0:3 v = fill(s, 1) z = zeros(TSM, n) A = @inferred Zeros{TSM}(n) * Diagonal(v) B = z * Diagonal(v) @test A == B w = fill(s, n) A = @inferred Diagonal(w) * Zeros{TSM}(n) B = Diagonal(w) * z @test A == B A = @inferred Zeros{TSM}(2n, n) * Diagonal(w) B = zeros(TSM, 2n, n) * Diagonal(w) @test A == B A = @inferred Diagonal(w) * Zeros{TSM}(n, 2n) B = Diagonal(w) * zeros(TSM, n, 2n) @test A == B end D = Diagonal([[1 2; 3 4], [1 2 3; 4 5 6]]) @test @inferred(Zeros(TSM, 2,2) * D) == zeros(TSM, 2,2) * D D = Diagonal(fill(SMatrix{2,3}(fill(im,6)),1)) Z = Zeros(SMatrix{2,3,ComplexF64,6},1) @test D * Z' == fill(zero(SMatrix{2,2,ComplexF64,4}),1,1) D = Diagonal(fill(zeros(2,3), 2)) Z = Zeros(SMatrix{2,3,Float64,6}, 2) @test Z' * D == Array(Z)' * D S = SMatrix{2,3}(1:6) A = reshape([S,2S,3S,4S],2,2) F = Fill(S',2,2) @test A * F == A * fill(S',size(F)) @test mul!(A * F, A, F, 2, 1) == 3 * A * fill(S',size(F)) @test F * A == fill(S',size(F)) * A @test mul!(F * A, F, A, 2, 1) == 3 * fill(S',size(F)) * A # doubly nested A = [[[1,2]]]' Z = Zeros(SMatrix{1,1,SMatrix{2,2,Int,4},1},1) Z2 = zeros(SMatrix{1,1,SMatrix{2,2,Int,4},1},1) @test A * Z == A * Z2 x = [1 2 3; 4 5 6] A = reshape([x,2x,3x,4x],2,2) F = Fill(x,2,2) @test A' * F == A' * fill(x,size(F)) @test mul!(A' * F, A', F, 2, 1) == 3 * A' * fill(x,size(F)) end for W in (zeros(3,4), @MMatrix zeros(3,4)) mW, nW = size(W) @test mul!(W, Fill(2,mW,5), Fill(3,5,nW)) ≈ Fill(30,mW,nW) ≈ fill(2,mW,5) * fill(3,5,nW) W .= 2 @test mul!(W, Fill(2,mW,5), Fill(3,5,nW), 1.0, 2.0) ≈ Fill(30,mW,nW) .+ 4 ≈ fill(2,mW,5) * fill(3,5,nW) .+ 4 end for w in (zeros(5), @MVector zeros(5)) mw = size(w,1) @test mul!(w, Fill(2,mw,5), Fill(3,5)) ≈ Fill(30,mw) ≈ fill(2,mw,5) * fill(3,5) w .= 2 @test mul!(w, Fill(2,mw,5), Fill(3,5), 1.0, 2.0) ≈ Fill(30,mw) .+ 4 ≈ fill(2,mw,5) * fill(3,5) .+ 4 end @testset "strided" begin @testset for (la, (mA, nA)) in [(3, (1,4)), (0, (1,4)), (3, (1, 0))] a = randn(la) na = 1 A = randn(mA,nA) @test Fill(2,3)*A ≈ Vector(Fill(2,3))*A @test Fill(2,0)*A ≈ Vector(Fill(2,0))*A @test Fill(2,3,mA)*A ≈ mul!(similar(A, 3,nA), Fill(2,3,mA), A) ≈ Matrix(Fill(2,3,mA))*A @test Fill(2,3,la)*a ≈ mul!(similar(a, 3), Fill(2,3,la), a) ≈ Matrix(Fill(2,3,la))*a @test Fill(2,3,la)*a isa Fill @test Ones(3)*A ≈ Vector(Ones(3))*A @test Ones(3,mA)*A ≈ mul!(similar(A, 3, nA), Ones(3,mA), A) ≈ Matrix(Ones(3,mA))*A @test Ones(3,la)*a ≈ mul!(similar(a, 3), Ones(3,la), a) ≈ Matrix(Ones(3,la))*a @test Ones(3,la)*a isa Fill @test Zeros(3)*A ≡ Zeros(3,nA) @test Zeros(3,mA)*A == mul!(similar(A, 3, nA), Zeros(3,mA), A) == Zeros(3,nA) @test Zeros(3,la)*a == mul!(similar(A, 3), Zeros(3,la), a) == Zeros(3) @test A*Fill(2,nA) ≈ A*Vector(Fill(2,nA)) @test A*Fill(2,nA,1) ≈ A*Matrix(Fill(2,nA,1)) @test a*Fill(2,na,3) ≈ a*Matrix(Fill(2,na,3)) @test A*Fill(2,nA,0) ≈ A*Matrix(Fill(2,nA,0)) @test a*Fill(2,na,0) ≈ a*Matrix(Fill(2,na,0)) @test A*Ones(nA) ≈ A*Vector(Ones(nA)) @test A*Ones(nA,1) ≈ A*Matrix(Ones(nA,1)) @test a*Ones(na,3) ≈ a*Matrix(Ones(na,3)) @test A*Zeros(nA) ≡ Zeros(mA) @test A*Zeros(nA,1) ≡ Zeros(mA,1) @test a*Zeros(na,3) ≡ Zeros(la,3) @test transpose(A) * Zeros(mA) ≡ Zeros(nA) @test A' * Zeros(mA) ≡ Zeros(nA) @test transpose(a) * Zeros(la, 3) ≡ transpose(Zeros(3)) @test a' * Zeros(la,3) ≡ adjoint(Zeros(3)) @test Zeros(la)' * Transpose(Adjoint(a)) == 0.0 F = Fill(2, mA, 3) @test transpose(A) * F ≈ transpose(Fill(2, 3, mA) * A) F = Fill(2, la, 3) FS = Fill(2, (Base.OneTo(la), SOneTo(3))) @testset for (adjf, adjT) in ((transpose, Transpose), (adjoint, Adjoint)) @test adjf(a) * F ≈ adjf(Fill(2, 3, la) * a) @test adjf(a) * F isa adjT{<:Any, <:Fill{<:Any,1}} @test adjf(a) * FS ≈ adjf(Fill(2, 3, la) * a) @test axes(adjf(a) * FS, 2) == SOneTo(3) end w = zeros(mA) @test mul!(w, A, Fill(2,nA), true, false) ≈ A * fill(2,nA) w .= 2 @test mul!(w, A, Fill(2,nA), 1.0, 1.0) ≈ A * fill(2,nA) .+ 2 nW = 3 W = zeros(mA, nW) @test mul!(W, A, Fill(2,nA,nW), true, false) ≈ A * fill(2,nA,nW) W .= 2 @test mul!(W, A, Fill(2,nA,nW), 1.0, 1.0) ≈ A * fill(2,nA,nW) .+ 2 mW = 5 W = zeros(mW, nA) @test mul!(W, Fill(2,mW,mA), A, true, false) ≈ fill(2,mW,mA) * A W .= 2 @test mul!(W, Fill(2,mW,mA), A, 1.0, 1.0) ≈ fill(2,mW,mA) * A .+ 2 mw = 5 w = zeros(mw) @test mul!(w, Fill(2,mw,la), a, true, false) ≈ fill(2,mw,la) * a w .= 2 @test mul!(w, Fill(2,mw,la), a, 1.0, 1.0) ≈ fill(2,mw,la) * a .+ 2 @testset for f in [adjoint, transpose] w = zeros(nA) @test mul!(w, f(A), Fill(2,mA), true, false) ≈ f(A) * fill(2,mA) w .= 2 @test mul!(w, f(A), Fill(2,mA), 1.0, 1.0) ≈ f(A) * fill(2,mA) .+ 2 W = zeros(nA, nW) @test mul!(W, f(A), Fill(2,mA,nW), true, false) ≈ f(A) * fill(2,mA,nW) W .= 2 @test mul!(W, f(A), Fill(2,mA,nW), 1.0, 1.0) ≈ f(A) * fill(2,mA,nW) .+ 2 W = zeros(mW, mA) @test mul!(W, Fill(2,mW,nA), f(A), true, false) ≈ fill(2,mW,nA) * f(A) W .= 2 @test mul!(W, Fill(2,mW,nA), f(A), 1.0, 1.0) ≈ fill(2,mW,nA) * f(A) .+ 2 end end end D = Diagonal(randn(1)) @test Zeros(1,1)*D ≡ Zeros(1,1) @test Zeros(1)*D ≡ Zeros(1,1) @test D*Zeros(1,1) ≡ Zeros(1,1) @test D*Zeros(1) ≡ Zeros(1) D = Diagonal(Fill(2,10)) @test D * Ones(10) ≡ Fill(2.0,10) @test D * Ones(10,5) ≡ Fill(2.0,10,5) @test Ones(5,10) * D ≡ Fill(2.0,5,10) # following test is broken in Base as of Julia v1.5 @test_throws DimensionMismatch Diagonal(Fill(1,1)) * Ones(10) @test_throws DimensionMismatch Diagonal(Fill(1,1)) * Ones(10,5) @test_throws DimensionMismatch Ones(5,10) * Diagonal(Fill(1,1)) E = Eye(5) @test E*(1:5) ≡ 1.0:5.0 @test (1:5)'E == (1.0:5)' @test E*E ≡ E n = 10 k = 12 m = 15 for T in (Float64, ComplexF64) Ank = rand(T, n, k) Akn = rand(T, k, n) Ak = rand(T, k) onesm = ones(m) zerosm = zeros(m) fv = T == Float64 ? T(1.6) : T(1.6, 1.3) for (fillvec, fillmat) in ((Fill(fv, k), Fill(fv, k, m)), (Ones(T, k), Ones(T, k, m)), (Zeros(T, k), Zeros(T, k, m))) Afillvec = Array(fillvec) Afillmat = Array(fillmat) @test Ank * fillvec ≈ Ank * Afillvec @test Ank * fillmat ≈ Ank * Afillmat for A in (Akn, Ak) @test transpose(A)*fillvec ≈ transpose(A)*Afillvec AtF = transpose(A)*fillmat AtM = transpose(A)*Afillmat @test AtF ≈ AtM @test AtF * Ones(m) ≈ AtM * onesm @test AtF * Zeros(m) ≈ AtM * zerosm @test adjoint(A)*fillvec ≈ adjoint(A)*Afillvec AadjF = adjoint(A)*fillmat AadjM = adjoint(A)*Afillmat @test AadjF ≈ AadjM @test AadjF * Ones(m) ≈ AadjM * onesm @test AadjF * Zeros(m) ≈ AadjM * zerosm end end # inplace C = F * A' * alpha + C * beta F = Fill(fv, m, k) M = Array(F) C = rand(T, m, n) @testset for f in (adjoint, transpose) @test mul!(copy(C), F, f(Ank)) ≈ mul!(copy(C), M, f(Ank)) @test mul!(copy(C), F, f(Ank), 1.0, 2.0) ≈ mul!(copy(C), M, f(Ank), 1.0, 2.0) end end @testset "non-commutative" begin A = Fill(quat(rand(4)...), 2, 2) M = Array(A) α, β = quat(0,1,1,0), quat(1,0,0,1) @testset "matvec" begin f = Fill(quat(rand(4)...), size(A,2)) v = Array(f) D = copy(v) exp_res = M * v * α + D * β @test mul!(copy(D), A, f, α, β) ≈ mul!(copy(D), M, v, α, β) ≈ exp_res @test mul!(copy(D), M, f, α, β) ≈ mul!(copy(D), M, v, α, β) ≈ exp_res @test mul!(copy(D), A, v, α, β) ≈ mul!(copy(D), M, v, α, β) ≈ exp_res @test mul!(copy(D), M', f, α, β) ≈ mul!(copy(D), M', v, α, β) ≈ M' * v * α + D * β @test mul!(copy(D), transpose(M), f, α, β) ≈ mul!(copy(D), transpose(M), v, α, β) ≈ transpose(M) * v * α + D * β end @testset "matmat" begin B = Fill(quat(rand(4)...), 2, 2) N = Array(B) D = copy(N) exp_res = M * N * α + D * β @test mul!(copy(D), A, B, α, β) ≈ mul!(copy(D), M, N, α, β) ≈ exp_res @test mul!(copy(D), M, B, α, β) ≈ mul!(copy(D), M, N, α, β) ≈ exp_res @test mul!(copy(D), A, N, α, β) ≈ mul!(copy(D), M, N, α, β) ≈ exp_res @test mul!(copy(D), M', B, α, β) ≈ mul!(copy(D), M', N, α, β) ≈ M' * N * α + D * β @test mul!(copy(D), transpose(M), B, α, β) ≈ mul!(copy(D), transpose(M), N, α, β) ≈ transpose(M) * N * α + D * β @test mul!(copy(D), A, N', α, β) ≈ mul!(copy(D), M, N', α, β) ≈ M * N' * α + D * β @test mul!(copy(D), A, transpose(N), α, β) ≈ mul!(copy(D), M, transpose(N), α, β) ≈ M * transpose(N) * α + D * β end end @testset "ambiguities" begin UT33 = UpperTriangular(ones(3,3)) UT11 = UpperTriangular(ones(1,1)) @test transpose(Zeros(3)) * Transpose(Adjoint([1,2,3])) == 0 @test Zeros(3)' * Adjoint(Transpose([1,2,3])) == 0 @test Zeros(3)' * UT33 == Zeros(3)' @test transpose(Zeros(3)) * UT33 == transpose(Zeros(3)) @test UT11 * Zeros(3)' == Zeros(1,3) @test UT11 * transpose(Zeros(3)) == Zeros(1,3) @test Zeros(2,3) * UT33 == Zeros(2,3) @test UT33 * Zeros(3,2) == Zeros(3,2) @test UT33 * Zeros(3) == Zeros(3) @test Diagonal([1]) * transpose(Zeros(3)) == Zeros(1,3) @test Diagonal([1]) * Zeros(3)' == Zeros(1,3) end end @testset "count" begin @test count(Ones{Bool}(10)) == count(Fill(true,10)) == 10 @test count(Zeros{Bool}(10)) == count(Fill(false,10)) == 0 @test count(x -> 1 ≤ x < 2, Fill(1.3,10)) == 10 @test count(x -> 1 ≤ x < 2, Fill(2.0,10)) == 0 end @testset "norm" begin for a in (Zeros{Int}(5), Zeros(5,3), Zeros(2,3,3), Zeros{ComplexF32}(5), Ones{Int}(5), Ones(5,3), Ones(2,3,3), Ones{ComplexF32}(5), Fill(2.3,5), Fill([2.3,4.2],5), Fill(4), Fill(2f0+3f0im,5)), p in (-Inf, 0, 0.1, 1, 2, 3, Inf) @test @inferred(norm(a,p)) ≈ norm(Array(a),p) @test typeof(norm(a,p)) == typeof(norm(Array(a),p)) end end @testset "kron" begin for T in (Fill, Zeros, Ones), sz in ((2,), (2,2)) f = T{Int}((T == Fill ? (3,sz...) : sz)...) g = Ones{Int}(2) z = Zeros{Int}(2) fc = collect(f) gc = collect(g) zc = collect(z) @test kron(f, f) == kron(fc, fc) @test kron(f, f) isa T{Int,length(sz)} @test kron(f, g) == kron(fc, gc) @test kron(f, g) isa AbstractFill{Int,length(sz)} @test kron(g, f) == kron(gc, fc) @test kron(g, f) isa AbstractFill{Int,length(sz)} @test kron(f, z) == kron(fc, zc) @test kron(f, z) isa AbstractFill{Int,length(sz)} @test kron(z, f) == kron(zc, fc) @test kron(z, f) isa AbstractFill{Int,length(sz)} @test kron(f, f .+ 0.5) == kron(fc, fc .+ 0.5) @test kron(f, f .+ 0.5) isa AbstractFill{Float64,length(sz)} @test kron(f, g .+ 0.5) isa AbstractFill{Float64,length(sz)} end for m in (Fill(2,2,2), "a"), sz in ((2,2), (2,)) f = Fill(m, sz) g = fill(m, sz) @test kron(f, f) == kron(g, g) end @test_throws MethodError kron(Fill("a",2), Zeros(1)) # can't multiply String and Float64 E = Eye(2) K = kron(E, E) @test K isa Diagonal @test K isa typeof(E) C = collect(E) @test K == kron(C, C) E = Eye(2,3) K = kron(E, E) C = collect(E) @test K == kron(C, C) @test issparse(kron(E,E)) E = RectDiagonal(Fill(4,3), (6,3)) C = collect(E) K = kron(E, E) @test K == kron(C, C) @test issparse(K) end @testset "dot products" begin n = 15 o = Ones(1:n) z = Zeros(1:n) D = Diagonal(o) Z = Diagonal(z) Random.seed!(5) u = rand(n) v = rand(n) c = rand(ComplexF16, n) @test dot(u, D, v) == dot(u, v) @test dot(u, 2D, v) == 2dot(u, v) @test dot(u, Z, v) == 0 @test @inferred(dot(Zeros(5), Zeros{ComplexF16}(5))) ≡ zero(ComplexF64) @test @inferred(dot(Zeros(5), Ones{ComplexF16}(5))) ≡ zero(ComplexF64) @test abs(@inferred(dot(Ones{ComplexF16}(5), Zeros(5)))) ≡ abs(@inferred(dot(randn(5), Zeros{ComplexF16}(5)))) ≡ abs(@inferred(dot(Zeros{ComplexF16}(5), randn(5)))) ≡ zero(Float64) # 0.0 !≡ -0.0 @test @inferred(dot(c, Fill(1 + im, 15))) ≡ (@inferred(dot(Fill(1 + im, 15), c)))' ≈ @inferred(dot(c, fill(1 + im, 15))) @test @inferred(dot(Fill(1,5), Fill(2.0,5))) ≡ 10.0 @test_skip dot(Fill(true,5), Fill(Int8(1),5)) isa Int8 # not working at present let N = 2^big(1000) # fast dot for fast sum @test dot(Fill(2,N),1:N) == dot(Fill(2,N),1:N) == dot(1:N,Fill(2,N)) == 2*sum(1:N) end @test dot(1:4, Eye(4), 1:4) === dot(1:4, oneunit(eltype(Eye(4))) * I(4), 1:4) @test_throws DimensionMismatch dot(u[1:end-1], D, v) @test_throws DimensionMismatch dot(u[1:end-1], D, v[1:end-1]) @test_throws DimensionMismatch dot(u, 2D, v[1:end-1]) @test_throws DimensionMismatch dot(u, 2D, v[1:end-1]) @test_throws DimensionMismatch dot(u, Z, v[1:end-1]) @test_throws DimensionMismatch dot(u, Z, v[1:end-1]) @test_throws DimensionMismatch dot(Zeros(5), Zeros(6)) @test_throws DimensionMismatch dot(Zeros(5), randn(6)) end @testset "print" begin # 3-arg show, full printing @test stringmime("text/plain", Zeros(3)) == "3-element Zeros{Float64}" @test stringmime("text/plain", Ones(3)) == "3-element Ones{Float64}" @test stringmime("text/plain", Fill(7,2)) == "2-element Fill{$Int}, with entries equal to 7" @test stringmime("text/plain", Zeros(3,2)) == "3×2 Zeros{Float64}" @test stringmime("text/plain", Ones(3,2)) == "3×2 Ones{Float64}" @test stringmime("text/plain", Fill(7,2,3)) == "2×3 Fill{$Int}, with entries equal to 7" @test stringmime("text/plain", Fill(8.0,1)) == "1-element Fill{Float64}, with entry equal to 8.0" @test stringmime("text/plain", Eye(5)) == "5×5 Eye{Float64}" # used downstream in LazyArrays.jl to deduce sparsity @test Base.replace_in_print_matrix(Zeros(5,3), 1, 2, "0.0") == " ⋅ " # 2-arg show, compact printing @test repr(Zeros{Int}()) == "Zeros{$Int}()" @test repr(Zeros{Int}(3)) == "Zeros{$Int}(3)" @test repr(Zeros(3)) == "Zeros(3)" @test repr(Ones{Int}(3)) == "Ones{$Int}(3)" @test repr(Ones{Int}(3,2)) == "Ones{$Int}(3, 2)" @test repr(Ones(3,2)) == "Ones(3, 2)" @test repr(Fill(7,3,2)) == "Fill(7, 3, 2)" @test repr(Fill(1f0,10)) == "Fill(1.0f0, 10)" # Float32! @test repr(Fill(0)) == "Fill(0)" @test repr(Eye(9)) == "Eye(9)" @test repr(Eye(9,4)) == "Eye(9,4)" # also used for arrays of arrays: @test occursin("Eye(2) ", stringmime("text/plain", [Eye(2) for i in 1:2, j in 1:2])) end @testset "reshape" begin @test reshape(Fill(2,6),2,3) ≡ reshape(Fill(2,6),(2,3)) ≡ reshape(Fill(2,6),:,3) ≡ reshape(Fill(2,6),2,:) ≡ Fill(2,2,3) @test reshape(Fill(2,6),big(2),3) == reshape(Fill(2,6), (big(2),3)) == reshape(Fill(2,6), big(2),:) == Fill(2,big(2),3) @test_throws DimensionMismatch reshape(Fill(2,6),2,4) @test reshape(Ones(6),2,3) ≡ reshape(Ones(6),(2,3)) ≡ reshape(Ones(6),:,3) ≡ reshape(Ones(6),2,:) ≡ Ones(2,3) @test reshape(Zeros(6),2,3) ≡ Zeros(2,3) @test reshape(Zeros(6),big(2),3) == Zeros(big(2),3) @test reshape(Fill(2,2,3),Val(1)) ≡ Fill(2,6) @test reshape(Fill(2, 2), (2, )) ≡ Fill(2, 2) @test reshape(Fill(2,3), :) === reshape(Fill(2,3), (:,)) === Fill(2,3) end @testset "lmul!/rmul!" begin z = Zeros(1_000_000_000_000) @test lmul!(2.0,z) === z @test rmul!(z,2.0) === z @test_throws ArgumentError lmul!(Inf,z) @test_throws ArgumentError rmul!(z,Inf) x = Fill([1,2],1_000_000_000_000) @test lmul!(1.0,x) === x @test rmul!(x,1.0) === x @test_throws ArgumentError lmul!(2.0,x) @test_throws ArgumentError rmul!(x,2.0) end @testset "Modified" begin @testset "Diagonal{<:Fill}" begin D = Diagonal(Fill(Fill(0.5,2,2),10)) @test @inferred(D[1,1]) === Fill(0.5,2,2) @test @inferred(D[1,2]) === Fill(0.0,2,2) @test axes(D) == (Base.OneTo(10),Base.OneTo(10)) D = Diagonal(Fill(Zeros(2,2),10)) @test @inferred(D[1,1]) === Zeros(2,2) @test @inferred(D[1,2]) === Zeros(2,2) D = Diagonal([Zeros(1,1), Zeros(2,2)]) @test @inferred(D[1,1]) === Zeros(1,1) @test @inferred(D[1,2]) === Zeros(1,2) @test_throws ArgumentError Diagonal(Fill(Ones(2,2),10))[1,2] end @testset "Triangular" begin U = UpperTriangular(Ones(3,3)) @test U == UpperTriangular(ones(3,3)) @test axes(U) == (Base.OneTo(3),Base.OneTo(3)) end end @testset "Trues" begin @test Trues(2,3) == Trues((2,3)) == trues(2,3) @test Falses(2,3) == Falses((2,3)) == falses(2,3) dim = (4,5) mask = Trues(dim) x = randn(dim) @test x[mask] == vec(x) # getindex y = similar(x) y[mask] = x # setindex! @test y == x @test_throws BoundsError ones(3)[Trues(2)] @test_throws BoundsError setindex!(ones(3), zeros(3), Trues(2)) @test_throws DimensionMismatch setindex!(ones(2), zeros(3), Trues(2)) @test Ones(3)[Trues(3)] == Ones(3) @test_throws BoundsError Ones(3)[Trues(2)] @test Ones(2,3)[Trues(2,3)] == Ones(6) @test Ones(2,3)[Trues(6)] == Ones(6) @test_throws BoundsError Ones(2,3)[Trues(3,2)] end @testset "FillArray interface" begin @testset "SubArray" begin a = Fill(2.0,5) v = SubArray(a,(1:2,)) @test FillArrays.getindex_value(v) == FillArrays.unique_value(v) == 2.0 @test convert(Fill, v) ≡ Fill(2.0,2) end @testset "views" begin a = Fill(2.0,5) v = view(a,1:2) @test v isa Fill @test FillArrays.getindex_value(v) == FillArrays.unique_value(v) == 2.0 @test convert(Fill, v) ≡ Fill(2.0,2) @test view(a,1) ≡ Fill(2.0) @test view(a,1,1) ≡ Fill(2.0) @test view(a, :) === a @test view(a, CartesianIndices(a)) === a vv = view(a, CartesianIndices(a), :, 1) @test ndims(vv) == 2 @test vv isa Fill && FillArrays.getindex_value(vv) == 2.0 vv = view(a, CartesianIndices(a), :, 1:1) @test ndims(vv) == 3 @test vv isa Fill && FillArrays.getindex_value(vv) == 2.0 end @testset "view with bool" begin a = Fill(2.0,5) @test a[[true,false,false,true,false]] ≡ view(a,[true,false,false,true,false]) a = Fill(2.0,2,2) @test a[[true false; false true]] ≡ view(a, [true false; false true]) end @testset "adjtrans" begin a = Fill(2.0+im, 5) @test FillArrays.getindex_value(a') == FillArrays.unique_value(a') == 2.0 - im @test convert(Fill, a') ≡ Fill(2.0-im,1,5) @test FillArrays.getindex_value(transpose(a)) == FillArrays.unique_value(transpose(a)) == 2.0 + im @test convert(Fill, transpose(a)) ≡ Fill(2.0+im,1,5) end @testset "custom AbstractFill types" begin # implicit axes struct StaticZerosVec{L,T} <: FillArrays.AbstractZeros{T,1,Tuple{SOneTo{L}}} end Base.size(::StaticZerosVec{L}) where {L} = (L,) Base.axes(::StaticZerosVec{L}) where {L} = (SOneTo(L),) S = StaticZerosVec{3,Int}() @test real.(S) == S @test imag.(S) == S struct StaticOnesVec{L,T} <: FillArrays.AbstractOnes{T,1,Tuple{SOneTo{L}}} end Base.size(::StaticOnesVec{L}) where {L} = (L,) Base.axes(::StaticOnesVec{L}) where {L} = (SOneTo(L),) S = StaticOnesVec{3,Int}() @test real.(S) == S @test imag.(S) == zero(S) struct StaticFill{S1,S2,T} <: FillArrays.AbstractFill{T,2,Tuple{SOneTo{S1},SOneTo{S2}}} x :: T end StaticFill{S1,S2}(x::T) where {S1,S2,T} = StaticFill{S1,S2,T}(x) Base.size(::StaticFill{S1,S2}) where {S1,S2} = (S1,S2) Base.axes(::StaticFill{S1,S2}) where {S1,S2} = (SOneTo(S1), SOneTo(S2)) FillArrays.getindex_value(S::StaticFill) = S.x S = StaticFill{2,3}(2) @test permutedims(S) == Fill(2, reverse(size(S))) end end @testset "Statistics" begin @test mean(Fill(3,4,5)) === mean(fill(3,4,5)) @test std(Fill(3,4,5)) === std(fill(3,4,5)) @test var(Trues(5)) === var(trues(5)) @test mean(Trues(5)) === mean(trues(5)) @test mean(sqrt, Fill(3,4,5)) ≈ mean(sqrt, fill(3,4,5)) @test mean(Fill(3,4,5), dims=2) == mean(fill(3,4,5), dims=2) @test std(Fill(3,4,5), corrected=true, mean=3) == std(fill(3,4,5), corrected=true, mean=3) @test cov(Fill(3,4)) === cov(fill(3,4)) @test cov(Fill(3,4,5)) == cov(fill(3,4,5)) @test cov(Fill(3,4,5), dims=2) == cov(fill(3,4,5), dims=2) @test cor(Fill(3,4)) == cor(fill(3,4)) @test cor(Fill(3, 4, 5)) ≈ cor(fill(3, 4, 5)) nans=true @test cor(Fill(3, 4, 5), dims=2) ≈ cor(fill(3, 4, 5), dims=2) nans=true end @testset "Structured broadcast" begin D = Diagonal(1:5) @test D + Zeros(5,5) isa Diagonal @test D - Zeros(5,5) isa Diagonal @test D .+ Zeros(5,5) isa Diagonal @test D .- Zeros(5,5) isa Diagonal @test D .* Zeros(5,5) isa Diagonal @test Zeros(5,5) .* D isa Diagonal @test Zeros(5,5) - D isa Diagonal @test Zeros(5,5) + D isa Diagonal @test Zeros(5,5) .- D isa Diagonal @test Zeros(5,5) .+ D isa Diagonal f = (x,y) -> x+1 @test f.(D, Zeros(5,5)) isa Matrix end @testset "OneElement" begin A = OneElement(2, (), ()) @test FillArrays.nzind(A) == CartesianIndex() @test A == Fill(2, ()) @test A[] === 2 @test A[1] === A[1,1] === 2 e₁ = OneElement(2, 5) @test e₁ == [0,1,0,0,0] @test FillArrays.nzind(e₁) == CartesianIndex(2) @test e₁[2] === e₁[2,1] === e₁[2,1,1] === 1 @test_throws BoundsError e₁[6] @test -e₁ === OneElement(-1, 2, 5) f₁ = AbstractArray{Float64}(e₁) @test f₁ isa OneElement{Float64,1} @test f₁ == e₁ fv₁ = AbstractVector{Float64}(e₁) @test fv₁ isa OneElement{Float64,1} @test fv₁ == e₁ @test stringmime("text/plain", e₁) == "5-element OneElement{$Int, 1, Tuple{$Int}, Tuple{Base.OneTo{$Int}}}:\n ⋅\n 1\n ⋅\n ⋅\n ⋅" e₁ = OneElement{Float64}(2, 5) @test e₁ == [0,1,0,0,0] v = OneElement{Float64}(2, 3, 4) @test v == [0,0,2,0] V = OneElement(2, (2,3), (3,4)) @test V == [0 0 0 0; 0 0 2 0; 0 0 0 0] @test FillArrays.nzind(V) == CartesianIndex(2,3) @test -V == OneElement(-2, (2,3), (3,4)) Vf = AbstractArray{Float64}(V) @test Vf isa OneElement{Float64,2} @test Vf == V VMf = AbstractMatrix{Float64}(V) @test VMf isa OneElement{Float64,2} @test VMf == V @test stringmime("text/plain", V) == "3×4 OneElement{$Int, 2, Tuple{$Int, $Int}, Tuple{Base.OneTo{$Int}, Base.OneTo{$Int}}}:\n ⋅ ⋅ ⋅ ⋅\n ⋅ ⋅ 2 ⋅\n ⋅ ⋅ ⋅ ⋅" @test Base.setindex(Zeros(5), 2, 2) ≡ OneElement(2.0, 2, 5) @test Base.setindex(Zeros(5,3), 2, 2, 3) ≡ OneElement(2.0, (2,3), (5,3)) @test_throws BoundsError Base.setindex(Zeros(5), 2, 6) @testset "non-numeric" begin S = SMatrix{2,2}(1:4) A = OneElement(S, (2,2), (2,2)) @test A[2,2] === S @test A[1,1] === A[1,2] === A[2,1] === zero(S) end @testset "Vector indexing" begin @testset "1D" begin A = OneElement(2, 2, 4) @test @inferred(A[:]) === @inferred(A[axes(A)...]) === A @test @inferred(A[3:4]) isa OneElement{Int,1} @test @inferred(A[3:4]) == Zeros(2) @test @inferred(A[1:2]) === OneElement(2, 2, 2) @test @inferred(A[2:3]) === OneElement(2, 1, 2) @test @inferred(A[Base.IdentityUnitRange(2:3)]) isa OneElement{Int,1} @test @inferred(A[Base.IdentityUnitRange(2:3)]) == OneElement(2,(2,),(Base.IdentityUnitRange(2:3),)) @test A[:,:] == reshape(A, size(A)..., 1) @test A[reverse(axes(A,1))] == A[collect(reverse(axes(A,1)))] @testset "repeated indices" begin @test A[StepRangeLen(2, 0, 3)] == A[fill(2, 3)] end B = OneElement(2, (2,), (Base.IdentityUnitRange(-1:4),)) @test @inferred(A[:]) === @inferred(A[axes(A)...]) === A @test @inferred(A[3:4]) isa OneElement{Int,1} @test @inferred(A[3:4]) == Zeros(2) @test @inferred(A[2:3]) === OneElement(2, 1, 2) C = OneElement(2, (2,), (Base.OneTo(big(4)),)) @test @inferred(C[1:4]) === OneElement(2, 2, 4) D = OneElement(2, (2,), (InfiniteArrays.OneToInf(),)) D2 = D[:] @test axes(D2) == axes(D) @test D2[2] == D[2] D3 = D[axes(D)...] @test axes(D3) == axes(D) @test D3[2] == D[2] end @testset "2D" begin A = OneElement(2, (2,3), (4,5)) @test @inferred(A[:,:]) === @inferred(A[axes(A)...]) === A @test @inferred(A[:,1]) isa OneElement{Int,1} @test @inferred(A[:,1]) == Zeros(4) @test A[:, Int64(1)] === A[:, Int32(1)] @test @inferred(A[1,:]) isa OneElement{Int,1} @test @inferred(A[1,:]) == Zeros(5) @test @inferred(A[:,3]) === OneElement(2, 2, 4) @test @inferred(A[2,:]) === OneElement(2, 3, 5) @test @inferred(A[1:1,:]) isa OneElement{Int,2} @test @inferred(A[1:1,:]) == Zeros(1,5) @test @inferred(A[4:4,:]) isa OneElement{Int,2} @test @inferred(A[4:4,:]) == Zeros(1,5) @test @inferred(A[2:2,:]) === OneElement(2, (1,3), (1,5)) @test @inferred(A[1:4,:]) === OneElement(2, (2,3), (4,5)) @test @inferred(A[:,3:3]) === OneElement(2, (2,1), (4,1)) @test @inferred(A[:,1:5]) === OneElement(2, (2,3), (4,5)) @test @inferred(A[1:4,1:4]) === OneElement(2, (2,3), (4,4)) @test @inferred(A[2:4,2:4]) === OneElement(2, (1,2), (3,3)) @test @inferred(A[2:4,3:4]) === OneElement(2, (1,1), (3,2)) @test @inferred(A[4:4,5:5]) isa OneElement{Int,2} @test @inferred(A[4:4,5:5]) == Zeros(1,1) @test @inferred(A[Base.IdentityUnitRange(2:4), :]) isa OneElement{Int,2} @test axes(A[Base.IdentityUnitRange(2:4), :]) == (Base.IdentityUnitRange(2:4), axes(A,2)) @test @inferred(A[:,:,:]) == reshape(A, size(A)...,1) B = OneElement(2, (2,3), (Base.IdentityUnitRange(2:4),Base.IdentityUnitRange(2:5))) @test @inferred(B[:,:]) === @inferred(B[axes(B)...]) === B @test @inferred(B[:,3]) === OneElement(2, (2,), (Base.IdentityUnitRange(2:4),)) @test @inferred(B[3:4, 4:5]) isa OneElement{Int,2} @test @inferred(B[3:4, 4:5]) == Zeros(2,2) b = @inferred(B[Base.IdentityUnitRange(3:4), Base.IdentityUnitRange(4:5)]) @test b == Zeros(axes(b)) C = OneElement(2, (2,3), (Base.OneTo(big(4)), Base.OneTo(big(5)))) @test @inferred(C[1:4, 1:5]) === OneElement(2, (2,3), Int.(size(C))) D = OneElement(2, (2,3), (InfiniteArrays.OneToInf(), InfiniteArrays.OneToInf())) D2 = @inferred D[:,:] @test axes(D2) == axes(D) @test D2[2,3] == D[2,3] D3 = @inferred D[axes(D)...] @test axes(D3) == axes(D) @test D3[2,3] == D[2,3] end end @testset "adjoint/transpose" begin A = OneElement(3im, (2,4), (4,6)) @test A' === OneElement(-3im, (4,2), (6,4)) @test transpose(A) === OneElement(3im, (4,2), (6,4)) A = OneElement(3im, 2, 3) @test A' isa Adjoint @test transpose(A) isa Transpose @test A' == OneElement(-3im, (1,2), (1,3)) @test transpose(A) == OneElement(3im, (1,2), (1,3)) A = OneElement(3, (2,2), (4,4)) @test adjoint(A) === A @test transpose(A) === A A = OneElement(3, 2, 4) @test transpose(A) isa Transpose @test adjoint(A) isa Adjoint @test transpose(A) == OneElement(3, (1,2), (1,4)) @test adjoint(A) == OneElement(3, (1,2), (1,4)) end @testset "reshape" begin for O in (OneElement(2, (2,3), (4,5)), OneElement(2, (2,), (20,)), OneElement(2, (1,2,2), (2,2,5))) A = Array(O) for shp in ((2,5,2), (5,1,4), (20,), (2,2,5,1,1)) @test reshape(O, shp) == reshape(A, shp) end end O = OneElement(2, (), ()) @test reshape(O, ()) === O O = OneElement(5, 3) @test reshape(O, 1, 3) == reshape(Array(O), 1, 3) @test reshape(reshape(O, 1, 3), 3) == O end @testset "isassigned" begin f = OneElement(2, (3,3), (4,4)) @test !isassigned(f, 0, 0) @test isassigned(f, 2, 2) @test !isassigned(f, 10, 10) @test_throws ArgumentError isassigned(f, true) end @testset "matmul" begin A = reshape(Float64[1:9;], 3, 3) v = reshape(Float64[1:3;], 3) testinds(w::AbstractArray) = testinds(size(w)) testinds(szw::Tuple{Int}) = (szw .- 1, szw .+ 1) function testinds(szA::Tuple{Int,Int}) (szA .- 1, szA .+ (-1,0), szA .+ (0,-1), szA .+ 1, szA .+ (1,-1), szA .+ (-1,1)) end # test matvec if w is a vector, or matmat if w is a matrix function test_mat_mul_OneElement(A, (w, w2), sz) @testset for ind in testinds(sz) x = OneElement(3, ind, sz) xarr = Array(x) Axarr = A * xarr Aadjxarr = A' * xarr @test A * x ≈ Axarr @test A' * x ≈ Aadjxarr @test transpose(A) * x ≈ transpose(A) * xarr @test mul!(w, A, x) ≈ Axarr # check columnwise to ensure zero columns @test all(((c1, c2),) -> c1 ≈ c2, zip(eachcol(w), eachcol(Axarr))) @test mul!(w, A', x) ≈ Aadjxarr w .= 1 @test mul!(w, A, x, 1.0, 2.0) ≈ Axarr .+ 2 w .= 1 @test mul!(w, A', x, 1.0, 2.0) ≈ Aadjxarr .+ 2 F = Fill(3, size(A)) w2 .= 1 @test mul!(w2, F, x, 1.0, 1.0) ≈ Array(F) * xarr .+ 1 end end function test_OneElementMatrix_mul_mat(A, (w, w2), sz) @testset for ind in testinds(sz) O = OneElement(3, ind, sz) Oarr = Array(O) OarrA = Oarr * A OarrAadj = Oarr * A' @test O * A ≈ OarrA @test O * A' ≈ OarrAadj @test O * transpose(A) ≈ Oarr * transpose(A) @test mul!(w, O, A) ≈ OarrA # check columnwise to ensure zero columns @test all(((c1, c2),) -> c1 ≈ c2, zip(eachcol(w), eachcol(OarrA))) @test mul!(w, O, A') ≈ OarrAadj w .= 1 @test mul!(w, O, A, 1.0, 2.0) ≈ OarrA .+ 2 w .= 1 @test mul!(w, O, A', 1.0, 2.0) ≈ OarrAadj .+ 2 F = Fill(3, size(A)) w2 .= 1 @test mul!(w2, O, F, 1.0, 1.0) ≈ Oarr * Array(F) .+ 1 end end function test_OneElementMatrix_mul_vec(v, (w, w2), sz) @testset for ind in testinds(sz) O = OneElement(3, ind, sz) Oarr = Array(O) Oarrv = Oarr * v @test O * v == Oarrv @test mul!(w, O, v) == Oarrv # check rowwise to ensure zero rows @test all(((r1, r2),) -> r1 == r2, zip(eachrow(w), eachrow(Oarrv))) w .= 1 @test mul!(w, O, v, 1.0, 2.0) == Oarrv .+ 2 F = Fill(3, size(v)) w2 .= 1 @test mul!(w2, O, F, 1.0, 1.0) == Oarr * Array(F) .+ 1 end end @testset "Matrix * OneElementVector" begin w = zeros(size(A,1)) w2 = MVector{length(w)}(w) test_mat_mul_OneElement(A, (w, w2), size(w)) end @testset "Matrix * OneElementMatrix" begin C = zeros(size(A)) C2 = MMatrix{size(C)...}(C) test_mat_mul_OneElement(A, (C, C2), size(C)) end @testset "OneElementMatrix * Vector" begin w = zeros(size(v)) w2 = MVector{size(v)...}(v) test_OneElementMatrix_mul_vec(v, (w, w2), size(A)) end @testset "OneElementMatrix * Matrix" begin C = zeros(size(A)) C2 = MMatrix{size(C)...}(C) test_OneElementMatrix_mul_mat(A, (C, C2), size(A)) end @testset "OneElementMatrix * OneElement" begin @testset for ind in testinds(A) O = OneElement(3, ind, size(A)) v = OneElement(4, ind[2], size(A,1)) @test O * v isa OneElement @test O * v == Array(O) * Array(v) @test mul!(ones(size(O,1)), O, v) == O * v @test mul!(ones(size(O,1)), O, v, 2, 1) == 2 * O * v .+ 1 B = OneElement(4, ind, size(A)) @test O * B isa OneElement @test O * B == Array(O) * Array(B) @test mul!(ones(size(O,1), size(B,2)), O, B) == O * B @test mul!(ones(size(O,1), size(B,2)), O, B, 2, 1) == 2 * O * B .+ 1 end @test OneElement(3, (2,3), (5,4)) * OneElement(2, 2, 4) == Zeros(5) @test OneElement(3, (2,3), (5,4)) * OneElement(2, (2,1), (4,2)) == Zeros(5,2) end @testset "AbstractFillMatrix * OneElementVector" begin F = Fill(3, size(A)) sw = (size(A,1),) @testset for ind in testinds(sw) x = OneElement(3, ind, sw) @test F * x isa Fill @test F * x == Array(F) * Array(x) end @test Zeros{Int8}(2,2) * OneElement{Int16}(2,2) === Zeros{Int16}(2) end @testset "OneElementMatrix * AbstractFillVector" begin @testset for ind in testinds(A) O = OneElement(3, ind, size(A)) v = Fill(2, size(O,1)) @test O * v isa OneElement @test O * v == Array(O) * Array(v) end A = OneElement(2,(2,2),(5,4)) B = Zeros(4) @test A * B === Zeros(5) end @testset "Diagonal and OneElementMatrix" begin for ind in ((2,3), (2,2), (10,10)) O = OneElement(3, ind, (4,3)) Oarr = Array(O) C = zeros(size(O)) D = Diagonal(axes(O,1)) @test D * O == D * Oarr @test mul!(C, D, O) == D * O C .= 1 @test mul!(C, D, O, 2, 2) == 2 * D * O .+ 2 D = Diagonal(axes(O,2)) @test O * D == Oarr * D @test mul!(C, O, D) == O * D C .= 1 @test mul!(C, O, D, 2, 2) == 2 * O * D .+ 2 end end @testset "array elements" begin A = [SMatrix{2,3}(1:6)*(i+j) for i in 1:3, j in 1:2] @testset "StridedMatrix * OneElementMatrix" begin B = OneElement(SMatrix{3,2}(1:6), (size(A,2),2), (size(A,2),4)) C = [SMatrix{2,2}(1:4) for i in axes(A,1), j in axes(B,2)] @test mul!(copy(C), A, B) == A * B @test mul!(copy(C), A, B, 2, 2) == 2 * A * B + 2 * C end @testset "StridedMatrix * OneElementVector" begin B = OneElement(SMatrix{3,2}(1:6), (size(A,2),), (size(A,2),)) C = [SMatrix{2,2}(1:4) for i in axes(A,1)] @test mul!(copy(C), A, B) == A * B @test mul!(copy(C), A, B, 2, 2) == 2 * A * B + 2 * C end A = OneElement(SMatrix{3,2}(1:6), (3,2), (5,4)) @testset "OneElementMatrix * StridedMatrix" begin B = [SMatrix{2,3}(1:6)*(i+j) for i in axes(A,2), j in 1:2] C = [SMatrix{3,3}(1:9) for i in axes(A,1), j in axes(B,2)] @test mul!(copy(C), A, B) == A * B @test mul!(copy(C), A, B, 2, 2) == 2 * A * B + 2 * C end @testset "OneElementMatrix * StridedVector" begin B = [SMatrix{2,3}(1:6)*i for i in axes(A,2)] C = [SMatrix{3,3}(1:9) for i in axes(A,1)] @test mul!(copy(C), A, B) == A * B @test mul!(copy(C), A, B, 2, 2) == 2 * A * B + 2 * C end @testset "OneElementMatrix * OneElementMatrix" begin B = OneElement(SMatrix{2,3}(1:6), (2,4), (size(A,2), 3)) C = [SMatrix{3,3}(1:9) for i in axes(A,1), j in axes(B,2)] @test mul!(copy(C), A, B) == A * B @test mul!(copy(C), A, B, 2, 2) == 2 * A * B + 2 * C end @testset "OneElementMatrix * OneElementVector" begin B = OneElement(SMatrix{2,3}(1:6), 2, size(A,2)) C = [SMatrix{3,3}(1:9) for i in axes(A,1)] @test mul!(copy(C), A, B) == A * B @test mul!(copy(C), A, B, 2, 2) == 2 * A * B + 2 * C end end @testset "non-commutative" begin A = OneElement(quat(rand(4)...), (2,3), (3,4)) for (B,C) in ( # OneElementMatrix * OneElementVector (OneElement(quat(rand(4)...), 3, size(A,2)), [quat(rand(4)...) for i in axes(A,1)]), # OneElementMatrix * OneElementMatrix (OneElement(quat(rand(4)...), (3,2), (size(A,2), 4)), [quat(rand(4)...) for i in axes(A,1), j in 1:4]), ) @test mul!(copy(C), A, B) ≈ A * B α, β = quat(0,0,1,0), quat(1,0,1,0) @test mul!(copy(C), A, B, α, β) ≈ mul!(copy(C), A, Array(B), α, β) ≈ A * B * α + C * β end A = [quat(rand(4)...)*(i+j) for i in 1:2, j in 1:3] for (B,C) in ( # StridedMatrix * OneElementVector (OneElement(quat(rand(4)...), 1, size(A,2)), [quat(rand(4)...) for i in axes(A,1)]), # StridedMatrix * OneElementMatrix (OneElement(quat(rand(4)...), (2,2), (size(A,2), 4)), [quat(rand(4)...) for i in axes(A,1), j in 1:4]), ) @test mul!(copy(C), A, B) ≈ A * B α, β = quat(0,0,1,0), quat(1,0,1,0) @test mul!(copy(C), A, B, α, β) ≈ mul!(copy(C), A, Array(B), α, β) ≈ A * B * α + C * β end A = OneElement(quat(rand(4)...), (2,2), (3, 4)) for (B,C) in ( # OneElementMatrix * StridedMatrix ([quat(rand(4)...) for i in axes(A,2), j in 1:3], [quat(rand(4)...) for i in axes(A,1), j in 1:3]), # OneElementMatrix * StridedVector ([quat(rand(4)...) for i in axes(A,2)], [quat(rand(4)...) for i in axes(A,1)]), ) @test mul!(copy(C), A, B) ≈ A * B α, β = quat(0,0,1,0), quat(1,0,1,0) @test mul!(copy(C), A, B, α, β) ≈ mul!(copy(C), A, Array(B), α, β) ≈ A * B * α + C * β end end end @testset "multiplication/division by a number" begin val = 2 x = OneElement(val,1,5) y = sparse(x) @test 3x === OneElement(3val,1,5) == 3y @test 3.0 * x === OneElement(3.0*val,1,5) == 3.0 * y @test 3.0 \ x === OneElement(3.0 \ val,1,5) == 3.0 \ y @test x * 3.0 === OneElement(val * 3.0,1,5) == y * 3.0 @test x / 3.0 === OneElement(val / 3.0,1,5) == y / 3.0 val = 3im A = OneElement(val, (2,2), (3,4)) B = sparse(A) @test 3A === OneElement(3val, (2,2), (3,4)) == 3B @test 3.0im * A === OneElement(3.0im * val, (2,2), (3,4)) == 3.0im * B @test 3.0im \ A === OneElement(3.0im \ val, (2,2), (3,4)) == 3.0im \ B @test A * (2 + 3.0im) === OneElement(val * (2 + 3.0im), (2,2), (3,4)) == B * (2 + 3.0im) @test A / (2 + 3.0im) === OneElement(val / (2 + 3.0im), (2,2), (3,4)) == B / (2 + 3.0im) end @testset "isbanded" begin A = OneElement(3, (2,3), (4,5)) @test !isdiag(A) @test istriu(A) @test !istril(A) @test LinearAlgebra.isbanded(A, 1, 2) @test LinearAlgebra.isbanded(A, -1, 2) @test !LinearAlgebra.isbanded(A, 2, 2) A = OneElement(3, (4,2), (4,5)) @test !isdiag(A) @test !istriu(A) @test istril(A) @test LinearAlgebra.isbanded(A, -2, -2) @test LinearAlgebra.isbanded(A, -2, 2) @test !LinearAlgebra.isbanded(A, 2, 2) for A in (OneElement(3, (2,2), (4,5)), OneElement(3, (1,1), (1,2)), OneElement(3, (8,7), (2,1))) @test isdiag(A) @test istriu(A) @test istril(A) end for A in (OneElement(0, (2,3), (4,5)), OneElement(0, (3,2), (4,5))) @test isdiag(A) @test istriu(A) @test istril(A) end end @testset "zero/iszero" begin v = OneElement(10, 3, 4) @test v + zero(v) == v @test typeof(zero(v)) == typeof(v) @test !iszero(v) @test iszero(OneElement(0, 3, 4)) @test iszero(OneElement(0, 5, 4)) @test iszero(OneElement(3, (2,2), (0,0))) @test iszero(OneElement(3, (2,2), (1,2))) v = OneElement(SMatrix{2,2}(1:4), 3, 4) @test v + zero(v) == v @test typeof(zero(v)) == typeof(v) end @testset "isone" begin @test isone(OneElement(3, (0,0), (0,0))) @test isone(OneElement(1, (1,1), (1,1))) @test !isone(OneElement(2, (1,1), (1,1))) @test !isone(OneElement(1, (2,2), (2,2))) end @testset "tril/triu" begin for A in (OneElement(3, (4,2), (4,5)), OneElement(3, (2,3), (4,5)), OneElement(3, (3,3), (4,5))) B = Array(A) for k in -5:5 @test tril(A, k) == tril(B, k) @test triu(A, k) == triu(B, k) end end end @testset "broadcasting" begin for v in (OneElement(-2, 3, 4), OneElement(2im, (1,2), (3,4))) w = Array(v) n = 2 @test abs.(v) == abs.(w) @test abs2.(v) == abs2.(w) @test real.(v) == real.(w) @test imag.(v) == imag.(w) @test conj.(v) == conj.(w) @test v .^ n == w .^ n @test v .* n == w .* n @test v ./ n == w ./ n @test n .\ v == n .\ w end end @testset "permutedims" begin v = OneElement(1, (2, 3), (2, 5)) @test permutedims(v) === OneElement(1, (3, 2), (5, 2)) w = OneElement(1, 3, 5) @test permutedims(w) === OneElement(1, (1, 3), (1, 5)) x = OneElement(1, (1, 2, 3), (4, 5, 6)) @test permutedims(x, (1, 2, 3)) === x @test permutedims(x, (3, 2, 1)) === OneElement(1, (3, 2, 1), (6, 5, 4)) @test permutedims(x, [2, 3, 1]) === OneElement(1, (2, 3, 1), (5, 6, 4)) @test_throws BoundsError permutedims(x, (2, 1)) end @testset "show" begin B = OneElement(2, (1, 2), (3, 4)) @test repr(B) == "OneElement(2, (1, 2), (3, 4))" B = OneElement(2, 1, 3) @test repr(B) == "OneElement(2, 1, 3)" B = OneElement(2, (1, 2), (Base.IdentityUnitRange(1:1), Base.IdentityUnitRange(2:2))) @test repr(B) == "OneElement(2, (1, 2), (Base.IdentityUnitRange(1:1), Base.IdentityUnitRange(2:2)))" end @testset "issymmetric/ishermitian" begin for el in (2, 3+0im, 4+5im, SMatrix{2,2}(1:4), SMatrix{2,3}(1:6)), size in [(3,3), (3,4)] O = OneElement(el, (2,2), size) A = Array(O) @test issymmetric(O) == issymmetric(A) @test ishermitian(O) == ishermitian(A) O = OneElement(el, (1,2), size) A = Array(O) @test issymmetric(O) == issymmetric(A) @test ishermitian(O) == ishermitian(A) O = OneElement(el, (5,5), size) A = Array(O) @test issymmetric(O) == issymmetric(A) @test ishermitian(O) == ishermitian(A) end end @testset "unique" begin @testset for n in 1:3 O = OneElement(5, 2, n) @test unique(O) == unique(Array(O)) @test allunique(O) == allunique(Array(O)) O = OneElement(0, 2, n) @test unique(O) == unique(Array(O)) @test allunique(O) == allunique(Array(O)) @testset for m in 1:4 O2 = OneElement(2, (2,1), (m,n)) @test unique(O2) == unique(Array(O2)) @test allunique(O2) == allunique(Array(O2)) O2 = OneElement(0, (2,1), (m,n)) @test unique(O2) == unique(Array(O2)) @test allunique(O2) == allunique(Array(O2)) end end end @testset "sum" begin @testset "OneElement($v, $ind, $sz)" for (v, ind, sz) in ( (Int8(2), 3, 4), (3.0, 5, 4), (3.0, 0, 0), (SMatrix{2,2}(1:4), (4, 2), (12,6)), ) O = OneElement(v,ind,sz) A = Array(O) @test @inferred(sum(O)) === sum(A) @test @inferred(sum(O, init=zero(eltype(O)))) === sum(A, init=zero(eltype(O))) @test @inferred(sum(x->1, O, init=0)) === sum(Fill(1, axes(O)), init=0) end @testset for O in (OneElement(Int8(2), (1,2), (2,4)), OneElement(3, (1,2,3), (2,4,4)), OneElement(2.0, (3,2,5), (2,3,2)), OneElement(SMatrix{2,2}(1:4), (1,2), (2,4)), ) A = Array(O) init = sum((zero(FillArrays.getindex_value(O)),)) for i in 1:3 @test @inferred(sum(O, dims=i)) == sum(A, dims=i) @test @inferred(sum(O, dims=i, init=init)) == sum(A, dims=i, init=init) @test @inferred(sum(x->1, O, dims=i, init=0)) == sum(Fill(1, axes(O)), dims=i, init=0) end @test @inferred(sum(O, dims=1:1)) == sum(A, dims=1:1) @test @inferred(sum(O, dims=1:2)) == sum(A, dims=1:2) @test @inferred(sum(O, dims=1:3)) == sum(A, dims=1:3) @test @inferred(sum(O, dims=(1,))) == sum(A, dims=(1,)) @test @inferred(sum(O, dims=(1,2))) == sum(A, dims=(1,2)) @test @inferred(sum(O, dims=(1,3))) == sum(A, dims=(1,3)) @test @inferred(sum(O, dims=(2,3))) == sum(A, dims=(2,3)) @test @inferred(sum(O, dims=(1,2,3))) == sum(A, dims=(1,2,3)) @test @inferred(sum(O, dims=1:1, init=init)) == sum(A, dims=1:1, init=init) @test @inferred(sum(O, dims=1:2, init=init)) == sum(A, dims=1:2, init=init) @test @inferred(sum(O, dims=1:3, init=init)) == sum(A, dims=1:3, init=init) @test @inferred(sum(O, dims=(1,), init=init)) == sum(A, dims=(1,), init=init) @test @inferred(sum(O, dims=(1,2), init=init)) == sum(A, dims=(1,2), init=init) @test @inferred(sum(O, dims=(1,3), init=init)) == sum(A, dims=(1,3), init=init) @test @inferred(sum(O, dims=(2,3), init=init)) == sum(A, dims=(2,3), init=init) @test @inferred(sum(O, dims=(1,2,3), init=init)) == sum(A, dims=(1,2,3), init=init) @test @inferred(sum(x->1, O, dims=(1,2,3), init=0)) == sum(Fill(1, axes(O)), dims=(1,2,3), init=0) end end @testset "diag" begin @testset for sz in [(0,0), (0,1), (1,0), (1,1), (4,4), (4,6), (6,3)], ind in CartesianIndices(sz) O = OneElement(4, Tuple(ind), sz) @testset for k in -maximum(sz):maximum(sz) @test diag(O, k) == diag(Array(O), k) @test diag(O, k) isa OneElement{Int,1} end end end end @testset "repeat" begin @testset "0D" begin @test repeat(Zeros()) isa Zeros @test repeat(Zeros()) == repeat(zeros()) @test repeat(Ones()) isa Ones @test repeat(Ones()) == repeat(ones()) @test repeat(Fill(3)) isa Fill @test repeat(Fill(3)) == repeat(fill(3)) @test repeat(Zeros(), inner=(), outer=()) isa Zeros @test repeat(Zeros(), inner=(), outer=()) == repeat(zeros(), inner=(), outer=()) @test repeat(Ones(), inner=(), outer=()) isa Ones @test repeat(Ones(), inner=(), outer=()) == repeat(ones(), inner=(), outer=()) @test repeat(Fill(4), inner=(), outer=()) isa Fill @test repeat(Fill(4), inner=(), outer=()) == repeat(fill(4), inner=(), outer=()) @test repeat(Zeros{Bool}(), 2, 3) isa Zeros{Bool} @test repeat(Zeros{Bool}(), 2, 3) == repeat(zeros(Bool), 2, 3) @test repeat(Ones{Bool}(), 2, 3) isa Ones{Bool} @test repeat(Ones{Bool}(), 2, 3) == repeat(ones(Bool), 2, 3) @test repeat(Fill(false), 2, 3) isa Fill @test repeat(Fill(false), 2, 3) == repeat(fill(false), 2, 3) @test repeat(Zeros(), inner=(2,2), outer=5) isa Zeros @test repeat(Zeros(), inner=(2,2), outer=5) == repeat(zeros(), inner=(2,2), outer=5) @test repeat(Ones(), inner=(2,2), outer=5) isa Ones @test repeat(Ones(), inner=(2,2), outer=5) == repeat(ones(), inner=(2,2), outer=5) @test repeat(Fill(2), inner=(2,2), outer=5) isa Fill @test repeat(Fill(2), inner=(2,2), outer=5) == repeat(fill(2), inner=(2,2), outer=5) @test repeat(Zeros(), inner=(2,2), outer=(2,3)) isa Zeros @test repeat(Zeros(), inner=(2,2), outer=(2,3)) == repeat(zeros(), inner=(2,2), outer=(2,3)) @test repeat(Ones(), inner=(2,2), outer=(2,3)) isa Ones @test repeat(Ones(), inner=(2,2), outer=(2,3)) == repeat(ones(), inner=(2,2), outer=(2,3)) @test repeat(Fill("a"), inner=(2,2), outer=(2,3)) isa Fill @test repeat(Fill("a"), inner=(2,2), outer=(2,3)) == repeat(fill("a"), inner=(2,2), outer=(2,3)) end @testset "1D" begin @test repeat(Zeros(2), 2, 3) isa Zeros @test repeat(Zeros(2), 2, 3) == repeat(zeros(2), 2, 3) @test repeat(Ones(2), 2, 3) isa Ones @test repeat(Ones(2), 2, 3) == repeat(ones(2), 2, 3) @test repeat(Fill(2,3), 2, 3) isa Fill @test repeat(Fill(2,3), 2, 3) == repeat(fill(2,3), 2, 3) @test repeat(Zeros(2), inner=2, outer=4) isa Zeros @test repeat(Zeros(2), inner=2, outer=4) == repeat(zeros(2), inner=2, outer=4) @test repeat(Ones(2), inner=2, outer=4) isa Ones @test repeat(Ones(2), inner=2, outer=4) == repeat(ones(2), inner=2, outer=4) @test repeat(Fill(2,3), inner=2, outer=4) isa Fill @test repeat(Fill(2,3), inner=2, outer=4) == repeat(fill(2,3), inner=2, outer=4) @test repeat(Zeros(2), inner=2, outer=(2,3)) isa Zeros @test repeat(Zeros(2), inner=2, outer=(2,3)) == repeat(zeros(2), inner=2, outer=(2,3)) @test repeat(Ones(2), inner=2, outer=(2,3)) isa Ones @test repeat(Ones(2), inner=2, outer=(2,3)) == repeat(ones(2), inner=2, outer=(2,3)) @test repeat(Fill("b",3), inner=2, outer=(2,3)) isa Fill @test repeat(Fill("b",3), inner=2, outer=(2,3)) == repeat(fill("b",3), inner=2, outer=(2,3)) @test repeat(Zeros(Int, 2), inner=(2,), outer=(2,3)) isa Zeros @test repeat(Zeros(Int, 2), inner=(2,), outer=(2,3)) == repeat(zeros(Int, 2), inner=(2,), outer=(2,3)) @test repeat(Ones(Int, 2), inner=(2,), outer=(2,3)) isa Ones @test repeat(Ones(Int, 2), inner=(2,), outer=(2,3)) == repeat(ones(Int, 2), inner=(2,), outer=(2,3)) @test repeat(Fill(2,3), inner=(2,), outer=(2,3)) isa Fill @test repeat(Fill(2,3), inner=(2,), outer=(2,3)) == repeat(fill(2,3), inner=(2,), outer=(2,3)) @test repeat(Zeros(2), inner=(2,2,1,4), outer=(2,3)) isa Zeros @test repeat(Zeros(2), inner=(2,2,1,4), outer=(2,3)) == repeat(zeros(2), inner=(2,2,1,4), outer=(2,3)) @test repeat(Ones(2), inner=(2,2,1,4), outer=(2,3)) isa Ones @test repeat(Ones(2), inner=(2,2,1,4), outer=(2,3)) == repeat(ones(2), inner=(2,2,1,4), outer=(2,3)) @test repeat(Fill(2,3), inner=(2,2,1,4), outer=(2,3)) isa Fill @test repeat(Fill(2,3), inner=(2,2,1,4), outer=(2,3)) == repeat(fill(2,3), inner=(2,2,1,4), outer=(2,3)) @test_throws ArgumentError repeat(Fill(2,3), inner=()) @test_throws ArgumentError repeat(Fill(2,3), outer=()) end @testset "2D" begin @test repeat(Zeros(2,3), 2, 3) isa Zeros @test repeat(Zeros(2,3), 2, 3) == repeat(zeros(2,3), 2, 3) @test repeat(Ones(2,3), 2, 3) isa Ones @test repeat(Ones(2,3), 2, 3) == repeat(ones(2,3), 2, 3) @test repeat(Fill(2,3,4), 2, 3) isa Fill @test repeat(Fill(2,3,4), 2, 3) == repeat(fill(2,3,4), 2, 3) @test repeat(Zeros(2,3), inner=(1,2), outer=(4,2)) isa Zeros @test repeat(Zeros(2,3), inner=(1,2), outer=(4,2)) == repeat(zeros(2,3), inner=(1,2), outer=(4,2)) @test repeat(Ones(2,3), inner=(1,2), outer=(4,2)) isa Ones @test repeat(Ones(2,3), inner=(1,2), outer=(4,2)) == repeat(ones(2,3), inner=(1,2), outer=(4,2)) @test repeat(Fill(2,3,4), inner=(1,2), outer=(4,2)) isa Fill @test repeat(Fill(2,3,4), inner=(1,2), outer=(4,2)) == repeat(fill(2,3,4), inner=(1,2), outer=(4,2)) @test repeat(Zeros(2,3), inner=(2,2,1,4), outer=(2,1,3)) isa Zeros @test repeat(Zeros(2,3), inner=(2,2,1,4), outer=(2,1,3)) == repeat(zeros(2,3), inner=(2,2,1,4), outer=(2,1,3)) @test repeat(Ones(2,3), inner=(2,2,1,4), outer=(2,1,3)) isa Ones @test repeat(Ones(2,3), inner=(2,2,1,4), outer=(2,1,3)) == repeat(ones(2,3), inner=(2,2,1,4), outer=(2,1,3)) @test repeat(Fill(2,3,4), inner=(2,2,1,4), outer=(2,1,3)) isa Fill @test repeat(Fill(2,3,4), inner=(2,2,1,4), outer=(2,1,3)) == repeat(fill(2,3,4), inner=(2,2,1,4), outer=(2,1,3)) @test_throws ArgumentError repeat(Fill(2,3,4), inner=()) @test_throws ArgumentError repeat(Fill(2,3,4), outer=()) @test_throws ArgumentError repeat(Fill(2,3,4), inner=(1,)) @test_throws ArgumentError repeat(Fill(2,3,4), outer=(1,)) end end @testset "structured matrix" begin bands = ((Fill(2,3), Fill(6,2)), (Zeros(3), Zeros(2))) @testset for (dv, ev) in bands for D in (Diagonal(dv), Bidiagonal(dv, ev, :U), Tridiagonal(ev, dv, ev), SymTridiagonal(dv, ev)) M = Matrix(D) for k in -5:5 @test diag(D, k) isa FillArrays.AbstractFill{eltype(D),1} @test diag(D, k) == diag(M, k) end end end end @testset "ReverseDiff with Zeros" begin # MWE in https://github.com/JuliaArrays/FillArrays.jl/issues/252 @test ReverseDiff.gradient(x -> sum(abs2.((Zeros(5) .- zeros(5)) ./ x)), rand(5)) == zeros(5) @test ReverseDiff.gradient(x -> sum(abs2.((zeros(5) .- Zeros(5)) ./ x)), rand(5)) == zeros(5) # MWE in https://github.com/JuliaArrays/FillArrays.jl/pull/278 @test ReverseDiff.gradient(x -> sum(abs2.((Zeros{eltype(x)}(5) .- zeros(5)) ./ x)), rand(5)) == zeros(5) @test ReverseDiff.gradient(x -> sum(abs2.((zeros(5) .- Zeros{eltype(x)}(5)) ./ x)), rand(5)) == zeros(5) # Corresponding tests with + @test ReverseDiff.gradient(x -> sum(abs2.((Zeros(5) .+ zeros(5)) ./ x)), rand(5)) == zeros(5) @test ReverseDiff.gradient(x -> sum(abs2.((zeros(5) .+ Zeros(5)) ./ x)), rand(5)) == zeros(5) @test ReverseDiff.gradient(x -> sum(abs2.((Zeros{eltype(x)}(5) .+ zeros(5)) ./ x)), rand(5)) == zeros(5) @test ReverseDiff.gradient(x -> sum(abs2.((zeros(5) .+ Zeros{eltype(x)}(5)) ./ x)), rand(5)) == zeros(5) end @testset "FillArraysPDMatsExt" begin for diag in (Ones(5), Fill(4.1, 8)) a = @inferred(AbstractPDMat(Diagonal(diag))) @test a isa ScalMat @test a.dim == length(diag) @test a.value == first(diag) end a = ScalMat(4, 1.) for zero in (Zeros(4), Zeros(4,4)) @test a * zero === zero end @test Zeros(4,4) * a === Zeros(4,4) end @testset "isbanded/isdiag" begin @testset for A in Any[Zeros(2,3), Zeros(0,1), Zeros(1,1), Zeros(1,2), Ones(0,1), Ones(1,1), Ones(3,4), Ones(0,4), Ones(7,0), Ones(7,2), Ones(2,7), Fill(3, 0,1), Fill(3, 1,1), Fill(3, 2,4), Fill(0, 3, 4), Fill(2, 0, 4), Fill(2, 6, 0), Fill(0, 8, 8)] B = Array(A) @test isdiag(A) == isdiag(B) for k in -5:5 @test istriu(A, k) == istriu(B, k) @test istril(A, k) == istril(B, k) end end end @testset "triu/tril for Zeros" begin Z = Zeros(3, 4) @test triu(Z) === Z @test tril(Z) === Z @test triu(Z, 2) === Z @test tril(Z, 2) === Z end @testset "Diagonal conversion (#389)" begin @test convert(Diagonal{Int, Vector{Int}}, Zeros(5,5)) isa Diagonal{Int,Vector{Int}} @test convert(Diagonal{Int, Vector{Int}}, Zeros(5,5)) == zeros(5,5) @test Diagonal{Int}(Zeros(5,5)) ≡ Diagonal(Zeros{Int}(5)) @test Diagonal{Int}(Ones(5,5)) ≡ Diagonal(Ones{Int}(5)) end @testset "sqrt/cbrt" begin F = Fill(4, 4, 4) A = Array(F) @test sqrt(F) ≈ sqrt(A) rtol=3e-8 @test sqrt(F)^2 ≈ F F = Fill(4+4im, 4, 4) A = Array(F) @test sqrt(F) ≈ sqrt(A) rtol=1e-8 @test sqrt(F)^2 ≈ F F = Fill(-4, 4, 4) A = Array(F) if VERSION >= v"1.11.0-rc3" @test cbrt(F) ≈ cbrt(A) rtol=1e-5 end @test cbrt(F)^3 ≈ F # avoid overflow F = Fill(4, typemax(Int), typemax(Int)) @test sqrt(F)^2 ≈ F @test cbrt(F)^3 ≈ F # zeros F = Zeros(4, 4) A = Array(F) @test sqrt(F) ≈ sqrt(A) atol=1e-14 @test sqrt(F)^2 == F @test cbrt(F)^3 == F end