Showing preview only (252K chars total). Download the full file or copy to clipboard to get everything.
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
[](https://JuliaArrays.github.io/FillArrays.jl/stable)
[](https://JuliaArrays.github.io/FillArrays.jl/dev)
[](https://github.com/JuliaArrays/FillArrays.jl/actions)
[](https://codecov.io/gh/JuliaArrays/FillArrays.jl)
[](https://juliahub.com/ui/Packages/FillArrays/2Dg1l?t=2)
[](https://juliahub.com/ui/Packages/FillArrays/2Dg1l)
[](https://juliaci.github.io/NanosoldierReports/pkgeval_badges/report.html)
[](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 *
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
Condensed preview — 28 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (256K chars).
[
{
"path": ".github/dependabot.yml",
"chars": 255,
"preview": "# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates\nversion: 2\nupda"
},
{
"path": ".github/workflows/CompatHelper.yml",
"chars": 1381,
"preview": "name: CompatHelper\non:\n schedule:\n - cron: 0 0 * * *\n workflow_dispatch:\npermissions:\n contents: write\n pull-requ"
},
{
"path": ".github/workflows/Invalidations.yml",
"chars": 1566,
"preview": "name: Invalidations\n\non:\n pull_request:\n paths-ignore:\n - 'LICENSE'\n - 'README.md'\n - '.github/workfl"
},
{
"path": ".github/workflows/TagBot.yml",
"chars": 440,
"preview": "name: TagBot\non:\n issue_comment:\n types:\n - created\n workflow_dispatch:\n inputs:\n lookback:\n de"
},
{
"path": ".github/workflows/ci.yml",
"chars": 1589,
"preview": "name: CI\non:\n push:\n branches:\n - master\n tags: [v*]\n paths-ignore:\n - 'LICENSE'\n - 'README.md'"
},
{
"path": ".github/workflows/docs.yml",
"chars": 1405,
"preview": "name: Documentation\n\non:\n pull_request:\n paths-ignore:\n - 'LICENSE'\n - 'README.md'\n - '.github/workfl"
},
{
"path": ".github/workflows/downstream.yml",
"chars": 3206,
"preview": "name: IntegrationTest\non:\n push:\n branches: [master]\n tags: [v*]\n paths-ignore:\n - 'LICENSE'\n - 'REA"
},
{
"path": ".gitignore",
"chars": 96,
"preview": "*.jl.cov\n*.jl.*.cov\n*.jl.mem\ndeps/deps.jl\n.DS_Store\nManifest.toml\nManifest-v*.*.toml\ndocs/build\n"
},
{
"path": "LICENSE",
"chars": 1070,
"preview": "MIT License\n\nCopyright (c) 2017 Sheehan Olver\n\nPermission is hereby granted, free of charge, to any person obtaining a c"
},
{
"path": "Project.toml",
"chars": 1495,
"preview": "name = \"FillArrays\"\nuuid = \"1a297f60-69ca-5386-bcde-b61e274b549b\"\nversion = \"1.16.0\"\n\n[deps]\nLinearAlgebra = \"37e2e46d-f"
},
{
"path": "README.md",
"chars": 4682,
"preview": "# FillArrays.jl\n\n[](https://JuliaArrays.github.io/FillArrays"
},
{
"path": "appveyor.yml",
"chars": 1067,
"preview": "environment:\n matrix:\n - julia_version: 1\n - julia_version: 1.5\n - julia_version: nightly\n\nplatform:\n - x86 # 32-bi"
},
{
"path": "benchmark/benchmarks.jl",
"chars": 2413,
"preview": "using BenchmarkTools\nusing FillArrays\nusing LinearAlgebra: triu, tril\n\n# Subtract the overhead from benchmark times\nBenc"
},
{
"path": "docs/Project.toml",
"chars": 345,
"preview": "[deps]\nDocumenter = \"e30172f5-a6a5-5a46-863b-614d45cd2de4\"\nFillArrays = \"1a297f60-69ca-5386-bcde-b61e274b549b\"\nRandom = "
},
{
"path": "docs/make.jl",
"chars": 412,
"preview": "using Documenter\nusing FillArrays\n\n# Setup for doctests in docstrings\nDocMeta.setdocmeta!(FillArrays, :DocTestSetup, :(u"
},
{
"path": "docs/src/index.md",
"chars": 5492,
"preview": "```@meta\nDocTestSetup = quote\n using FillArrays\nend\n```\n\n# Introduction\n\n`FillArrays` allows one to lazily represent"
},
{
"path": "ext/FillArraysPDMatsExt.jl",
"chars": 648,
"preview": "module FillArraysPDMatsExt\n\nimport FillArrays\nimport FillArrays.LinearAlgebra\nimport PDMats\nusing FillArrays: mult_zeros"
},
{
"path": "ext/FillArraysSparseArraysExt.jl",
"chars": 3011,
"preview": "module FillArraysSparseArraysExt\n\nusing SparseArrays\nusing SparseArrays: SparseVectorUnion\nimport Base: convert, kron\nus"
},
{
"path": "ext/FillArraysStaticArraysExt.jl",
"chars": 736,
"preview": "module FillArraysStaticArraysExt\n\nusing FillArrays\nusing StaticArrays\n\nimport Base: promote_op\nimport FillArrays: elconv"
},
{
"path": "ext/FillArraysStatisticsExt.jl",
"chars": 1179,
"preview": "module FillArraysStatisticsExt\n\nimport Statistics: mean, var, cov, cor\nusing LinearAlgebra: diagind\n\nusing FillArrays\nus"
},
{
"path": "src/FillArrays.jl",
"chars": 33088,
"preview": "\"\"\" `FillArrays` module to lazily represent matrices with a single value \"\"\"\nmodule FillArrays\n\nusing LinearAlgebra\nimpo"
},
{
"path": "src/fillalgebra.jl",
"chars": 22919,
"preview": "## vec\n\nvec(a::AbstractFill) = fillsimilar(a, length(a))\n\n## Transpose/Adjoint\n# cannot do this for vectors since that w"
},
{
"path": "src/fillbroadcast.jl",
"chars": 14299,
"preview": "### map\n\nmap(f::Function, r::AbstractFill) = Fill(f(getindex_value(r)), axes(r))\n\nfunction map(f::Function, v::AbstractF"
},
{
"path": "src/oneelement.jl",
"chars": 16316,
"preview": "\"\"\"\n OneElement(val, ind, axesorsize) <: AbstractArray\n\nRepresents an array with the specified axes (if its a tuple o"
},
{
"path": "src/trues.jl",
"chars": 714,
"preview": "\n\"\"\"\n Trues = Ones{Bool, N, Axes} where {N, Axes}\n\nLazy version of `trues` with axes.\nTypically created using `Trues("
},
{
"path": "test/aqua.jl",
"chars": 336,
"preview": "using Aqua\nusing FillArrays\nusing Test\ndownstream_test = \"--downstream_integration_test\" in ARGS\n@testset \"Project quali"
},
{
"path": "test/infinitearrays.jl",
"chars": 1572,
"preview": "# Infinite Arrays implementation from\n# https://github.com/JuliaLang/julia/blob/master/test/testhelpers/InfiniteArrays.j"
},
{
"path": "test/runtests.jl",
"chars": 123946,
"preview": "using FillArrays, LinearAlgebra, PDMats, SparseArrays, StaticArrays, ReverseDiff, Random, Test, Statistics, Quaternions\n"
}
]
About this extraction
This page contains the full source code of the JuliaArrays/FillArrays.jl GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 28 files (239.9 KB), approximately 85.4k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.