diff --git a/.github/workflows/Tests.yml b/.github/workflows/CI.yml similarity index 52% rename from .github/workflows/Tests.yml rename to .github/workflows/CI.yml index 267e3d8d..c51c30b8 100644 --- a/.github/workflows/Tests.yml +++ b/.github/workflows/CI.yml @@ -1,4 +1,4 @@ -name: "Tests" +name: CI on: pull_request: @@ -23,23 +23,40 @@ jobs: fail-fast: false matrix: version: - - "1" - "lts" + - "1" - "pre" group: - "Core" - - "RaggedArrays" - - "ArrayPartitionAnyAll" - - "ShorthandConstructors" + - "QA" + - "SymbolicIndexingInterface" - "Downstream" - - "nopre" + - "NoPre" exclude: + # JET (NoPre) is not run on prereleases. - version: "pre" - group: "nopre" + group: "NoPre" + # QA (Aqua) runs only on lts and 1, never on prereleases. + - version: "pre" + group: "QA" uses: "SciML/.github/.github/workflows/tests.yml@v1" with: julia-version: "${{ matrix.version }}" group: "${{ matrix.group }}" num-threads: "2" + check-bounds: "auto" + coverage-directories: "src,ext" + secrets: "inherit" + + # GPU tests are a dep-adding group (test/GPU/Project.toml) that runs on the + # self-hosted GPU runner. Folded in from the former bespoke GPU.yml. + gpu: + name: "GPU Tests" + uses: "SciML/.github/.github/workflows/tests.yml@v1" + with: + julia-version: "1" + group: "GPU" + runner: '["self-hosted", "Linux", "X64", "gpu"]' + timeout-minutes: 60 coverage-directories: "src,ext" secrets: "inherit" diff --git a/.github/workflows/Downgrade.yml b/.github/workflows/Downgrade.yml index 32358924..3a3753d2 100644 --- a/.github/workflows/Downgrade.yml +++ b/.github/workflows/Downgrade.yml @@ -20,7 +20,5 @@ jobs: - Core uses: "SciML/.github/.github/workflows/downgrade.yml@v1" with: - julia-version: "1.10" group: "${{ matrix.group }}" - skip: "Pkg,TOML" secrets: "inherit" diff --git a/.github/workflows/DowngradeSublibraries.yml b/.github/workflows/DowngradeSublibraries.yml index 19bc09db..4ceffb08 100644 --- a/.github/workflows/DowngradeSublibraries.yml +++ b/.github/workflows/DowngradeSublibraries.yml @@ -19,4 +19,6 @@ jobs: secrets: "inherit" with: julia-version: "lts" - skip: "RecursiveArrayTools, Pkg, TOML, Statistics, LinearAlgebra, SparseArrays, InteractiveUtils, Random, Test" + group-env-name: "RECURSIVEARRAYTOOLS_TEST_GROUP" + group-env-value: "Core" + # Every lib/* sublibrary is downgrade-tested (projects auto-discovered, no exclusions). diff --git a/.github/workflows/GPU.yml b/.github/workflows/GPU.yml deleted file mode 100644 index bd8bd57e..00000000 --- a/.github/workflows/GPU.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: "GPU Tests" - -on: - pull_request: - branches: - - master - paths-ignore: - - 'docs/**' - push: - branches: - - master - paths-ignore: - - 'docs/**' - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ github.ref_name != github.event.repository.default_branch || github.ref != 'refs/tags/v*' }} - -jobs: - gpu-tests: - name: "GPU Tests" - runs-on: [self-hosted, Linux, X64, gpu] - timeout-minutes: 60 - steps: - - uses: actions/checkout@v6 - - uses: julia-actions/setup-julia@v3 - with: - version: "1" - - uses: julia-actions/julia-buildpkg@v1 - - uses: julia-actions/julia-runtest@v1 - env: - GROUP: "GPU" - - uses: julia-actions/julia-processcoverage@v1 - - uses: codecov/codecov-action@v7 - with: - files: lcov.info - token: ${{ secrets.CODECOV_TOKEN }} - fail_ci_if_error: false diff --git a/.github/workflows/SublibraryCI.yml b/.github/workflows/SublibraryCI.yml index 6cec66bb..0f75350f 100644 --- a/.github/workflows/SublibraryCI.yml +++ b/.github/workflows/SublibraryCI.yml @@ -19,4 +19,7 @@ concurrency: jobs: sublibraries: uses: "SciML/.github/.github/workflows/sublibrary-project-tests.yml@v1" + with: + group-env-name: RECURSIVEARRAYTOOLS_TEST_GROUP + check-bounds: auto secrets: "inherit" diff --git a/.github/workflows/TagBot.yml b/.github/workflows/TagBot.yml index f49313b6..c2c44047 100644 --- a/.github/workflows/TagBot.yml +++ b/.github/workflows/TagBot.yml @@ -1,15 +1,21 @@ -name: TagBot +name: "TagBot" on: issue_comment: - types: - - created + types: [created] workflow_dispatch: 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 }} + tagbot: + uses: "SciML/.github/.github/workflows/tagbot.yml@v1" + secrets: "inherit" + tagbot-subpackages: + strategy: + fail-fast: false + matrix: + subdir: + - "lib/RecursiveArrayToolsArrayPartitionAnyAll" + - "lib/RecursiveArrayToolsRaggedArrays" + - "lib/RecursiveArrayToolsShorthandConstructors" + uses: "SciML/.github/.github/workflows/tagbot.yml@v1" + with: + subdir: "${{ matrix.subdir }}" + secrets: "inherit" diff --git a/.gitignore b/.gitignore index 49a815a8..cb5b1974 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,13 @@ *.jl.cov *.jl.*.cov *.jl.mem +*.jl.*.mem Manifest.toml +*.DS_Store .vscode +profile.pb.gz +.*.swp +LocalPreferences.toml + +docs/build +.claude/ diff --git a/Project.toml b/Project.toml index d61458c8..b7e70c60 100644 --- a/Project.toml +++ b/Project.toml @@ -50,7 +50,6 @@ RecursiveArrayToolsZygoteExt = "Zygote" [compat] Adapt = "4" -Aqua = "0.8" ArrayInterface = "7.17.0" CUDA = "5, 6.0" DocStringExtensions = "0.9.3" @@ -62,12 +61,12 @@ LinearAlgebra = "1.10" Measurements = "2.11" MonteCarloMeasurements = "1.2" Mooncake = "0.5" -NLsolve = "4.5" Pkg = "1" Polyester = "0.7.16" PrecompileTools = "1.2.1" Random = "1" RecipesBase = "1.3.4" +RecursiveArrayToolsShorthandConstructors = "1" ReverseDiff = "1.15" SafeTestsets = "0.1" SparseArrays = "1.10" @@ -84,17 +83,16 @@ Zygote = "0.7" julia = "1.10" [extras] -Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" FastBroadcast = "7034ab61-46d4-4ed7-9d0f-46aef9175898" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca" -NLsolve = "2774e3e8-f4cf-5e23-947b-6d7e65073b56" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Polyester = "f517fe37-dbe3-4b94-8317-1923a5111588" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +RecursiveArrayToolsShorthandConstructors = "39fb7555-b4ad-4efd-8abe-30331df017d3" SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" @@ -106,4 +104,4 @@ Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [targets] -test = ["Aqua", "FastBroadcast", "ForwardDiff", "KernelAbstractions", "Measurements", "Mooncake", "NLsolve", "Pkg", "Polyester", "Random", "ReverseDiff", "SafeTestsets", "SparseArrays", "StaticArrays", "Statistics", "StructArrays", "Tables", "Test", "Unitful", "Zygote"] +test = ["FastBroadcast", "ForwardDiff", "KernelAbstractions", "Measurements", "Mooncake", "Pkg", "Polyester", "Random", "RecursiveArrayToolsShorthandConstructors", "ReverseDiff", "SafeTestsets", "SparseArrays", "StaticArrays", "Statistics", "StructArrays", "Tables", "Test", "Unitful", "Zygote"] diff --git a/lib/RecursiveArrayToolsArrayPartitionAnyAll/Project.toml b/lib/RecursiveArrayToolsArrayPartitionAnyAll/Project.toml index 0be72b75..abfb64fe 100644 --- a/lib/RecursiveArrayToolsArrayPartitionAnyAll/Project.toml +++ b/lib/RecursiveArrayToolsArrayPartitionAnyAll/Project.toml @@ -5,6 +5,9 @@ version = "1.0.0" [deps] RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" +[sources] +RecursiveArrayTools = {path = "../.."} + [compat] RecursiveArrayTools = "4" julia = "1.10" diff --git a/lib/RecursiveArrayToolsArrayPartitionAnyAll/README.md b/lib/RecursiveArrayToolsArrayPartitionAnyAll/README.md index 28abf03b..377baf2a 100644 --- a/lib/RecursiveArrayToolsArrayPartitionAnyAll/README.md +++ b/lib/RecursiveArrayToolsArrayPartitionAnyAll/README.md @@ -1,8 +1,13 @@ # RecursiveArrayToolsArrayPartitionAnyAll.jl -Optimized `any` and `all` for -[RecursiveArrayTools.jl](https://github.com/SciML/RecursiveArrayTools.jl)'s -`ArrayPartition` type. +[![Join the chat at https://julialang.zulipchat.com #sciml-bridged](https://img.shields.io/static/v1?label=Zulip&message=chat&color=9558b2&labelColor=389826)](https://julialang.zulipchat.com/#narrow/stream/279055-sciml-bridged) +[![Global Docs](https://img.shields.io/badge/docs-SciML-blue.svg)](https://docs.sciml.ai/RecursiveArrayTools/stable/) + +[![ColPrac: Contributor's Guide on Collaborative Practices for Community Packages](https://img.shields.io/badge/ColPrac-Contributor%27s%20Guide-blueviolet)](https://github.com/SciML/ColPrac) +[![SciML Code Style](https://img.shields.io/static/v1?label=code%20style&message=SciML&color=9558b2&labelColor=389826)](https://github.com/SciML/SciMLStyle) + +RecursiveArrayToolsArrayPartitionAnyAll.jl is a component of the [RecursiveArrayTools.jl](https://github.com/SciML/RecursiveArrayTools.jl) monorepo. Optimized `any` and `all` for the `ArrayPartition` type. +While completely independent and usable on its own, users wanting the full functionality should use [RecursiveArrayTools.jl](https://github.com/SciML/RecursiveArrayTools.jl). `ArrayPartition` stores data as a tuple of arrays. The default `AbstractArray` `any`/`all` iterates element-by-element through the partition, which incurs diff --git a/lib/RecursiveArrayToolsArrayPartitionAnyAll/test/runtests.jl b/lib/RecursiveArrayToolsArrayPartitionAnyAll/test/runtests.jl index c765e279..967f1126 100644 --- a/lib/RecursiveArrayToolsArrayPartitionAnyAll/test/runtests.jl +++ b/lib/RecursiveArrayToolsArrayPartitionAnyAll/test/runtests.jl @@ -1,34 +1,38 @@ using RecursiveArrayTools, RecursiveArrayToolsArrayPartitionAnyAll, Test -@testset "Optimized any" begin - ap = ArrayPartition(collect(1:5), collect(6:10), collect(11:15)) - @test any(x -> x == 4, ap) - @test any(x -> x == 15, ap) - @test !any(x -> x == 17, ap) - @test any(ap .> 10) - @test !any(ap .> 20) -end +const TEST_GROUP = get(ENV, "RECURSIVEARRAYTOOLS_TEST_GROUP", "Core") -@testset "Optimized all" begin - ap = ArrayPartition(ones(5), ones(5), ones(5)) - @test all(x -> x == 1.0, ap) - @test !all(x -> x == 2.0, ap) - @test all(ap .> 0) +if TEST_GROUP == "Core" || TEST_GROUP == "All" + @testset "Optimized any" begin + ap = ArrayPartition(collect(1:5), collect(6:10), collect(11:15)) + @test any(x -> x == 4, ap) + @test any(x -> x == 15, ap) + @test !any(x -> x == 17, ap) + @test any(ap .> 10) + @test !any(ap .> 20) + end - ap2 = ArrayPartition(ones(5), [1.0, 1.0, 0.0, 1.0, 1.0], ones(5)) - @test !all(x -> x == 1.0, ap2) -end + @testset "Optimized all" begin + ap = ArrayPartition(ones(5), ones(5), ones(5)) + @test all(x -> x == 1.0, ap) + @test !all(x -> x == 2.0, ap) + @test all(ap .> 0) + + ap2 = ArrayPartition(ones(5), [1.0, 1.0, 0.0, 1.0, 1.0], ones(5)) + @test !all(x -> x == 1.0, ap2) + end -@testset "Matches AbstractArray default results" begin - ap = ArrayPartition(rand(100), rand(100), rand(100)) - f = x -> x > 0.5 + @testset "Matches AbstractArray default results" begin + ap = ArrayPartition(rand(100), rand(100), rand(100)) + f = x -> x > 0.5 - # Results must match - @test any(f, ap) == any(f, collect(ap)) - @test all(f, ap) == all(f, collect(ap)) + # Results must match + @test any(f, ap) == any(f, collect(ap)) + @test all(f, ap) == all(f, collect(ap)) - # Edge case: empty - ap_empty = ArrayPartition(Float64[], Float64[]) - @test !any(x -> true, ap_empty) - @test all(x -> true, ap_empty) + # Edge case: empty + ap_empty = ArrayPartition(Float64[], Float64[]) + @test !any(x -> true, ap_empty) + @test all(x -> true, ap_empty) + end end diff --git a/lib/RecursiveArrayToolsArrayPartitionAnyAll/test/test_groups.toml b/lib/RecursiveArrayToolsArrayPartitionAnyAll/test/test_groups.toml new file mode 100644 index 00000000..dda1469c --- /dev/null +++ b/lib/RecursiveArrayToolsArrayPartitionAnyAll/test/test_groups.toml @@ -0,0 +1,2 @@ +[Core] +versions = ["lts", "1", "pre"] diff --git a/lib/RecursiveArrayToolsRaggedArrays/Project.toml b/lib/RecursiveArrayToolsRaggedArrays/Project.toml index c75a306b..93687be7 100644 --- a/lib/RecursiveArrayToolsRaggedArrays/Project.toml +++ b/lib/RecursiveArrayToolsRaggedArrays/Project.toml @@ -10,6 +10,9 @@ RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" StaticArraysCore = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5" +[sources] +RecursiveArrayTools = {path = "../.."} + [compat] Adapt = "4" ArrayInterface = "7.17.0" diff --git a/lib/RecursiveArrayToolsRaggedArrays/README.md b/lib/RecursiveArrayToolsRaggedArrays/README.md index 484adb99..41f92ee2 100644 --- a/lib/RecursiveArrayToolsRaggedArrays/README.md +++ b/lib/RecursiveArrayToolsRaggedArrays/README.md @@ -1,7 +1,13 @@ # RecursiveArrayToolsRaggedArrays.jl -True ragged (non-rectangular) array types for -[RecursiveArrayTools.jl](https://github.com/SciML/RecursiveArrayTools.jl). +[![Join the chat at https://julialang.zulipchat.com #sciml-bridged](https://img.shields.io/static/v1?label=Zulip&message=chat&color=9558b2&labelColor=389826)](https://julialang.zulipchat.com/#narrow/stream/279055-sciml-bridged) +[![Global Docs](https://img.shields.io/badge/docs-SciML-blue.svg)](https://docs.sciml.ai/RecursiveArrayTools/stable/) + +[![ColPrac: Contributor's Guide on Collaborative Practices for Community Packages](https://img.shields.io/badge/ColPrac-Contributor%27s%20Guide-blueviolet)](https://github.com/SciML/ColPrac) +[![SciML Code Style](https://img.shields.io/static/v1?label=code%20style&message=SciML&color=9558b2&labelColor=389826)](https://github.com/SciML/SciMLStyle) + +RecursiveArrayToolsRaggedArrays.jl is a component of the [RecursiveArrayTools.jl](https://github.com/SciML/RecursiveArrayTools.jl) monorepo. True ragged (non-rectangular) array types for `RaggedVectorOfArray` and `RaggedDiffEqArray`. +While completely independent and usable on its own, users wanting the full functionality should use [RecursiveArrayTools.jl](https://github.com/SciML/RecursiveArrayTools.jl). This subpackage provides `RaggedVectorOfArray` and `RaggedDiffEqArray`, which preserve the exact ragged structure of collections of differently-sized arrays diff --git a/lib/RecursiveArrayToolsRaggedArrays/test/runtests.jl b/lib/RecursiveArrayToolsRaggedArrays/test/runtests.jl index 923cc63f..17b27602 100644 --- a/lib/RecursiveArrayToolsRaggedArrays/test/runtests.jl +++ b/lib/RecursiveArrayToolsRaggedArrays/test/runtests.jl @@ -4,1027 +4,1031 @@ using SymbolicIndexingInterface using SymbolicIndexingInterface: SymbolCache using Test -@testset "RecursiveArrayToolsRaggedArrays" begin - # =================================================================== - # Tests ported from v3 basic_indexing.jl - # =================================================================== - - @testset "v3 basic indexing (ported)" begin - # Example Problem - recs = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] - testa = cat(recs..., dims = 2) - testva = RaggedVectorOfArray(recs) - @test maximum(testva) == maximum(maximum.(recs)) - - testva = RaggedVectorOfArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - @test maximum(testva) == maximum(maximum.(recs)) - - # broadcast with array - X = rand(3, 3) - mulX = sqrt.(abs.(testva .* X)) - ref = mapreduce((x, y) -> sqrt.(abs.(x .* y)), hcat, testva, eachcol(X)) - @test mulX == ref - fill!(mulX, 0) - mulX .= sqrt.(abs.(testva .* X)) - @test mulX == ref - - @test Array(testva) == [ - 1 4 7 - 2 5 8 - 3 6 9 - ] - - @test testa[1:2, 1:2] == [1 4; 2 5] - @test testva[1:2, 1:2] == [1 4; 2 5] - @test testa[1:2, 1:2] == [1 4; 2 5] - - t = [1, 2, 3] - diffeq = RaggedDiffEqArray(recs, t) - @test Array(diffeq) == [ - 1 4 7 - 2 5 8 - 3 6 9 - ] - @test diffeq[1:2, 1:2] == [1 4; 2 5] - end +const TEST_GROUP = get(ENV, "RECURSIVEARRAYTOOLS_TEST_GROUP", "Core") + +if TEST_GROUP == "Core" || TEST_GROUP == "All" + @testset "RecursiveArrayToolsRaggedArrays" begin + # =================================================================== + # Tests ported from v3 basic_indexing.jl + # =================================================================== + + @testset "v3 basic indexing (ported)" begin + # Example Problem + recs = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] + testa = cat(recs..., dims = 2) + testva = RaggedVectorOfArray(recs) + @test maximum(testva) == maximum(maximum.(recs)) + + testva = RaggedVectorOfArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + @test maximum(testva) == maximum(maximum.(recs)) + + # broadcast with array + X = rand(3, 3) + mulX = sqrt.(abs.(testva .* X)) + ref = mapreduce((x, y) -> sqrt.(abs.(x .* y)), hcat, testva, eachcol(X)) + @test mulX == ref + fill!(mulX, 0) + mulX .= sqrt.(abs.(testva .* X)) + @test mulX == ref + + @test Array(testva) == [ + 1 4 7 + 2 5 8 + 3 6 9 + ] - @testset "v3 ndims==2 indexing (ported)" begin - t = 1:10 - recs = [rand(8) for i in 1:10] - testa = cat(recs..., dims = 2) - testva = RaggedVectorOfArray(recs) - - # Array functions - @test size(testva) == (8, 10) - @test ndims(testva) == 2 - @test eltype(testva) == eltype(eltype(recs)) - testvasim = similar(testva) - @test size(testvasim) == size(testva) - @test eltype(testvasim) == eltype(testva) - testvasim = similar(testva, Float32) - @test size(testvasim) == size(testva) - @test eltype(testvasim) == Float32 - testvb = deepcopy(testva) - @test testva == testvb == recs - - # Math operations - @test testva + testvb == testva + recs == 2testva == 2 .* recs - @test testva - testvb == testva - recs == 0 .* recs - @test testva / 2 == recs ./ 2 - @test 2 .\ testva == 2 .\ recs - - # Column indexing - @test testva[:, begin] == first(testva) - @test testva[:, end] == last(testva) - @test testa[:, 1] == recs[1] - @test testva.u == recs - @test testva[:, 2:end] == RaggedVectorOfArray([recs[i] for i in 2:length(recs)]) - - diffeq = RaggedDiffEqArray(recs, t) - @test diffeq[:, 1] == testa[:, 1] - @test diffeq.u == recs - @test diffeq[:, end] == testa[:, end] - @test diffeq[:, 2:end] == RaggedDiffEqArray([recs[i] for i in 2:length(recs)], t[2:end]) - @test diffeq[:, 2:end].t == t[2:end] - @test diffeq[:, (end - 1):end] == RaggedDiffEqArray([recs[i] for i in (length(recs) - 1):length(recs)], t[(length(t) - 1):length(t)]) - @test diffeq[:, (end - 1):end].t == t[(length(t) - 1):length(t)] - @test diffeq[:, (end - 5):8] == RaggedDiffEqArray([recs[i] for i in (length(t) - 5):8], t[(length(t) - 5):8]) - @test diffeq[:, (end - 5):8].t == t[(length(t) - 5):8] - - # (Int, Int) - @test testa[5, 4] == testva[5, 4] - @test testa[5, 4] == diffeq[5, 4] - - # (Int, Range) or (Range, Int) - @test testa[1, 2:3] == testva[1, 2:3] - @test testa[5:end, 1] == testva[5:end, 1] - @test testa[:, 1] == testva[:, 1] - @test testa[3, :] == testva[3, :] - - @test testa[1, 2:3] == diffeq[1, 2:3] - @test testa[5:end, 1] == diffeq[5:end, 1] - @test testa[:, 1] == diffeq[:, 1] - @test testa[3, :] == diffeq[3, :] - - # (Range, Range) - @test testa[5:end, 1:2] == testva[5:end, 1:2] - @test testa[5:end, 1:2] == diffeq[5:end, 1:2] - end + @test testa[1:2, 1:2] == [1 4; 2 5] + @test testva[1:2, 1:2] == [1 4; 2 5] + @test testa[1:2, 1:2] == [1 4; 2 5] - @testset "v3 ndims==3 indexing (ported)" begin - t = 1:15 - recs = [rand(10, 8) for i in 1:15] - testa = cat(recs..., dims = 3) - testva = RaggedVectorOfArray(recs) - diffeq = RaggedDiffEqArray(recs, t) + t = [1, 2, 3] + diffeq = RaggedDiffEqArray(recs, t) + @test Array(diffeq) == [ + 1 4 7 + 2 5 8 + 3 6 9 + ] + @test diffeq[1:2, 1:2] == [1 4; 2 5] + end - # (Int, Int, Int) - @test testa[1, 7, 14] == testva[1, 7, 14] - @test testa[1, 7, 14] == diffeq[1, 7, 14] + @testset "v3 ndims==2 indexing (ported)" begin + t = 1:10 + recs = [rand(8) for i in 1:10] + testa = cat(recs..., dims = 2) + testva = RaggedVectorOfArray(recs) + + # Array functions + @test size(testva) == (8, 10) + @test ndims(testva) == 2 + @test eltype(testva) == eltype(eltype(recs)) + testvasim = similar(testva) + @test size(testvasim) == size(testva) + @test eltype(testvasim) == eltype(testva) + testvasim = similar(testva, Float32) + @test size(testvasim) == size(testva) + @test eltype(testvasim) == Float32 + testvb = deepcopy(testva) + @test testva == testvb == recs + + # Math operations + @test testva + testvb == testva + recs == 2testva == 2 .* recs + @test testva - testvb == testva - recs == 0 .* recs + @test testva / 2 == recs ./ 2 + @test 2 .\ testva == 2 .\ recs + + # Column indexing + @test testva[:, begin] == first(testva) + @test testva[:, end] == last(testva) + @test testa[:, 1] == recs[1] + @test testva.u == recs + @test testva[:, 2:end] == RaggedVectorOfArray([recs[i] for i in 2:length(recs)]) + + diffeq = RaggedDiffEqArray(recs, t) + @test diffeq[:, 1] == testa[:, 1] + @test diffeq.u == recs + @test diffeq[:, end] == testa[:, end] + @test diffeq[:, 2:end] == RaggedDiffEqArray([recs[i] for i in 2:length(recs)], t[2:end]) + @test diffeq[:, 2:end].t == t[2:end] + @test diffeq[:, (end - 1):end] == RaggedDiffEqArray([recs[i] for i in (length(recs) - 1):length(recs)], t[(length(t) - 1):length(t)]) + @test diffeq[:, (end - 1):end].t == t[(length(t) - 1):length(t)] + @test diffeq[:, (end - 5):8] == RaggedDiffEqArray([recs[i] for i in (length(t) - 5):8], t[(length(t) - 5):8]) + @test diffeq[:, (end - 5):8].t == t[(length(t) - 5):8] + + # (Int, Int) + @test testa[5, 4] == testva[5, 4] + @test testa[5, 4] == diffeq[5, 4] + + # (Int, Range) or (Range, Int) + @test testa[1, 2:3] == testva[1, 2:3] + @test testa[5:end, 1] == testva[5:end, 1] + @test testa[:, 1] == testva[:, 1] + @test testa[3, :] == testva[3, :] + + @test testa[1, 2:3] == diffeq[1, 2:3] + @test testa[5:end, 1] == diffeq[5:end, 1] + @test testa[:, 1] == diffeq[:, 1] + @test testa[3, :] == diffeq[3, :] + + # (Range, Range) + @test testa[5:end, 1:2] == testva[5:end, 1:2] + @test testa[5:end, 1:2] == diffeq[5:end, 1:2] + end - # (Int, Int, Range) - @test testa[2, 3, 1:2] == testva[2, 3, 1:2] - @test testa[2, 3, 1:2] == diffeq[2, 3, 1:2] + @testset "v3 ndims==3 indexing (ported)" begin + t = 1:15 + recs = [rand(10, 8) for i in 1:15] + testa = cat(recs..., dims = 3) + testva = RaggedVectorOfArray(recs) + diffeq = RaggedDiffEqArray(recs, t) - # (Int, Range, Int) - @test testa[2, 3:4, 1] == testva[2, 3:4, 1] - @test testa[2, 3:4, 1] == diffeq[2, 3:4, 1] + # (Int, Int, Int) + @test testa[1, 7, 14] == testva[1, 7, 14] + @test testa[1, 7, 14] == diffeq[1, 7, 14] - # (Int, Range, Range) - @test testa[2, 3:4, 1:2] == testva[2, 3:4, 1:2] - @test testa[2, 3:4, 1:2] == diffeq[2, 3:4, 1:2] + # (Int, Int, Range) + @test testa[2, 3, 1:2] == testva[2, 3, 1:2] + @test testa[2, 3, 1:2] == diffeq[2, 3, 1:2] - # (Range, Int, Range) - @test testa[2:3, 1, 1:2] == testva[2:3, 1, 1:2] - @test testa[2:3, 1, 1:2] == diffeq[2:3, 1, 1:2] + # (Int, Range, Int) + @test testa[2, 3:4, 1] == testva[2, 3:4, 1] + @test testa[2, 3:4, 1] == diffeq[2, 3:4, 1] - # (Range, Range, Int) - @test testa[1:2, 2:3, 1] == testva[1:2, 2:3, 1] - @test testa[1:2, 2:3, 1] == diffeq[1:2, 2:3, 1] + # (Int, Range, Range) + @test testa[2, 3:4, 1:2] == testva[2, 3:4, 1:2] + @test testa[2, 3:4, 1:2] == diffeq[2, 3:4, 1:2] - # (Range, Range, Range) - @test testa[2:3, 2:3, 1:2] == testva[2:3, 2:3, 1:2] - @test testa[2:3, 2:3, 1:2] == diffeq[2:3, 2:3, 1:2] + # (Range, Int, Range) + @test testa[2:3, 1, 1:2] == testva[2:3, 1, 1:2] + @test testa[2:3, 1, 1:2] == diffeq[2:3, 1, 1:2] - # Make sure that 1:1 like ranges are not collapsed - @test testa[1:1, 2:3, 1:2] == testva[1:1, 2:3, 1:2] - @test testa[1:1, 2:3, 1:2] == diffeq[1:1, 2:3, 1:2] - end + # (Range, Range, Int) + @test testa[1:2, 2:3, 1] == testva[1:2, 2:3, 1] + @test testa[1:2, 2:3, 1] == diffeq[1:2, 2:3, 1] - @testset "v3 ragged arrays (ported)" begin - t = 1:3 - recs = [[1, 2, 3], [3, 5, 6, 7], [8, 9, 10, 11]] - testva = RaggedVectorOfArray(recs) - diffeq = RaggedDiffEqArray(recs, t) - - @test testva[:, 1] == recs[1] - @test testva[1:2, 1:2] == [1 3; 2 5] - @test diffeq[:, 1] == recs[1] - @test diffeq[1:2, 1:2] == [1 3; 2 5] - @test diffeq[:, 1:2] == RaggedDiffEqArray([recs[i] for i in 1:2], t[1:2]) - @test diffeq[:, 1:2].t == t[1:2] - @test diffeq[:, 2:end] == RaggedDiffEqArray([recs[i] for i in 2:3], t[2:end]) - @test diffeq[:, 2:end].t == t[2:end] - @test diffeq[:, (end - 1):end] == RaggedDiffEqArray([recs[i] for i in (length(recs) - 1):length(recs)], t[(length(t) - 1):length(t)]) - @test diffeq[:, (end - 1):end].t == t[(length(t) - 1):length(t)] - end + # (Range, Range, Range) + @test testa[2:3, 2:3, 1:2] == testva[2:3, 2:3, 1:2] + @test testa[2:3, 2:3, 1:2] == diffeq[2:3, 2:3, 1:2] - @testset "v3 heterogeneous views (ported, issue #453)" begin - f = RaggedVectorOfArray([[1.0], [2.0, 3.0]]) - @test length(view(f, :, 1)) == 1 - @test length(view(f, :, 2)) == 2 - @test view(f, :, 1) == [1.0] - @test view(f, :, 2) == [2.0, 3.0] - @test collect(view(f, :, 1)) == f[:, 1] - @test collect(view(f, :, 2)) == f[:, 2] - - f2 = RaggedVectorOfArray([[1.0, 2.0], [3.0]]) - @test length(view(f2, :, 1)) == 2 - @test length(view(f2, :, 2)) == 1 - @test view(f2, :, 1) == [1.0, 2.0] - @test view(f2, :, 2) == [3.0] - @test collect(view(f2, :, 1)) == f2[:, 1] - @test collect(view(f2, :, 2)) == f2[:, 2] - end + # Make sure that 1:1 like ranges are not collapsed + @test testa[1:1, 2:3, 1:2] == testva[1:1, 2:3, 1:2] + @test testa[1:1, 2:3, 1:2] == diffeq[1:1, 2:3, 1:2] + end - @testset "v3 end indexing with ragged arrays (ported)" begin - ragged = RaggedVectorOfArray([[1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0, 9.0]]) - @test ragged[end, 1] == 2.0 - @test ragged[end, 2] == 5.0 - @test ragged[end, 3] == 9.0 - @test ragged[end - 1, 1] == 1.0 - @test ragged[end - 1, 2] == 4.0 - @test ragged[end - 1, 3] == 8.0 - @test ragged[1:end, 1] == [1.0, 2.0] - @test ragged[1:end, 2] == [3.0, 4.0, 5.0] - @test ragged[1:end, 3] == [6.0, 7.0, 8.0, 9.0] - @test ragged[:, end] == [6.0, 7.0, 8.0, 9.0] - @test ragged[:, 2:end] == RaggedVectorOfArray(ragged.u[2:end]) - @test ragged[:, (end - 1):end] == RaggedVectorOfArray(ragged.u[(end - 1):end]) - - ragged2 = RaggedVectorOfArray([[1.0, 2.0, 3.0, 4.0], [5.0, 6.0], [7.0, 8.0, 9.0]]) - @test ragged2[end, 1] == 4.0 - @test ragged2[end, 2] == 6.0 - @test ragged2[end, 3] == 9.0 - @test ragged2[end - 1, 1] == 3.0 - @test ragged2[end - 1, 2] == 5.0 - @test ragged2[end - 1, 3] == 8.0 - @test ragged2[end - 2, 1] == 2.0 - @test ragged2[1:end, 1] == [1.0, 2.0, 3.0, 4.0] - @test ragged2[1:end, 2] == [5.0, 6.0] - @test ragged2[1:end, 3] == [7.0, 8.0, 9.0] - @test ragged2[2:end, 1] == [2.0, 3.0, 4.0] - @test ragged2[2:end, 2] == [6.0] - @test ragged2[2:end, 3] == [8.0, 9.0] - @test ragged2[:, end] == [7.0, 8.0, 9.0] - @test ragged2[:, 2:end] == RaggedVectorOfArray(ragged2.u[2:end]) - @test ragged2[1:(end - 1), 1] == [1.0, 2.0, 3.0] - @test ragged2[1:(end - 1), 2] == [5.0] - @test ragged2[1:(end - 1), 3] == [7.0, 8.0] - @test ragged2[:, (end - 1):end] == RaggedVectorOfArray(ragged2.u[(end - 1):end]) - end + @testset "v3 ragged arrays (ported)" begin + t = 1:3 + recs = [[1, 2, 3], [3, 5, 6, 7], [8, 9, 10, 11]] + testva = RaggedVectorOfArray(recs) + diffeq = RaggedDiffEqArray(recs, t) + + @test testva[:, 1] == recs[1] + @test testva[1:2, 1:2] == [1 3; 2 5] + @test diffeq[:, 1] == recs[1] + @test diffeq[1:2, 1:2] == [1 3; 2 5] + @test diffeq[:, 1:2] == RaggedDiffEqArray([recs[i] for i in 1:2], t[1:2]) + @test diffeq[:, 1:2].t == t[1:2] + @test diffeq[:, 2:end] == RaggedDiffEqArray([recs[i] for i in 2:3], t[2:end]) + @test diffeq[:, 2:end].t == t[2:end] + @test diffeq[:, (end - 1):end] == RaggedDiffEqArray([recs[i] for i in (length(recs) - 1):length(recs)], t[(length(t) - 1):length(t)]) + @test diffeq[:, (end - 1):end].t == t[(length(t) - 1):length(t)] + end - @testset "v3 RaggedEnd/RaggedRange broadcast as scalars (ported)" begin - ragged = RaggedVectorOfArray([[1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0, 9.0]]) - ragged_idx = lastindex(ragged, 1) - @test ragged_idx isa RaggedEnd - @test Base.broadcastable(ragged_idx) isa Ref - # Broadcasting with RaggedEnd should not error - @test identity.(ragged_idx) === ragged_idx - - ragged_range_idx = 1:lastindex(ragged, 1) - @test ragged_range_idx isa RaggedRange - @test Base.broadcastable(ragged_range_idx) isa Ref - # Broadcasting with RaggedRange should not error - @test identity.(ragged_range_idx) === ragged_range_idx - end + @testset "v3 heterogeneous views (ported, issue #453)" begin + f = RaggedVectorOfArray([[1.0], [2.0, 3.0]]) + @test length(view(f, :, 1)) == 1 + @test length(view(f, :, 2)) == 2 + @test view(f, :, 1) == [1.0] + @test view(f, :, 2) == [2.0, 3.0] + @test collect(view(f, :, 1)) == f[:, 1] + @test collect(view(f, :, 2)) == f[:, 2] + + f2 = RaggedVectorOfArray([[1.0, 2.0], [3.0]]) + @test length(view(f2, :, 1)) == 2 + @test length(view(f2, :, 2)) == 1 + @test view(f2, :, 1) == [1.0, 2.0] + @test view(f2, :, 2) == [3.0] + @test collect(view(f2, :, 1)) == f2[:, 1] + @test collect(view(f2, :, 2)) == f2[:, 2] + end - @testset "v3 broadcast assignment heterogeneous (ported, issue #454)" begin - u = RaggedVectorOfArray([[1.0], [2.0, 3.0]]) - @test length(view(u, :, 1)) == 1 - @test length(view(u, :, 2)) == 2 - # broadcast assignment into selected column (last index Int) - u[:, 2] .= [10.0, 11.0] - @test u.u[2] == [10.0, 11.0] - end + @testset "v3 end indexing with ragged arrays (ported)" begin + ragged = RaggedVectorOfArray([[1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0, 9.0]]) + @test ragged[end, 1] == 2.0 + @test ragged[end, 2] == 5.0 + @test ragged[end, 3] == 9.0 + @test ragged[end - 1, 1] == 1.0 + @test ragged[end - 1, 2] == 4.0 + @test ragged[end - 1, 3] == 8.0 + @test ragged[1:end, 1] == [1.0, 2.0] + @test ragged[1:end, 2] == [3.0, 4.0, 5.0] + @test ragged[1:end, 3] == [6.0, 7.0, 8.0, 9.0] + @test ragged[:, end] == [6.0, 7.0, 8.0, 9.0] + @test ragged[:, 2:end] == RaggedVectorOfArray(ragged.u[2:end]) + @test ragged[:, (end - 1):end] == RaggedVectorOfArray(ragged.u[(end - 1):end]) + + ragged2 = RaggedVectorOfArray([[1.0, 2.0, 3.0, 4.0], [5.0, 6.0], [7.0, 8.0, 9.0]]) + @test ragged2[end, 1] == 4.0 + @test ragged2[end, 2] == 6.0 + @test ragged2[end, 3] == 9.0 + @test ragged2[end - 1, 1] == 3.0 + @test ragged2[end - 1, 2] == 5.0 + @test ragged2[end - 1, 3] == 8.0 + @test ragged2[end - 2, 1] == 2.0 + @test ragged2[1:end, 1] == [1.0, 2.0, 3.0, 4.0] + @test ragged2[1:end, 2] == [5.0, 6.0] + @test ragged2[1:end, 3] == [7.0, 8.0, 9.0] + @test ragged2[2:end, 1] == [2.0, 3.0, 4.0] + @test ragged2[2:end, 2] == [6.0] + @test ragged2[2:end, 3] == [8.0, 9.0] + @test ragged2[:, end] == [7.0, 8.0, 9.0] + @test ragged2[:, 2:end] == RaggedVectorOfArray(ragged2.u[2:end]) + @test ragged2[1:(end - 1), 1] == [1.0, 2.0, 3.0] + @test ragged2[1:(end - 1), 2] == [5.0] + @test ragged2[1:(end - 1), 3] == [7.0, 8.0] + @test ragged2[:, (end - 1):end] == RaggedVectorOfArray(ragged2.u[(end - 1):end]) + end - @testset "v3 DiffEqArray with 2D inner arrays (ported)" begin - t = 1:2 - recs_2d = [rand(2, 3), rand(2, 4)] - diffeq_2d = RaggedDiffEqArray(recs_2d, t) - @test diffeq_2d[:, 1] == recs_2d[1] - @test diffeq_2d[:, 2] == recs_2d[2] - @test diffeq_2d[:, 1:2] == RaggedDiffEqArray(recs_2d[1:2], t[1:2]) - @test diffeq_2d[:, 1:2].t == t[1:2] - @test diffeq_2d[:, 2:end] == RaggedDiffEqArray(recs_2d[2:end], t[2:end]) - @test diffeq_2d[:, 2:end].t == t[2:end] - @test diffeq_2d[:, (end - 1):end] == RaggedDiffEqArray(recs_2d[(end - 1):end], t[(end - 1):end]) - @test diffeq_2d[:, (end - 1):end].t == t[(end - 1):end] - end + @testset "v3 RaggedEnd/RaggedRange broadcast as scalars (ported)" begin + ragged = RaggedVectorOfArray([[1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0, 9.0]]) + ragged_idx = lastindex(ragged, 1) + @test ragged_idx isa RaggedEnd + @test Base.broadcastable(ragged_idx) isa Ref + # Broadcasting with RaggedEnd should not error + @test identity.(ragged_idx) === ragged_idx + + ragged_range_idx = 1:lastindex(ragged, 1) + @test ragged_range_idx isa RaggedRange + @test Base.broadcastable(ragged_range_idx) isa Ref + # Broadcasting with RaggedRange should not error + @test identity.(ragged_range_idx) === ragged_range_idx + end - @testset "v3 DiffEqArray with 3D inner arrays (ported)" begin - t = 1:2 - recs_3d = [rand(2, 3, 4), rand(2, 3, 5)] - diffeq_3d = RaggedDiffEqArray(recs_3d, t) - @test diffeq_3d[:, :, :, 1] == recs_3d[1] - @test diffeq_3d[:, :, :, 2] == recs_3d[2] - @test diffeq_3d[:, :, :, 1:2] == RaggedDiffEqArray(recs_3d[1:2], t[1:2]) - @test diffeq_3d[:, :, :, 1:2].t == t[1:2] - @test diffeq_3d[:, :, :, 2:end] == RaggedDiffEqArray(recs_3d[2:end], t[2:end]) - @test diffeq_3d[:, :, :, 2:end].t == t[2:end] - @test diffeq_3d[:, :, :, (end - 1):end] == RaggedDiffEqArray(recs_3d[(end - 1):end], t[(end - 1):end]) - @test diffeq_3d[:, :, :, (end - 1):end].t == t[(end - 1):end] - end + @testset "v3 broadcast assignment heterogeneous (ported, issue #454)" begin + u = RaggedVectorOfArray([[1.0], [2.0, 3.0]]) + @test length(view(u, :, 1)) == 1 + @test length(view(u, :, 2)) == 2 + # broadcast assignment into selected column (last index Int) + u[:, 2] .= [10.0, 11.0] + @test u.u[2] == [10.0, 11.0] + end - @testset "v3 2D inner arrays ragged second dimension (ported)" begin - u = RaggedVectorOfArray([zeros(1, n) for n in (2, 3)]) - @test length(view(u, 1, :, 1)) == 2 - @test length(view(u, 1, :, 2)) == 3 - u[1, :, 2] .= [1.0, 2.0, 3.0] - @test u.u[2] == [1.0 2.0 3.0] - # partial column selection by indices - u[1, [1, 3], 2] .= [7.0, 9.0] - @test u.u[2] == [7.0 2.0 9.0] - # test scalar indexing with end - @test u[1, 1, end] == u.u[end][1, 1] - @test u[1, end, end] == u.u[end][1, end] - @test u[1, 2:end, end] == vec(u.u[end][1, 2:end]) - end + @testset "v3 DiffEqArray with 2D inner arrays (ported)" begin + t = 1:2 + recs_2d = [rand(2, 3), rand(2, 4)] + diffeq_2d = RaggedDiffEqArray(recs_2d, t) + @test diffeq_2d[:, 1] == recs_2d[1] + @test diffeq_2d[:, 2] == recs_2d[2] + @test diffeq_2d[:, 1:2] == RaggedDiffEqArray(recs_2d[1:2], t[1:2]) + @test diffeq_2d[:, 1:2].t == t[1:2] + @test diffeq_2d[:, 2:end] == RaggedDiffEqArray(recs_2d[2:end], t[2:end]) + @test diffeq_2d[:, 2:end].t == t[2:end] + @test diffeq_2d[:, (end - 1):end] == RaggedDiffEqArray(recs_2d[(end - 1):end], t[(end - 1):end]) + @test diffeq_2d[:, (end - 1):end].t == t[(end - 1):end] + end - @testset "v3 3D inner arrays ragged third dimension (ported)" begin - u = RaggedVectorOfArray([zeros(2, 1, n) for n in (2, 3)]) - @test size(view(u, :, :, :, 1)) == (2, 1, 2) - @test size(view(u, :, :, :, 2)) == (2, 1, 3) - # assign into a slice of the second inner array using last index Int - u[2, 1, :, 2] .= [7.0, 8.0, 9.0] - @test vec(u.u[2][2, 1, :]) == [7.0, 8.0, 9.0] - # check mixed slicing with range on front dims - u[1:2, 1, [1, 3], 2] .= [1.0 3.0; 2.0 4.0] - @test u.u[2][1, 1, 1] == 1.0 - @test u.u[2][2, 1, 1] == 2.0 - @test u.u[2][1, 1, 3] == 3.0 - @test u.u[2][2, 1, 3] == 4.0 - @test u[:, :, end] == u.u[end] - @test u[:, :, 2:end] == RaggedVectorOfArray(u.u[2:end]) - @test u[1, 1, end] == u.u[end][1, 1, end] - @test u[end, 1, end] == u.u[end][end, 1, end] - end + @testset "v3 DiffEqArray with 3D inner arrays (ported)" begin + t = 1:2 + recs_3d = [rand(2, 3, 4), rand(2, 3, 5)] + diffeq_3d = RaggedDiffEqArray(recs_3d, t) + @test diffeq_3d[:, :, :, 1] == recs_3d[1] + @test diffeq_3d[:, :, :, 2] == recs_3d[2] + @test diffeq_3d[:, :, :, 1:2] == RaggedDiffEqArray(recs_3d[1:2], t[1:2]) + @test diffeq_3d[:, :, :, 1:2].t == t[1:2] + @test diffeq_3d[:, :, :, 2:end] == RaggedDiffEqArray(recs_3d[2:end], t[2:end]) + @test diffeq_3d[:, :, :, 2:end].t == t[2:end] + @test diffeq_3d[:, :, :, (end - 1):end] == RaggedDiffEqArray(recs_3d[(end - 1):end], t[(end - 1):end]) + @test diffeq_3d[:, :, :, (end - 1):end].t == t[(end - 1):end] + end - @testset "v3 views can be modified (ported)" begin - f3 = RaggedVectorOfArray([[1.0, 2.0], [3.0, 4.0, 5.0]]) - v = view(f3, :, 2) - @test length(v) == 3 - v[1] = 10.0 - @test f3[1, 2] == 10.0 - @test f3.u[2][1] == 10.0 - end + @testset "v3 2D inner arrays ragged second dimension (ported)" begin + u = RaggedVectorOfArray([zeros(1, n) for n in (2, 3)]) + @test length(view(u, 1, :, 1)) == 2 + @test length(view(u, 1, :, 2)) == 3 + u[1, :, 2] .= [1.0, 2.0, 3.0] + @test u.u[2] == [1.0 2.0 3.0] + # partial column selection by indices + u[1, [1, 3], 2] .= [7.0, 9.0] + @test u.u[2] == [7.0 2.0 9.0] + # test scalar indexing with end + @test u[1, 1, end] == u.u[end][1, 1] + @test u[1, end, end] == u.u[end][1, end] + @test u[1, 2:end, end] == vec(u.u[end][1, 2:end]) + end - @testset "v3 2D inner array conversions (ported)" begin - t = 1:5 - recs = [rand(2, 2) for i in 1:5] - testva = RaggedVectorOfArray(recs) - diffeq = RaggedDiffEqArray(recs, t) + @testset "v3 3D inner arrays ragged third dimension (ported)" begin + u = RaggedVectorOfArray([zeros(2, 1, n) for n in (2, 3)]) + @test size(view(u, :, :, :, 1)) == (2, 1, 2) + @test size(view(u, :, :, :, 2)) == (2, 1, 3) + # assign into a slice of the second inner array using last index Int + u[2, 1, :, 2] .= [7.0, 8.0, 9.0] + @test vec(u.u[2][2, 1, :]) == [7.0, 8.0, 9.0] + # check mixed slicing with range on front dims + u[1:2, 1, [1, 3], 2] .= [1.0 3.0; 2.0 4.0] + @test u.u[2][1, 1, 1] == 1.0 + @test u.u[2][2, 1, 1] == 2.0 + @test u.u[2][1, 1, 3] == 3.0 + @test u.u[2][2, 1, 3] == 4.0 + @test u[:, :, end] == u.u[end] + @test u[:, :, 2:end] == RaggedVectorOfArray(u.u[2:end]) + @test u[1, 1, end] == u.u[end][1, 1, end] + @test u[end, 1, end] == u.u[end][end, 1, end] + end - @test Array(testva) isa Array{Float64, 3} - @test Array(diffeq) isa Array{Float64, 3} - end + @testset "v3 views can be modified (ported)" begin + f3 = RaggedVectorOfArray([[1.0, 2.0], [3.0, 4.0, 5.0]]) + v = view(f3, :, 2) + @test length(v) == 3 + v[1] = 10.0 + @test f3[1, 2] == 10.0 + @test f3.u[2][1] == 10.0 + end - @testset "v3 CartesianIndex (ported)" begin - v = RaggedVectorOfArray([zeros(20), zeros(10, 10), zeros(3, 3, 3)]) - v[CartesianIndex((2, 3, 2, 3))] = 1 - @test v[CartesianIndex((2, 3, 2, 3))] == 1 - @test v.u[3][2, 3, 2] == 1 + @testset "v3 2D inner array conversions (ported)" begin + t = 1:5 + recs = [rand(2, 2) for i in 1:5] + testva = RaggedVectorOfArray(recs) + diffeq = RaggedDiffEqArray(recs, t) - v = RaggedDiffEqArray([zeros(20), zeros(10, 10), zeros(3, 3, 3)], 1:3) - v[CartesianIndex((2, 3, 2, 3))] = 1 - @test v[CartesianIndex((2, 3, 2, 3))] == 1 - @test v.u[3][2, 3, 2] == 1 - end + @test Array(testva) isa Array{Float64, 3} + @test Array(diffeq) isa Array{Float64, 3} + end - @testset "v3 heterogeneous broadcasting (ported)" begin - v = RaggedVectorOfArray([rand(20), rand(10, 10), rand(3, 3, 3)]) - w = v .* v - @test w isa RaggedVectorOfArray - @test w[:, 1] isa Vector - @test w[:, 1] == v[:, 1] .* v[:, 1] - @test w[:, 2] == v[:, 2] .* v[:, 2] - @test w[:, 3] == v[:, 3] .* v[:, 3] - x = copy(v) - x .= v .* v - @test x.u == w.u - w = v .+ 1 - @test w isa RaggedVectorOfArray - @test w.u == map(x -> x .+ 1, v.u) - - v = RaggedDiffEqArray([rand(20), rand(10, 10), rand(3, 3, 3)], 1:3) - w = v .* v - @test_broken w isa RaggedDiffEqArray # FIXME - @test w[:, 1] isa Vector - @test w[:, 1] == v[:, 1] .* v[:, 1] - @test w[:, 2] == v[:, 2] .* v[:, 2] - @test w[:, 3] == v[:, 3] .* v[:, 3] - x = copy(v) - x .= v .* v - @test x.u == w.u - w = v .+ 1 - @test_broken w isa RaggedDiffEqArray # FIXME - @test w.u == map(x -> x .+ 1, v.u) - end + @testset "v3 CartesianIndex (ported)" begin + v = RaggedVectorOfArray([zeros(20), zeros(10, 10), zeros(3, 3, 3)]) + v[CartesianIndex((2, 3, 2, 3))] = 1 + @test v[CartesianIndex((2, 3, 2, 3))] == 1 + @test v.u[3][2, 3, 2] == 1 - @testset "v3 setindex! (ported)" begin - testva = RaggedVectorOfArray([i * ones(3, 3) for i in 1:5]) - testva[:, 2] = 7ones(3, 3) - @test testva[:, 2] == 7ones(3, 3) - testva[:, :] = [2i * ones(3, 3) for i in 1:5] - for i in 1:5 - @test testva[:, i] == 2i * ones(3, 3) - end - testva[:, 1:2:5] = [5i * ones(3, 3) for i in 1:2:5] - for i in 1:2:5 - @test testva[:, i] == 5i * ones(3, 3) + v = RaggedDiffEqArray([zeros(20), zeros(10, 10), zeros(3, 3, 3)], 1:3) + v[CartesianIndex((2, 3, 2, 3))] = 1 + @test v[CartesianIndex((2, 3, 2, 3))] == 1 + @test v.u[3][2, 3, 2] == 1 end - testva[CartesianIndex(3, 3, 5)] = 64.0 - @test testva[:, 5][3, 3] == 64.0 - @test_throws ArgumentError testva[2, 1:2, :] = 108.0 - testva[2, 1:2, :] .= 108.0 - for i in 1:5 - @test all(testva[:, i][2, 1:2] .== 108.0) + + @testset "v3 heterogeneous broadcasting (ported)" begin + v = RaggedVectorOfArray([rand(20), rand(10, 10), rand(3, 3, 3)]) + w = v .* v + @test w isa RaggedVectorOfArray + @test w[:, 1] isa Vector + @test w[:, 1] == v[:, 1] .* v[:, 1] + @test w[:, 2] == v[:, 2] .* v[:, 2] + @test w[:, 3] == v[:, 3] .* v[:, 3] + x = copy(v) + x .= v .* v + @test x.u == w.u + w = v .+ 1 + @test w isa RaggedVectorOfArray + @test w.u == map(x -> x .+ 1, v.u) + + v = RaggedDiffEqArray([rand(20), rand(10, 10), rand(3, 3, 3)], 1:3) + w = v .* v + @test_broken w isa RaggedDiffEqArray # FIXME + @test w[:, 1] isa Vector + @test w[:, 1] == v[:, 1] .* v[:, 1] + @test w[:, 2] == v[:, 2] .* v[:, 2] + @test w[:, 3] == v[:, 3] .* v[:, 3] + x = copy(v) + x .= v .* v + @test x.u == w.u + w = v .+ 1 + @test_broken w isa RaggedDiffEqArray # FIXME + @test w.u == map(x -> x .+ 1, v.u) end - testva[:, 3, :] = [3i / 7j for i in 1:3, j in 1:5] - for j in 1:5 - for i in 1:3 - @test testva[i, 3, j] == 3i / 7j + + @testset "v3 setindex! (ported)" begin + testva = RaggedVectorOfArray([i * ones(3, 3) for i in 1:5]) + testva[:, 2] = 7ones(3, 3) + @test testva[:, 2] == 7ones(3, 3) + testva[:, :] = [2i * ones(3, 3) for i in 1:5] + for i in 1:5 + @test testva[:, i] == 2i * ones(3, 3) + end + testva[:, 1:2:5] = [5i * ones(3, 3) for i in 1:2:5] + for i in 1:2:5 + @test testva[:, i] == 5i * ones(3, 3) + end + testva[CartesianIndex(3, 3, 5)] = 64.0 + @test testva[:, 5][3, 3] == 64.0 + @test_throws ArgumentError testva[2, 1:2, :] = 108.0 + testva[2, 1:2, :] .= 108.0 + for i in 1:5 + @test all(testva[:, i][2, 1:2] .== 108.0) + end + testva[:, 3, :] = [3i / 7j for i in 1:3, j in 1:5] + for j in 1:5 + for i in 1:3 + @test testva[i, 3, j] == 3i / 7j + end end end - end - @testset "v3 edge cases DiffEqArray scalar broadcast (ported)" begin - x = [1, 2, 3, 4, 5, 6, 7, 8, 9] - testva = RaggedDiffEqArray(x, x) - testvb = RaggedDiffEqArray(x, x) - mulX = sqrt.(abs.(testva .* testvb)) - ref = sqrt.(abs.(x .* x)) - @test mulX == ref - fill!(mulX, 0) - mulX .= sqrt.(abs.(testva .* testvb)) - @test mulX == ref - end + @testset "v3 edge cases DiffEqArray scalar broadcast (ported)" begin + x = [1, 2, 3, 4, 5, 6, 7, 8, 9] + testva = RaggedDiffEqArray(x, x) + testvb = RaggedDiffEqArray(x, x) + mulX = sqrt.(abs.(testva .* testvb)) + ref = sqrt.(abs.(x .* x)) + @test mulX == ref + fill!(mulX, 0) + mulX .= sqrt.(abs.(testva .* testvb)) + @test mulX == ref + end - @testset "v3 multidimensional parent VectorOfArray (ported)" begin - u_matrix = RaggedVectorOfArray([[1, 2] for i in 1:2, j in 1:3]) - u_vector = RaggedVectorOfArray([[1, 2] for i in 1:6]) + @testset "v3 multidimensional parent VectorOfArray (ported)" begin + u_matrix = RaggedVectorOfArray([[1, 2] for i in 1:2, j in 1:3]) + u_vector = RaggedVectorOfArray([[1, 2] for i in 1:6]) - # test broadcasting - function foo!(u) - @. u += 2 * u * abs(u) - return u + # test broadcasting + function foo!(u) + @. u += 2 * u * abs(u) + return u + end + foo!(u_matrix) + foo!(u_vector) + @test all(u_matrix .== [3, 10]) + @test all(vec(u_matrix) .≈ vec(u_vector)) + + # test that, for VectorOfArray with multi-dimensional parent arrays, + # broadcast and `similar` preserve the structure of the parent array + @test typeof(parent(similar(u_matrix))) == typeof(parent(u_matrix)) + @test typeof(parent((x -> x).(u_matrix))) == typeof(parent(u_matrix)) + + # test efficiency (allow 1 alloc on Julia 1.10 LTS) + num_allocs = @allocations foo!(u_matrix) + @test num_allocs <= 1 end - foo!(u_matrix) - foo!(u_vector) - @test all(u_matrix .== [3, 10]) - @test all(vec(u_matrix) .≈ vec(u_vector)) - - # test that, for VectorOfArray with multi-dimensional parent arrays, - # broadcast and `similar` preserve the structure of the parent array - @test typeof(parent(similar(u_matrix))) == typeof(parent(u_matrix)) - @test typeof(parent((x -> x).(u_matrix))) == typeof(parent(u_matrix)) - - # test efficiency (allow 1 alloc on Julia 1.10 LTS) - num_allocs = @allocations foo!(u_matrix) - @test num_allocs <= 1 - end - @testset "v3 issue 354 (ported)" begin - @test RaggedVectorOfArray(ones(1))[:] == ones(1) - end + @testset "v3 issue 354 (ported)" begin + @test RaggedVectorOfArray(ones(1))[:] == ones(1) + end - # =================================================================== - # Tests ported from v3 interface_tests.jl - # =================================================================== + # =================================================================== + # Tests ported from v3 interface_tests.jl + # =================================================================== - @testset "v3 iteration (ported)" begin - t = 1:3 - testva = RaggedVectorOfArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - testda = RaggedDiffEqArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]], t) + @testset "v3 iteration (ported)" begin + t = 1:3 + testva = RaggedVectorOfArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + testda = RaggedDiffEqArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]], t) - for (i, elem) in enumerate(testva) - @test elem == testva[:, i] + for (i, elem) in enumerate(testva) + @test elem == testva[:, i] + end + + for (i, elem) in enumerate(testda) + @test elem == testda[:, i] + end end - for (i, elem) in enumerate(testda) - @test elem == testda[:, i] + @testset "v3 push! and copy (ported)" begin + testva = RaggedVectorOfArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + testda = RaggedDiffEqArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]], 1:3) + + push!(testva, [10, 11, 12]) + @test testva[:, end] == [10, 11, 12] + push!(testda, [10, 11, 12]) + @test testda[:, end] == [10, 11, 12] + + testva2 = copy(testva) + push!(testva2, [13, 14, 15]) + testda2 = copy(testva) + push!(testda2, [13, 14, 15]) + + # make sure we copy when we pass containers + @test size(testva) == (3, 4) + @test testva2[:, end] == [13, 14, 15] + @test size(testda) == (3, 4) + @test testda2[:, end] == [13, 14, 15] end - end - @testset "v3 push! and copy (ported)" begin - testva = RaggedVectorOfArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - testda = RaggedDiffEqArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]], 1:3) - - push!(testva, [10, 11, 12]) - @test testva[:, end] == [10, 11, 12] - push!(testda, [10, 11, 12]) - @test testda[:, end] == [10, 11, 12] - - testva2 = copy(testva) - push!(testva2, [13, 14, 15]) - testda2 = copy(testva) - push!(testda2, [13, 14, 15]) - - # make sure we copy when we pass containers - @test size(testva) == (3, 4) - @test testva2[:, end] == [13, 14, 15] - @test size(testda) == (3, 4) - @test testda2[:, end] == [13, 14, 15] - end + @testset "v3 append! (ported)" begin + testva = RaggedVectorOfArray([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]) + testda = RaggedDiffEqArray([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]], 1:4) - @testset "v3 append! (ported)" begin - testva = RaggedVectorOfArray([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]) - testda = RaggedDiffEqArray([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]], 1:4) + append!(testva, testva) + @test testva[1:2, 5:6] == [1 4; 2 5] + append!(testda, testda) + @test testda[1:2, 5:6] == [1 4; 2 5] + end - append!(testva, testva) - @test testva[1:2, 5:6] == [1 4; 2 5] - append!(testda, testda) - @test testda[1:2, 5:6] == [1 4; 2 5] - end + @testset "v3 push! making array ragged (ported)" begin + testva = RaggedVectorOfArray([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]) + testda = RaggedDiffEqArray([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]], 1:4) - @testset "v3 push! making array ragged (ported)" begin - testva = RaggedVectorOfArray([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]) - testda = RaggedDiffEqArray([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]], 1:4) + # After appending, add ragged element + append!(testva, testva) + append!(testda, testda) - # After appending, add ragged element - append!(testva, testva) - append!(testda, testda) + push!(testva, [-1, -2, -3, -4]) + push!(testda, [-1, -2, -3, -4]) + @test testva[1:2, 5:6] == [1 4; 2 5] + @test testda[1:2, 5:6] == [1 4; 2 5] - push!(testva, [-1, -2, -3, -4]) - push!(testda, [-1, -2, -3, -4]) - @test testva[1:2, 5:6] == [1 4; 2 5] - @test testda[1:2, 5:6] == [1 4; 2 5] + @test_throws BoundsError testva[4:5, 5:6] + @test_throws BoundsError testda[4:5, 5:6] - @test_throws BoundsError testva[4:5, 5:6] - @test_throws BoundsError testda[4:5, 5:6] + @test testva[:, 9] == [-1, -2, -3, -4] + @test testva[:, end] == [-1, -2, -3, -4] + @test testda[:, 9] == [-1, -2, -3, -4] + @test testda[:, end] == [-1, -2, -3, -4] + end - @test testva[:, 9] == [-1, -2, -3, -4] - @test testva[:, end] == [-1, -2, -3, -4] - @test testda[:, 9] == [-1, -2, -3, -4] - @test testda[:, end] == [-1, -2, -3, -4] - end + @testset "v3 ndims (ported)" begin + testva = RaggedVectorOfArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + @test ndims(testva) == 2 + @test ndims(typeof(testva)) == 2 + end - @testset "v3 ndims (ported)" begin - testva = RaggedVectorOfArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - @test ndims(testva) == 2 - @test ndims(typeof(testva)) == 2 - end + @testset "v3 push! shape enforcement (ported)" begin + testva = RaggedVectorOfArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + testda = RaggedDiffEqArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]], 1:3) - @testset "v3 push! shape enforcement (ported)" begin - testva = RaggedVectorOfArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - testda = RaggedDiffEqArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]], 1:3) + @test_throws MethodError push!(testva, [-1 -2 -3 -4]) + @test_throws MethodError push!(testva, [-1 -2; -3 -4]) + @test_throws MethodError push!(testda, [-1 -2 -3 -4]) + @test_throws MethodError push!(testda, [-1 -2; -3 -4]) + end - @test_throws MethodError push!(testva, [-1 -2 -3 -4]) - @test_throws MethodError push!(testva, [-1 -2; -3 -4]) - @test_throws MethodError push!(testda, [-1 -2 -3 -4]) - @test_throws MethodError push!(testda, [-1 -2; -3 -4]) - end + @testset "v3 type inference (ported)" begin + testva = RaggedVectorOfArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + @inferred sum(testva) + @inferred sum(RaggedVectorOfArray([RaggedVectorOfArray([zeros(4, 4)])])) + @inferred mapreduce(string, *, testva) + # Type stability for `end` indexing (issue #525) + testva_end = RaggedVectorOfArray(fill(fill(2.0, 2), 10)) + # Use lastindex directly since `end` doesn't work in SafeTestsets + last_col = lastindex(testva_end, 2) + @inferred testva_end[1, last_col] + @inferred testva_end[1, 1:last_col] + @test testva_end[1, last_col] == 2.0 + last_row = lastindex(testva_end, 1) + @inferred testva_end[last_row, 1] + @inferred testva_end[1:last_row, 1] + @test testva_end[last_row, 1] == 2.0 + end - @testset "v3 type inference (ported)" begin - testva = RaggedVectorOfArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - @inferred sum(testva) - @inferred sum(RaggedVectorOfArray([RaggedVectorOfArray([zeros(4, 4)])])) - @inferred mapreduce(string, *, testva) - # Type stability for `end` indexing (issue #525) - testva_end = RaggedVectorOfArray(fill(fill(2.0, 2), 10)) - # Use lastindex directly since `end` doesn't work in SafeTestsets - last_col = lastindex(testva_end, 2) - @inferred testva_end[1, last_col] - @inferred testva_end[1, 1:last_col] - @test testva_end[1, last_col] == 2.0 - last_row = lastindex(testva_end, 1) - @inferred testva_end[last_row, 1] - @inferred testva_end[1:last_row, 1] - @test testva_end[last_row, 1] == 2.0 - end + @testset "v3 mapreduce (ported)" begin + testva = RaggedVectorOfArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + @test mapreduce(x -> string(x) * "q", *, testva) == "1q2q3q4q5q6q7q8q9q" - @testset "v3 mapreduce (ported)" begin - testva = RaggedVectorOfArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - @test mapreduce(x -> string(x) * "q", *, testva) == "1q2q3q4q5q6q7q8q9q" + testvb = RaggedVectorOfArray([rand(1:10, 3, 3, 3) for _ in 1:4]) + arrvb = Array(testvb) + for i in 1:ndims(arrvb) + @test sum(arrvb; dims = i) == sum(testvb; dims = i) + @test prod(arrvb; dims = i) == prod(testvb; dims = i) + @test mapreduce(string, *, arrvb; dims = i) == mapreduce(string, *, testvb; dims = i) + end + end - testvb = RaggedVectorOfArray([rand(1:10, 3, 3, 3) for _ in 1:4]) - arrvb = Array(testvb) - for i in 1:ndims(arrvb) - @test sum(arrvb; dims = i) == sum(testvb; dims = i) - @test prod(arrvb; dims = i) == prod(testvb; dims = i) - @test mapreduce(string, *, arrvb; dims = i) == mapreduce(string, *, testvb; dims = i) + @testset "v3 mapreduce ndims==1 (ported)" begin + testvb = RaggedVectorOfArray(collect(1.0:0.1:2.0)) + arrvb = Array(testvb) + @test sum(arrvb) == sum(testvb) + @test prod(arrvb) == prod(testvb) + @test mapreduce(string, *, arrvb) == mapreduce(string, *, testvb) end - end - @testset "v3 mapreduce ndims==1 (ported)" begin - testvb = RaggedVectorOfArray(collect(1.0:0.1:2.0)) - arrvb = Array(testvb) - @test sum(arrvb) == sum(testvb) - @test prod(arrvb) == prod(testvb) - @test mapreduce(string, *, arrvb) == mapreduce(string, *, testvb) - end + @testset "v3 view (ported)" begin + testvc = RaggedVectorOfArray([rand(1:10, 3, 3) for _ in 1:3]) + arrvc = Array(testvc) + for idxs in [ + (2, 2, :), (2, :, 2), (:, 2, 2), (:, :, 2), (:, 2, :), (2, :, :), (:, :, :), + (1:2, 1:2, Bool[1, 0, 1]), (1:2, Bool[1, 0, 1], 1:2), (Bool[1, 0, 1], 1:2, 1:2), + ] + arr_view = view(arrvc, idxs...) + voa_view = view(testvc, idxs...) + @test size(arr_view) == size(voa_view) + @test all(arr_view .== voa_view) + end - @testset "v3 view (ported)" begin - testvc = RaggedVectorOfArray([rand(1:10, 3, 3) for _ in 1:3]) - arrvc = Array(testvc) - for idxs in [ - (2, 2, :), (2, :, 2), (:, 2, 2), (:, :, 2), (:, 2, :), (2, :, :), (:, :, :), - (1:2, 1:2, Bool[1, 0, 1]), (1:2, Bool[1, 0, 1], 1:2), (Bool[1, 0, 1], 1:2, 1:2), - ] - arr_view = view(arrvc, idxs...) - voa_view = view(testvc, idxs...) - @test size(arr_view) == size(voa_view) - @test all(arr_view .== voa_view) + testvc = RaggedVectorOfArray(collect(1:10)) + arrvc = Array(testvc) + bool_idx = rand(Bool, 10) + for (voaidx, arridx) in [ + ((:,), (:,)), + ((3:5,), (3:5,)), + ((bool_idx,), (bool_idx,)), + ] + arr_view = view(arrvc, arridx...) + voa_view = view(testvc.u, voaidx...) + @test size(arr_view) == size(voa_view) + @test all(arr_view .== voa_view) + end end - testvc = RaggedVectorOfArray(collect(1:10)) - arrvc = Array(testvc) - bool_idx = rand(Bool, 10) - for (voaidx, arridx) in [ - ((:,), (:,)), - ((3:5,), (3:5,)), - ((bool_idx,), (bool_idx,)), - ] - arr_view = view(arrvc, arridx...) - voa_view = view(testvc.u, voaidx...) - @test size(arr_view) == size(voa_view) - @test all(arr_view .== voa_view) - end - end + @testset "v3 stack (ported)" begin + testva = RaggedVectorOfArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + @test stack(testva) == [1 4 7; 2 5 8; 3 6 9] + @test stack(testva; dims = 1) == [1 2 3; 4 5 6; 7 8 9] - @testset "v3 stack (ported)" begin - testva = RaggedVectorOfArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - @test stack(testva) == [1 4 7; 2 5 8; 3 6 9] - @test stack(testva; dims = 1) == [1 2 3; 4 5 6; 7 8 9] + testva = RaggedVectorOfArray([RaggedVectorOfArray([ones(2, 2), 2ones(2, 2)]), 3ones(2, 2, 2)]) + @test stack(testva) == + [1.0 1.0; 1.0 1.0;;; 2.0 2.0; 2.0 2.0;;;; 3.0 3.0; 3.0 3.0;;; 3.0 3.0; 3.0 3.0] + end - testva = RaggedVectorOfArray([RaggedVectorOfArray([ones(2, 2), 2ones(2, 2)]), 3ones(2, 2, 2)]) - @test stack(testva) == - [1.0 1.0; 1.0 1.0;;; 2.0 2.0; 2.0 2.0;;;; 3.0 3.0; 3.0 3.0;;; 3.0 3.0; 3.0 3.0] - end + @testset "v3 convert Array (ported)" begin + t = 1:8 + recs = [rand(10, 7) for i in 1:8] + testva = RaggedVectorOfArray(recs) + testda = RaggedDiffEqArray(recs, t) + testa = cat(recs..., dims = 3) - @testset "v3 convert Array (ported)" begin - t = 1:8 - recs = [rand(10, 7) for i in 1:8] - testva = RaggedVectorOfArray(recs) - testda = RaggedDiffEqArray(recs, t) - testa = cat(recs..., dims = 3) + @test convert(Array, testva) == testa + @test convert(Array, testda) == testa - @test convert(Array, testva) == testa - @test convert(Array, testda) == testa + t = 1:3 + recs = [[1 2; 3 4], [3 5; 6 7], [8 9; 10 11]] + testva = RaggedVectorOfArray(recs) + testda = RaggedDiffEqArray(recs, t) - t = 1:3 - recs = [[1 2; 3 4], [3 5; 6 7], [8 9; 10 11]] - testva = RaggedVectorOfArray(recs) - testda = RaggedDiffEqArray(recs, t) + @test size(convert(Array, testva)) == (2, 2, 3) + @test size(convert(Array, testda)) == (2, 2, 3) + end - @test size(convert(Array, testva)) == (2, 2, 3) - @test size(convert(Array, testda)) == (2, 2, 3) - end + @testset "v3 similar (ported)" begin + recs = [rand(6) for i in 1:4] + testva = RaggedVectorOfArray(recs) - @testset "v3 similar (ported)" begin - recs = [rand(6) for i in 1:4] - testva = RaggedVectorOfArray(recs) + testva2 = similar(testva) + @test typeof(testva2) == typeof(testva) + @test size(testva2) == size(testva) - testva2 = similar(testva) - @test typeof(testva2) == typeof(testva) - @test size(testva2) == size(testva) + testva3 = similar(testva, 10) + @test typeof(testva3) == typeof(testva) + @test length(testva3) == 10 + end - testva3 = similar(testva, 10) - @test typeof(testva3) == typeof(testva) - @test length(testva3) == 10 - end + @testset "v3 fill! and all (ported)" begin + recs = [rand(6) for i in 1:4] + testva = RaggedVectorOfArray(recs) + + testval = 3.0 + testva2 = similar(testva) + fill!(testva2, testval) + @test all(x -> (x == testval), testva2) + testts = rand(Float64, size(testva.u)) + testda = RaggedDiffEqArray(recursivecopy(testva.u), testts) + fill!(testda, testval) + @test all(x -> (x == testval), testda) + end - @testset "v3 fill! and all (ported)" begin - recs = [rand(6) for i in 1:4] - testva = RaggedVectorOfArray(recs) - - testval = 3.0 - testva2 = similar(testva) - fill!(testva2, testval) - @test all(x -> (x == testval), testva2) - testts = rand(Float64, size(testva.u)) - testda = RaggedDiffEqArray(recursivecopy(testva.u), testts) - fill!(testda, testval) - @test all(x -> (x == testval), testda) - end + @testset "v3 copyto! (ported)" begin + testva = RaggedVectorOfArray(collect(0.1:0.1:1.0)) + arr = 0.2:0.2:2.0 + copyto!(testva, arr) + @test Array(testva) == arr + testva = RaggedVectorOfArray([i * ones(3, 2) for i in 1:4]) + arr = rand(3, 2, 4) + copyto!(testva, arr) + @test Array(testva) == arr + testva = RaggedVectorOfArray( + [ + ones(3, 2, 2), + RaggedVectorOfArray( + [ + 2ones(3, 2), + RaggedVectorOfArray([3ones(3), 4ones(3)]), + ] + ), + RaggedDiffEqArray( + [ + 5ones(3, 2), + RaggedVectorOfArray([6ones(3), 7ones(3)]), + ], + [0.1, 0.2], + [100.0, 200.0], + SymbolCache([:x, :y], [:a, :b], :t) + ), + ] + ) + arr = rand(3, 2, 2, 3) + copyto!(testva, arr) + @test Array(testva) == arr + # ensure structure and fields are maintained + @test testva.u[1] isa Array + @test testva.u[2] isa RaggedVectorOfArray + @test testva.u[2].u[2] isa RaggedVectorOfArray + @test testva.u[3] isa RaggedDiffEqArray + @test testva.u[3].u[2] isa RaggedVectorOfArray + @test testva.u[3].t == [0.1, 0.2] + @test testva.u[3].p == [100.0, 200.0] + @test testva.u[3].sys isa SymbolCache + end - @testset "v3 copyto! (ported)" begin - testva = RaggedVectorOfArray(collect(0.1:0.1:1.0)) - arr = 0.2:0.2:2.0 - copyto!(testva, arr) - @test Array(testva) == arr - testva = RaggedVectorOfArray([i * ones(3, 2) for i in 1:4]) - arr = rand(3, 2, 4) - copyto!(testva, arr) - @test Array(testva) == arr - testva = RaggedVectorOfArray( - [ - ones(3, 2, 2), - RaggedVectorOfArray( - [ - 2ones(3, 2), - RaggedVectorOfArray([3ones(3), 4ones(3)]), - ] - ), - RaggedDiffEqArray( - [ - 5ones(3, 2), - RaggedVectorOfArray([6ones(3), 7ones(3)]), - ], - [0.1, 0.2], - [100.0, 200.0], - SymbolCache([:x, :y], [:a, :b], :t) - ), - ] - ) - arr = rand(3, 2, 2, 3) - copyto!(testva, arr) - @test Array(testva) == arr - # ensure structure and fields are maintained - @test testva.u[1] isa Array - @test testva.u[2] isa RaggedVectorOfArray - @test testva.u[2].u[2] isa RaggedVectorOfArray - @test testva.u[3] isa RaggedDiffEqArray - @test testva.u[3].u[2] isa RaggedVectorOfArray - @test testva.u[3].t == [0.1, 0.2] - @test testva.u[3].p == [100.0, 200.0] - @test testva.u[3].sys isa SymbolCache - end + @testset "v3 any (ported)" begin + recs = [collect(1:5), collect(6:10), collect(11:15)] + testts = rand(5) + testva = RaggedVectorOfArray(recs) + testda = RaggedDiffEqArray(recs, testts) + testval1 = 4 + testval2 = 17 + @test any(x -> (x == testval1), testva) + @test !any(x -> (x == testval2), testda) + end - @testset "v3 any (ported)" begin - recs = [collect(1:5), collect(6:10), collect(11:15)] - testts = rand(5) - testva = RaggedVectorOfArray(recs) - testda = RaggedDiffEqArray(recs, testts) - testval1 = 4 - testval2 = 17 - @test any(x -> (x == testval1), testva) - @test !any(x -> (x == testval2), testda) - end + @testset "v3 empty creation (ported)" begin + emptyva = RaggedVectorOfArray(Array{Vector{Float64}}([])) + @test isempty(emptyva) + emptyda = RaggedDiffEqArray(Array{Vector{Float64}}([]), Vector{Float64}()) + @test isempty(emptyda) + end - @testset "v3 empty creation (ported)" begin - emptyva = RaggedVectorOfArray(Array{Vector{Float64}}([])) - @test isempty(emptyva) - emptyda = RaggedDiffEqArray(Array{Vector{Float64}}([]), Vector{Float64}()) - @test isempty(emptyda) - end + @testset "v3 map (ported)" begin + A = RaggedVectorOfArray(map(i -> rand(2, 4), 1:7)) + @test map(x -> maximum(x), A) isa Vector - @testset "v3 map (ported)" begin - A = RaggedVectorOfArray(map(i -> rand(2, 4), 1:7)) - @test map(x -> maximum(x), A) isa Vector + DA = RaggedDiffEqArray(map(i -> rand(2, 4), 1:7), 1:7) + @test map(x -> maximum(x), DA) isa Vector + end - DA = RaggedDiffEqArray(map(i -> rand(2, 4), 1:7), 1:7) - @test map(x -> maximum(x), DA) isa Vector - end + @testset "v3 zero and resize! (ported)" begin + u = RaggedVectorOfArray([fill(2.0, 2), ones(2)]) + @test typeof(zero(u)) <: typeof(u) + resize!(u, 3) + @test pointer(u) === pointer(u.u) + end - @testset "v3 zero and resize! (ported)" begin - u = RaggedVectorOfArray([fill(2.0, 2), ones(2)]) - @test typeof(zero(u)) <: typeof(u) - resize!(u, 3) - @test pointer(u) === pointer(u.u) - end + @testset "v3 DiffEqArray constructor ambiguity (ported, issue SciMLBase#889)" begin + darr = RaggedDiffEqArray([[1.0, 1.0]], [1.0], ()) + @test darr.p == () + @test darr.sys === nothing + @test ndims(darr) == 2 + darr = RaggedDiffEqArray([[1.0, 1.0]], [1.0], (), "A") + @test darr.p == () + @test darr.sys == "A" + @test ndims(darr) == 2 + darr = RaggedDiffEqArray([ones(2, 2)], [1.0], (1, 1, 1)) + @test darr.p == (1, 1, 1) + @test darr.sys === nothing + @test ndims(darr) == 3 + darr = RaggedDiffEqArray([ones(2, 2)], [1.0], (1, 1, 1), "A") + @test darr.p == (1, 1, 1) + @test darr.sys == "A" + @test ndims(darr) == 3 + end - @testset "v3 DiffEqArray constructor ambiguity (ported, issue SciMLBase#889)" begin - darr = RaggedDiffEqArray([[1.0, 1.0]], [1.0], ()) - @test darr.p == () - @test darr.sys === nothing - @test ndims(darr) == 2 - darr = RaggedDiffEqArray([[1.0, 1.0]], [1.0], (), "A") - @test darr.p == () - @test darr.sys == "A" - @test ndims(darr) == 2 - darr = RaggedDiffEqArray([ones(2, 2)], [1.0], (1, 1, 1)) - @test darr.p == (1, 1, 1) - @test darr.sys === nothing - @test ndims(darr) == 3 - darr = RaggedDiffEqArray([ones(2, 2)], [1.0], (1, 1, 1), "A") - @test darr.p == (1, 1, 1) - @test darr.sys == "A" - @test ndims(darr) == 3 - end + @testset "v3 system retained in 4-argument constructor (ported)" begin + darr = RaggedDiffEqArray([ones(2)], [1.0], :params, :sys) + @test darr.sys == :sys + end - @testset "v3 system retained in 4-argument constructor (ported)" begin - darr = RaggedDiffEqArray([ones(2)], [1.0], :params, :sys) - @test darr.sys == :sys - end + # issparse test skipped: ragged types are not AbstractArray, so + # SparseArrays.issparse is not defined for them (issue #486 only + # applies to AbstractVectorOfArray which is now AbstractArray). - # issparse test skipped: ragged types are not AbstractArray, so - # SparseArrays.issparse is not defined for them (issue #486 only - # applies to AbstractVectorOfArray which is now AbstractArray). + # =================================================================== + # Additional tests for new v4 functionality (interp, conversion, SII) + # =================================================================== - # =================================================================== - # Additional tests for new v4 functionality (interp, conversion, SII) - # =================================================================== + @testset "RaggedVectorOfArray construction (typed)" begin + r = RaggedVectorOfArray([[1.0, 2.0], [3.0, 4.0, 5.0]]) + @test r.u == [[1.0, 2.0], [3.0, 4.0, 5.0]] + @test length(r) == 2 + @test ndims(r) == 2 - @testset "RaggedVectorOfArray construction (typed)" begin - r = RaggedVectorOfArray([[1.0, 2.0], [3.0, 4.0, 5.0]]) - @test r.u == [[1.0, 2.0], [3.0, 4.0, 5.0]] - @test length(r) == 2 - @test ndims(r) == 2 + # From typed vector + r2 = RaggedVectorOfArray(Vector{Float64}[[1.0], [2.0, 3.0]]) + @test length(r2) == 2 + end - # From typed vector - r2 = RaggedVectorOfArray(Vector{Float64}[[1.0], [2.0, 3.0]]) - @test length(r2) == 2 - end + # Conversion, SII, interp/dense, and type hierarchy tests below - # Conversion, SII, interp/dense, and type hierarchy tests below + @testset "RaggedVectorOfArray <-> VectorOfArray conversion" begin + r = RaggedVectorOfArray([[1.0, 2.0], [3.0, 4.0, 5.0]]) - @testset "RaggedVectorOfArray <-> VectorOfArray conversion" begin - r = RaggedVectorOfArray([[1.0, 2.0], [3.0, 4.0, 5.0]]) + # To VectorOfArray (rectangular, zero-padded) + va = VectorOfArray(r) + @test va isa VectorOfArray + @test size(va) == (3, 2) + @test va[:, 1] == [1.0, 2.0, 0.0] # zero-padded + @test va[:, 2] == [3.0, 4.0, 5.0] - # To VectorOfArray (rectangular, zero-padded) - va = VectorOfArray(r) - @test va isa VectorOfArray - @test size(va) == (3, 2) - @test va[:, 1] == [1.0, 2.0, 0.0] # zero-padded - @test va[:, 2] == [3.0, 4.0, 5.0] + # Back to RaggedVectorOfArray + r2 = RaggedVectorOfArray(va) + @test r2 isa RaggedVectorOfArray + # u is shared, inner arrays have original sizes + @test r2[:, 1] == [1.0, 2.0] + @test r2[:, 2] == [3.0, 4.0, 5.0] + end - # Back to RaggedVectorOfArray - r2 = RaggedVectorOfArray(va) - @test r2 isa RaggedVectorOfArray - # u is shared, inner arrays have original sizes - @test r2[:, 1] == [1.0, 2.0] - @test r2[:, 2] == [3.0, 4.0, 5.0] - end + @testset "RaggedVectorOfArray equality" begin + r1 = RaggedVectorOfArray([[1, 2], [3, 4, 5]]) + r2 = RaggedVectorOfArray([[1, 2], [3, 4, 5]]) + r3 = RaggedVectorOfArray([[1, 2], [3, 4, 6]]) + @test r1 == r2 + @test r1 != r3 + end - @testset "RaggedVectorOfArray equality" begin - r1 = RaggedVectorOfArray([[1, 2], [3, 4, 5]]) - r2 = RaggedVectorOfArray([[1, 2], [3, 4, 5]]) - r3 = RaggedVectorOfArray([[1, 2], [3, 4, 6]]) - @test r1 == r2 - @test r1 != r3 - end + @testset "RaggedVectorOfArray reverse" begin + r = RaggedVectorOfArray([[1.0], [2.0, 3.0], [4.0, 5.0, 6.0]]) + rr = reverse(r) + @test rr[:, 1] == [4.0, 5.0, 6.0] + @test rr[:, 2] == [2.0, 3.0] + @test rr[:, 3] == [1.0] + end - @testset "RaggedVectorOfArray reverse" begin - r = RaggedVectorOfArray([[1.0], [2.0, 3.0], [4.0, 5.0, 6.0]]) - rr = reverse(r) - @test rr[:, 1] == [4.0, 5.0, 6.0] - @test rr[:, 2] == [2.0, 3.0] - @test rr[:, 3] == [1.0] - end + @testset "RaggedDiffEqArray construction" begin + r = RaggedDiffEqArray([[1.0, 2.0], [3.0, 4.0, 5.0]], [0.0, 1.0]) + @test r.u == [[1.0, 2.0], [3.0, 4.0, 5.0]] + @test r.t == [0.0, 1.0] + @test length(r) == 2 - @testset "RaggedDiffEqArray construction" begin - r = RaggedDiffEqArray([[1.0, 2.0], [3.0, 4.0, 5.0]], [0.0, 1.0]) - @test r.u == [[1.0, 2.0], [3.0, 4.0, 5.0]] - @test r.t == [0.0, 1.0] - @test length(r) == 2 + # With parameters + r2 = RaggedDiffEqArray([[1.0], [2.0, 3.0]], [0.0, 1.0], [0.5]) + @test r2.p == [0.5] + end - # With parameters - r2 = RaggedDiffEqArray([[1.0], [2.0, 3.0]], [0.0, 1.0], [0.5]) - @test r2.p == [0.5] - end + @testset "RaggedDiffEqArray indexing — no zero-padding" begin + r = RaggedDiffEqArray([[1.0, 2.0], [3.0, 4.0, 5.0]], [0.0, 1.0]) + + @test r[:, 1] == [1.0, 2.0] + @test r[:, 2] == [3.0, 4.0, 5.0] + @test r[1, 1] == 1.0 + @test r[2, 2] == 4.0 + @test_throws BoundsError r[3, 1] + + # Linear indexing returns inner arrays (v3 behavior) + @test r[1] == [1.0, 2.0] + @test r[2] == [3.0, 4.0, 5.0] + + # Colon-colon preserves DiffEqArray type + r_all = r[:, :] + @test r_all isa RaggedDiffEqArray + @test r_all.t == [0.0, 1.0] + + # Subset preserves time + r_sub = r[:, [2]] + @test r_sub isa RaggedDiffEqArray + @test r_sub.t == [1.0] + @test r_sub[:, 1] == [3.0, 4.0, 5.0] + end - @testset "RaggedDiffEqArray indexing — no zero-padding" begin - r = RaggedDiffEqArray([[1.0, 2.0], [3.0, 4.0, 5.0]], [0.0, 1.0]) - - @test r[:, 1] == [1.0, 2.0] - @test r[:, 2] == [3.0, 4.0, 5.0] - @test r[1, 1] == 1.0 - @test r[2, 2] == 4.0 - @test_throws BoundsError r[3, 1] - - # Linear indexing returns inner arrays (v3 behavior) - @test r[1] == [1.0, 2.0] - @test r[2] == [3.0, 4.0, 5.0] - - # Colon-colon preserves DiffEqArray type - r_all = r[:, :] - @test r_all isa RaggedDiffEqArray - @test r_all.t == [0.0, 1.0] - - # Subset preserves time - r_sub = r[:, [2]] - @test r_sub isa RaggedDiffEqArray - @test r_sub.t == [1.0] - @test r_sub[:, 1] == [3.0, 4.0, 5.0] - end + @testset "RaggedDiffEqArray <-> DiffEqArray conversion" begin + r = RaggedDiffEqArray([[1.0, 2.0], [3.0, 4.0, 5.0]], [0.0, 1.0]) - @testset "RaggedDiffEqArray <-> DiffEqArray conversion" begin - r = RaggedDiffEqArray([[1.0, 2.0], [3.0, 4.0, 5.0]], [0.0, 1.0]) + da = DiffEqArray(r) + @test da isa DiffEqArray + @test da.t == [0.0, 1.0] - da = DiffEqArray(r) - @test da isa DiffEqArray - @test da.t == [0.0, 1.0] + r2 = RaggedDiffEqArray(da) + @test r2 isa RaggedDiffEqArray + @test r2[:, 1] == [1.0, 2.0] + @test r2[:, 2] == [3.0, 4.0, 5.0] + @test r2.t == [0.0, 1.0] + end - r2 = RaggedDiffEqArray(da) - @test r2 isa RaggedDiffEqArray - @test r2[:, 1] == [1.0, 2.0] - @test r2[:, 2] == [3.0, 4.0, 5.0] - @test r2.t == [0.0, 1.0] - end + @testset "RaggedDiffEqArray copy/zero" begin + r = RaggedDiffEqArray([[1.0, 2.0], [3.0, 4.0, 5.0]], [0.0, 1.0]) - @testset "RaggedDiffEqArray copy/zero" begin - r = RaggedDiffEqArray([[1.0, 2.0], [3.0, 4.0, 5.0]], [0.0, 1.0]) + r2 = copy(r) + @test r2 == r + @test r2.u !== r.u - r2 = copy(r) - @test r2 == r - @test r2.u !== r.u + r0 = zero(r) + @test r0[:, 1] == [0.0, 0.0] + @test r0[:, 2] == [0.0, 0.0, 0.0] + @test r0.t == [0.0, 1.0] + end - r0 = zero(r) - @test r0[:, 1] == [0.0, 0.0] - @test r0[:, 2] == [0.0, 0.0, 0.0] - @test r0.t == [0.0, 1.0] - end + @testset "RaggedDiffEqArray SymbolicIndexingInterface" begin + r = RaggedDiffEqArray( + [[1.0, 2.0], [3.0, 4.0, 5.0]], [0.0, 1.0]; + variables = [:a, :b], parameters = [:p], independent_variables = [:t] + ) + + @test is_timeseries(r) == Timeseries() + @test state_values(r) === r.u + @test current_time(r) === r.t + @test symbolic_container(r) === r.sys + + # Variable/parameter query + @test is_variable(r, :a) + @test is_variable(r, :b) + @test is_parameter(r, :p) + @test is_independent_variable(r, :t) + @test variable_index(r, :a) == 1 + @test variable_index(r, :b) == 2 + @test parameter_index(r, :p) == 1 + end - @testset "RaggedDiffEqArray SymbolicIndexingInterface" begin - r = RaggedDiffEqArray( - [[1.0, 2.0], [3.0, 4.0, 5.0]], [0.0, 1.0]; - variables = [:a, :b], parameters = [:p], independent_variables = [:t] - ) - - @test is_timeseries(r) == Timeseries() - @test state_values(r) === r.u - @test current_time(r) === r.t - @test symbolic_container(r) === r.sys - - # Variable/parameter query - @test is_variable(r, :a) - @test is_variable(r, :b) - @test is_parameter(r, :p) - @test is_independent_variable(r, :t) - @test variable_index(r, :a) == 1 - @test variable_index(r, :b) == 2 - @test parameter_index(r, :p) == 1 - end + @testset "RaggedDiffEqArray symbolic getindex" begin + r = RaggedDiffEqArray( + [[1.0, 2.0], [3.0, 4.0, 5.0]], [0.0, 1.0], [0.5]; + variables = [:a, :b], parameters = [:p], independent_variables = [:t] + ) - @testset "RaggedDiffEqArray symbolic getindex" begin - r = RaggedDiffEqArray( - [[1.0, 2.0], [3.0, 4.0, 5.0]], [0.0, 1.0], [0.5]; - variables = [:a, :b], parameters = [:p], independent_variables = [:t] - ) + # Symbolic variable indexing + @test r[:a] == [1.0, 3.0] + @test r[:a, 1] == 1.0 + @test r[:a, 2] == 3.0 + @test r[:b] == [2.0, 4.0] - # Symbolic variable indexing - @test r[:a] == [1.0, 3.0] - @test r[:a, 1] == 1.0 - @test r[:a, 2] == 3.0 - @test r[:b] == [2.0, 4.0] + # Parameter indexing should throw + @test_throws RecursiveArrayToolsRaggedArrays.ParameterIndexingError r[:p] + end - # Parameter indexing should throw - @test_throws RecursiveArrayToolsRaggedArrays.ParameterIndexingError r[:p] - end + @testset "2D inner arrays" begin + r = RaggedVectorOfArray([zeros(2, 3), zeros(2, 4)]) + @test length(r) == 2 + @test ndims(r) == 3 + @test size(r[:, 1]) == (2, 3) + @test size(r[:, 2]) == (2, 4) - @testset "2D inner arrays" begin - r = RaggedVectorOfArray([zeros(2, 3), zeros(2, 4)]) - @test length(r) == 2 - @test ndims(r) == 3 - @test size(r[:, 1]) == (2, 3) - @test size(r[:, 2]) == (2, 4) + # No zero-padding in column access + @test r[:, 1] == zeros(2, 3) + @test r[:, 2] == zeros(2, 4) + end - # No zero-padding in column access - @test r[:, 1] == zeros(2, 3) - @test r[:, 2] == zeros(2, 4) - end + @testset "RaggedDiffEqArray interp/dense fields" begin + r = RaggedDiffEqArray([[1.0, 2.0], [3.0, 4.0, 5.0]], [0.0, 1.0]) + @test r.interp === nothing + @test r.dense == false + + # With interp kwarg + r2 = RaggedDiffEqArray( + [[1.0, 2.0], [3.0, 4.0, 5.0]], [0.0, 1.0]; + interp = :test_interp, dense = true + ) + @test r2.interp == :test_interp + @test r2.dense == true + + # Callable errors when no interp + @test_throws ErrorException r(0.5) + + # Conversion preserves interp/dense + da = DiffEqArray(r2) + @test da.interp == :test_interp + @test da.dense == true + + r3 = RaggedDiffEqArray(da) + @test r3.interp == :test_interp + @test r3.dense == true + + # Copy preserves interp/dense + r4 = copy(r2) + @test r4.interp == :test_interp + @test r4.dense == true + end - @testset "RaggedDiffEqArray interp/dense fields" begin - r = RaggedDiffEqArray([[1.0, 2.0], [3.0, 4.0, 5.0]], [0.0, 1.0]) - @test r.interp === nothing - @test r.dense == false - - # With interp kwarg - r2 = RaggedDiffEqArray( - [[1.0, 2.0], [3.0, 4.0, 5.0]], [0.0, 1.0]; - interp = :test_interp, dense = true - ) - @test r2.interp == :test_interp - @test r2.dense == true - - # Callable errors when no interp - @test_throws ErrorException r(0.5) - - # Conversion preserves interp/dense - da = DiffEqArray(r2) - @test da.interp == :test_interp - @test da.dense == true - - r3 = RaggedDiffEqArray(da) - @test r3.interp == :test_interp - @test r3.dense == true - - # Copy preserves interp/dense - r4 = copy(r2) - @test r4.interp == :test_interp - @test r4.dense == true - end + @testset "v3 zero and fill on ragged (ported)" begin + r = RaggedVectorOfArray([[1.0, 2.0], [3.0, 4.0, 5.0]]) + + # zero preserves ragged structure + r0 = zero(r) + @test r0[:, 1] == [0.0, 0.0] + @test r0[:, 2] == [0.0, 0.0, 0.0] + @test length(r0[:, 1]) == 2 + @test length(r0[:, 2]) == 3 + + # fill! preserves ragged structure + fill!(r0, 42.0) + @test r0[:, 1] == [42.0, 42.0] + @test r0[:, 2] == [42.0, 42.0, 42.0] + + # .= zero works (the exact bug JoshuaLampert reported) + r2 = copy(r) + r2 .= zero(r2) + @test r2[:, 1] == [0.0, 0.0] + @test r2[:, 2] == [0.0, 0.0, 0.0] + end - @testset "v3 zero and fill on ragged (ported)" begin - r = RaggedVectorOfArray([[1.0, 2.0], [3.0, 4.0, 5.0]]) - - # zero preserves ragged structure - r0 = zero(r) - @test r0[:, 1] == [0.0, 0.0] - @test r0[:, 2] == [0.0, 0.0, 0.0] - @test length(r0[:, 1]) == 2 - @test length(r0[:, 2]) == 3 - - # fill! preserves ragged structure - fill!(r0, 42.0) - @test r0[:, 1] == [42.0, 42.0] - @test r0[:, 2] == [42.0, 42.0, 42.0] - - # .= zero works (the exact bug JoshuaLampert reported) - r2 = copy(r) - r2 .= zero(r2) - @test r2[:, 1] == [0.0, 0.0] - @test r2[:, 2] == [0.0, 0.0, 0.0] - end + @testset "v3 component timeseries (ported)" begin + r = RaggedVectorOfArray([[1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0]]) - @testset "v3 component timeseries (ported)" begin - r = RaggedVectorOfArray([[1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0]]) + # A[j, :] returns the j-th component across all inner arrays + @test r[1, :] == [1.0, 3.0, 6.0] + @test r[2, :] == [2.0, 4.0, 7.0] + end - # A[j, :] returns the j-th component across all inner arrays - @test r[1, :] == [1.0, 3.0, 6.0] - @test r[2, :] == [2.0, 4.0, 7.0] - end + @testset "Type hierarchy" begin + r = RaggedVectorOfArray([[1, 2], [3, 4, 5]]) + @test r isa RecursiveArrayTools.AbstractRaggedVectorOfArray + @test !(r isa AbstractArray) - @testset "Type hierarchy" begin - r = RaggedVectorOfArray([[1, 2], [3, 4, 5]]) - @test r isa RecursiveArrayTools.AbstractRaggedVectorOfArray - @test !(r isa AbstractArray) + rd = RaggedDiffEqArray([[1.0], [2.0, 3.0]], [0.0, 1.0]) + @test rd isa RecursiveArrayTools.AbstractRaggedDiffEqArray + @test rd isa RecursiveArrayTools.AbstractRaggedVectorOfArray + @test !(rd isa AbstractArray) + end - rd = RaggedDiffEqArray([[1.0], [2.0, 3.0]], [0.0, 1.0]) - @test rd isa RecursiveArrayTools.AbstractRaggedDiffEqArray - @test rd isa RecursiveArrayTools.AbstractRaggedVectorOfArray - @test !(rd isa AbstractArray) - end + @testset "recursivefill! for RaggedVectorOfArray" begin + # Bool argument — the pattern used by ODE solver cache initialisation + r = RaggedVectorOfArray([ones(2), ones(3)]) + recursivefill!(r, false) + @test r[:, 1] == [0.0, 0.0] + @test r[:, 2] == [0.0, 0.0, 0.0] + + # Numeric argument + r2 = RaggedVectorOfArray([zeros(2), zeros(3)]) + recursivefill!(r2, 1.0) + @test r2[:, 1] == [1.0, 1.0] + @test r2[:, 2] == [1.0, 1.0, 1.0] + + # Ragged sizes are preserved + @test length(r[:, 1]) == 2 + @test length(r[:, 2]) == 3 + end - @testset "recursivefill! for RaggedVectorOfArray" begin - # Bool argument — the pattern used by ODE solver cache initialisation - r = RaggedVectorOfArray([ones(2), ones(3)]) - recursivefill!(r, false) - @test r[:, 1] == [0.0, 0.0] - @test r[:, 2] == [0.0, 0.0, 0.0] - - # Numeric argument - r2 = RaggedVectorOfArray([zeros(2), zeros(3)]) - recursivefill!(r2, 1.0) - @test r2[:, 1] == [1.0, 1.0] - @test r2[:, 2] == [1.0, 1.0, 1.0] - - # Ragged sizes are preserved - @test length(r[:, 1]) == 2 - @test length(r[:, 2]) == 3 - end + @testset "recursivecopy! for RaggedVectorOfArray" begin + src = RaggedVectorOfArray([ones(2), 2 * ones(3)]) + dst = RaggedVectorOfArray([zeros(2), zeros(3)]) + recursivecopy!(dst, src) + @test dst[:, 1] == [1.0, 1.0] + @test dst[:, 2] == [2.0, 2.0, 2.0] - @testset "recursivecopy! for RaggedVectorOfArray" begin - src = RaggedVectorOfArray([ones(2), 2 * ones(3)]) - dst = RaggedVectorOfArray([zeros(2), zeros(3)]) - recursivecopy!(dst, src) - @test dst[:, 1] == [1.0, 1.0] - @test dst[:, 2] == [2.0, 2.0, 2.0] + # Verify deep copy — modifying src must not affect dst + src[:, 1] .= 99.0 + @test dst[:, 1] == [1.0, 1.0] + end - # Verify deep copy — modifying src must not affect dst - src[:, 1] .= 99.0 - @test dst[:, 1] == [1.0, 1.0] - end + @testset "mapreduce over nested ragged arrays" begin + # Outer array whose inner RaggedVoA elements have different column counts. + # mapreduce must recurse over A.u rather than building a fixed-shape view. + inner1 = RaggedVectorOfArray([ones(3), ones(3)]) # 2 columns + inner2 = RaggedVectorOfArray([ones(3), ones(3), ones(3)]) # 3 columns — ragged! + u = RaggedVectorOfArray([inner1, inner2]) - @testset "mapreduce over nested ragged arrays" begin - # Outer array whose inner RaggedVoA elements have different column counts. - # mapreduce must recurse over A.u rather than building a fixed-shape view. - inner1 = RaggedVectorOfArray([ones(3), ones(3)]) # 2 columns - inner2 = RaggedVectorOfArray([ones(3), ones(3), ones(3)]) # 3 columns — ragged! - u = RaggedVectorOfArray([inner1, inner2]) + @test mapreduce(identity, +, u) == 15.0 # (2+3)*3 + end - @test mapreduce(identity, +, u) == 15.0 # (2+3)*3 end - end diff --git a/lib/RecursiveArrayToolsRaggedArrays/test/test_groups.toml b/lib/RecursiveArrayToolsRaggedArrays/test/test_groups.toml new file mode 100644 index 00000000..dda1469c --- /dev/null +++ b/lib/RecursiveArrayToolsRaggedArrays/test/test_groups.toml @@ -0,0 +1,2 @@ +[Core] +versions = ["lts", "1", "pre"] diff --git a/lib/RecursiveArrayToolsShorthandConstructors/Project.toml b/lib/RecursiveArrayToolsShorthandConstructors/Project.toml index a355d716..6ad88ae4 100644 --- a/lib/RecursiveArrayToolsShorthandConstructors/Project.toml +++ b/lib/RecursiveArrayToolsShorthandConstructors/Project.toml @@ -5,6 +5,9 @@ version = "1.0.0" [deps] RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" +[sources] +RecursiveArrayTools = {path = "../.."} + [compat] RecursiveArrayTools = "4" julia = "1.10" diff --git a/lib/RecursiveArrayToolsShorthandConstructors/README.md b/lib/RecursiveArrayToolsShorthandConstructors/README.md index 7b957a5d..86494a0e 100644 --- a/lib/RecursiveArrayToolsShorthandConstructors/README.md +++ b/lib/RecursiveArrayToolsShorthandConstructors/README.md @@ -1,7 +1,13 @@ # RecursiveArrayToolsShorthandConstructors.jl -Shorthand constructor syntax for -[RecursiveArrayTools.jl](https://github.com/SciML/RecursiveArrayTools.jl) types. +[![Join the chat at https://julialang.zulipchat.com #sciml-bridged](https://img.shields.io/static/v1?label=Zulip&message=chat&color=9558b2&labelColor=389826)](https://julialang.zulipchat.com/#narrow/stream/279055-sciml-bridged) +[![Global Docs](https://img.shields.io/badge/docs-SciML-blue.svg)](https://docs.sciml.ai/RecursiveArrayTools/stable/) + +[![ColPrac: Contributor's Guide on Collaborative Practices for Community Packages](https://img.shields.io/badge/ColPrac-Contributor%27s%20Guide-blueviolet)](https://github.com/SciML/ColPrac) +[![SciML Code Style](https://img.shields.io/static/v1?label=code%20style&message=SciML&color=9558b2&labelColor=389826)](https://github.com/SciML/SciMLStyle) + +RecursiveArrayToolsShorthandConstructors.jl is a component of the [RecursiveArrayTools.jl](https://github.com/SciML/RecursiveArrayTools.jl) monorepo. Shorthand `VA[...]` and `AP[...]` constructor syntax for RecursiveArrayTools types. +While completely independent and usable on its own, users wanting the full functionality should use [RecursiveArrayTools.jl](https://github.com/SciML/RecursiveArrayTools.jl). This subpackage provides `VA[...]` and `AP[...]` syntax for constructing `VectorOfArray` and `ArrayPartition` objects. It is separated from the main diff --git a/lib/RecursiveArrayToolsShorthandConstructors/test/runtests.jl b/lib/RecursiveArrayToolsShorthandConstructors/test/runtests.jl index 006061ea..ea04eaca 100644 --- a/lib/RecursiveArrayToolsShorthandConstructors/test/runtests.jl +++ b/lib/RecursiveArrayToolsShorthandConstructors/test/runtests.jl @@ -1,29 +1,33 @@ using RecursiveArrayTools, RecursiveArrayToolsShorthandConstructors, Test -@testset "VA[...] shorthand" begin - recs = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] - testva = VA[[1, 2, 3], [4, 5, 6], [7, 8, 9]] - @test testva isa VectorOfArray - @test testva.u == recs - @test Array(testva) == [1 4 7; 2 5 8; 3 6 9] +const TEST_GROUP = get(ENV, "RECURSIVEARRAYTOOLS_TEST_GROUP", "Core") - # Nesting - nested = VA[ - fill(1, 2, 3), - VA[3ones(3), zeros(3)], - ] - @test nested isa VectorOfArray - @test nested.u[1] == fill(1, 2, 3) - @test nested.u[2] isa VectorOfArray -end +if TEST_GROUP == "Core" || TEST_GROUP == "All" + @testset "VA[...] shorthand" begin + recs = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] + testva = VA[[1, 2, 3], [4, 5, 6], [7, 8, 9]] + @test testva isa VectorOfArray + @test testva.u == recs + @test Array(testva) == [1 4 7; 2 5 8; 3 6 9] + + # Nesting + nested = VA[ + fill(1, 2, 3), + VA[3ones(3), zeros(3)], + ] + @test nested isa VectorOfArray + @test nested.u[1] == fill(1, 2, 3) + @test nested.u[2] isa VectorOfArray + end -@testset "AP[...] shorthand" begin - x = AP[1:5, 1:6] - @test x isa ArrayPartition - @test length(x) == 11 - @test x[1:5] == 1:5 - @test x[6:11] == 1:6 + @testset "AP[...] shorthand" begin + x = AP[1:5, 1:6] + @test x isa ArrayPartition + @test length(x) == 11 + @test x[1:5] == 1:5 + @test x[6:11] == 1:6 - @test length(AP[]) == 0 - @test isempty(AP[]) + @test length(AP[]) == 0 + @test isempty(AP[]) + end end diff --git a/lib/RecursiveArrayToolsShorthandConstructors/test/test_groups.toml b/lib/RecursiveArrayToolsShorthandConstructors/test/test_groups.toml new file mode 100644 index 00000000..dda1469c --- /dev/null +++ b/lib/RecursiveArrayToolsShorthandConstructors/test/test_groups.toml @@ -0,0 +1,2 @@ +[Core] +versions = ["lts", "1", "pre"] diff --git a/test/adjoints.jl b/test/Core/adjoints.jl similarity index 100% rename from test/adjoints.jl rename to test/Core/adjoints.jl diff --git a/test/basic_indexing.jl b/test/Core/basic_indexing.jl similarity index 100% rename from test/basic_indexing.jl rename to test/Core/basic_indexing.jl diff --git a/test/copy_static_array_test.jl b/test/Core/copy_static_array_test.jl similarity index 100% rename from test/copy_static_array_test.jl rename to test/Core/copy_static_array_test.jl diff --git a/test/interface_tests.jl b/test/Core/interface_tests.jl similarity index 100% rename from test/interface_tests.jl rename to test/Core/interface_tests.jl diff --git a/test/linalg.jl b/test/Core/linalg.jl similarity index 100% rename from test/linalg.jl rename to test/Core/linalg.jl diff --git a/test/measurements.jl b/test/Core/measurements.jl similarity index 100% rename from test/measurements.jl rename to test/Core/measurements.jl diff --git a/test/mooncake.jl b/test/Core/mooncake.jl similarity index 100% rename from test/mooncake.jl rename to test/Core/mooncake.jl diff --git a/test/named_array_partition_tests.jl b/test/Core/named_array_partition_tests.jl similarity index 100% rename from test/named_array_partition_tests.jl rename to test/Core/named_array_partition_tests.jl diff --git a/test/partitions_and_static_arrays.jl b/test/Core/partitions_and_static_arrays.jl similarity index 100% rename from test/partitions_and_static_arrays.jl rename to test/Core/partitions_and_static_arrays.jl diff --git a/test/partitions_test.jl b/test/Core/partitions_test.jl similarity index 100% rename from test/partitions_test.jl rename to test/Core/partitions_test.jl diff --git a/test/tabletraits.jl b/test/Core/tabletraits.jl similarity index 100% rename from test/tabletraits.jl rename to test/Core/tabletraits.jl diff --git a/test/testutils.jl b/test/Core/testutils.jl similarity index 100% rename from test/testutils.jl rename to test/Core/testutils.jl diff --git a/test/utils_test.jl b/test/Core/utils_test.jl similarity index 100% rename from test/utils_test.jl rename to test/Core/utils_test.jl diff --git a/test/downstream/Project.toml b/test/Downstream/Project.toml similarity index 79% rename from test/downstream/Project.toml rename to test/Downstream/Project.toml index ad7277e8..81edbff6 100644 --- a/test/downstream/Project.toml +++ b/test/Downstream/Project.toml @@ -10,10 +10,15 @@ RecursiveArrayToolsShorthandConstructors = "39fb7555-b4ad-4efd-8abe-30331df017d3 StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5" Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" +[sources] +RecursiveArrayTools = {path = "../.."} +RecursiveArrayToolsShorthandConstructors = {path = "../../lib/RecursiveArrayToolsShorthandConstructors"} + [compat] ArrayInterface = "7" ModelingToolkit = "8.33, 9, 10, 11" @@ -22,8 +27,11 @@ NLsolve = "4" OrdinaryDiffEq = "6.31, 7" OrdinaryDiffEqRosenbrock = "1, 2" StaticArrays = "1" +RecursiveArrayTools = "4" +RecursiveArrayToolsShorthandConstructors = "1" SymbolicIndexingInterface = "0.3" Tables = "1" +Test = "1" Tracker = "0.2" Unitful = "1.17" Zygote = "0.6, 0.7" diff --git a/test/downstream/TrackerExt.jl b/test/Downstream/TrackerExt.jl similarity index 100% rename from test/downstream/TrackerExt.jl rename to test/Downstream/TrackerExt.jl diff --git a/test/downstream/adjoints.jl b/test/Downstream/adjoints.jl similarity index 100% rename from test/downstream/adjoints.jl rename to test/Downstream/adjoints.jl diff --git a/test/downstream/downstream_events.jl b/test/Downstream/downstream_events.jl similarity index 100% rename from test/downstream/downstream_events.jl rename to test/Downstream/downstream_events.jl diff --git a/test/downstream/measurements_and_units.jl b/test/Downstream/measurements_and_units.jl similarity index 100% rename from test/downstream/measurements_and_units.jl rename to test/Downstream/measurements_and_units.jl diff --git a/test/downstream/odesolve.jl b/test/Downstream/odesolve.jl similarity index 100% rename from test/downstream/odesolve.jl rename to test/Downstream/odesolve.jl diff --git a/test/downstream/symbol_indexing.jl b/test/Downstream/symbol_indexing.jl similarity index 98% rename from test/downstream/symbol_indexing.jl rename to test/Downstream/symbol_indexing.jl index 3cbae750..759ba53d 100644 --- a/test/downstream/symbol_indexing.jl +++ b/test/Downstream/symbol_indexing.jl @@ -2,7 +2,7 @@ using RecursiveArrayTools, ModelingToolkit, OrdinaryDiffEq, SymbolicIndexingInte using Zygote using ModelingToolkit: t_nounits as t, D_nounits as D -include("../testutils.jl") +include("../Core/testutils.jl") @variables x(t) @parameters τ diff --git a/test/GPU/Project.toml b/test/GPU/Project.toml new file mode 100644 index 00000000..f52d5575 --- /dev/null +++ b/test/GPU/Project.toml @@ -0,0 +1,23 @@ +[deps] +Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" +ArrayInterface = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9" +CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" +KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" +RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" +RecursiveArrayToolsArrayPartitionAnyAll = "172d604e-c495-4f00-97bf-d70957099afa" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" + +[sources] +RecursiveArrayTools = {path = "../.."} +RecursiveArrayToolsArrayPartitionAnyAll = {path = "../../lib/RecursiveArrayToolsArrayPartitionAnyAll"} + +[compat] +Adapt = "4" +ArrayInterface = "7.17.0" +CUDA = "3.12, 4, 5" +KernelAbstractions = "0.9.36" +RecursiveArrayTools = "4" +RecursiveArrayToolsArrayPartitionAnyAll = "1" +Test = "1" +Zygote = "0.7" diff --git a/test/gpu/arraypartition_gpu.jl b/test/GPU/arraypartition_gpu.jl similarity index 100% rename from test/gpu/arraypartition_gpu.jl rename to test/GPU/arraypartition_gpu.jl diff --git a/test/gpu/ode_gpu.jl b/test/GPU/ode_gpu.jl similarity index 100% rename from test/gpu/ode_gpu.jl rename to test/GPU/ode_gpu.jl diff --git a/test/gpu/vectorofarray_gpu.jl b/test/GPU/vectorofarray_gpu.jl similarity index 100% rename from test/gpu/vectorofarray_gpu.jl rename to test/GPU/vectorofarray_gpu.jl diff --git a/test/NoPre/Project.toml b/test/NoPre/Project.toml new file mode 100644 index 00000000..f289409f --- /dev/null +++ b/test/NoPre/Project.toml @@ -0,0 +1,12 @@ +[deps] +JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b" +RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[sources] +RecursiveArrayTools = {path = "../.."} + +[compat] +JET = "0.9, 0.10, 0.11" +RecursiveArrayTools = "4" +Test = "1" diff --git a/test/jet_tests.jl b/test/NoPre/jet_tests.jl similarity index 100% rename from test/jet_tests.jl rename to test/NoPre/jet_tests.jl diff --git a/test/QA/Project.toml b/test/QA/Project.toml new file mode 100644 index 00000000..4c049614 --- /dev/null +++ b/test/QA/Project.toml @@ -0,0 +1,15 @@ +[deps] +Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" +Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[sources] +RecursiveArrayTools = {path = "../.."} + +[compat] +Aqua = "0.8" +Pkg = "1" +RecursiveArrayTools = "4" +Test = "1" +julia = "1.10" diff --git a/test/qa.jl b/test/QA/qa.jl similarity index 100% rename from test/qa.jl rename to test/QA/qa.jl diff --git a/test/symbolic_indexing_interface_test.jl b/test/SymbolicIndexingInterface/symbolic_indexing_interface_test.jl similarity index 100% rename from test/symbolic_indexing_interface_test.jl rename to test/SymbolicIndexingInterface/symbolic_indexing_interface_test.jl diff --git a/test/gpu/Project.toml b/test/gpu/Project.toml deleted file mode 100644 index 79c3e5ea..00000000 --- a/test/gpu/Project.toml +++ /dev/null @@ -1,8 +0,0 @@ -[deps] -CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" -KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" -RecursiveArrayToolsArrayPartitionAnyAll = "172d604e-c495-4f00-97bf-d70957099afa" - -[compat] -CUDA = "3.12, 4, 5" -KernelAbstractions = "0.9" diff --git a/test/nopre/Project.toml b/test/nopre/Project.toml deleted file mode 100644 index 0cf8f6e9..00000000 --- a/test/nopre/Project.toml +++ /dev/null @@ -1,6 +0,0 @@ -[deps] -JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b" -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[compat] -JET = "0.9, 0.10, 0.11" diff --git a/test/runtests.jl b/test/runtests.jl index b87c9aa6..94de91c1 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,10 +1,4 @@ using Pkg -# Install the ShorthandConstructors subpackage for tests that need VA[...]/AP[...] syntax -Pkg.develop( - PackageSpec( - path = joinpath(dirname(@__DIR__), "lib", "RecursiveArrayToolsShorthandConstructors") - ) -) using RecursiveArrayTools using Test using SafeTestsets @@ -12,13 +6,13 @@ using SafeTestsets const GROUP = get(ENV, "GROUP", "All") function activate_downstream_env() - Pkg.activate("downstream") + Pkg.activate(joinpath(@__DIR__, "Downstream")) Pkg.develop(PackageSpec(path = dirname(@__DIR__))) return Pkg.instantiate() end function activate_gpu_env() - Pkg.activate("gpu") + Pkg.activate(joinpath(@__DIR__, "GPU")) Pkg.develop(PackageSpec(path = dirname(@__DIR__))) Pkg.develop( PackageSpec( @@ -29,77 +23,141 @@ function activate_gpu_env() end function activate_nopre_env() - Pkg.activate("nopre") + Pkg.activate(joinpath(@__DIR__, "NoPre")) Pkg.develop(PackageSpec(path = dirname(@__DIR__))) return Pkg.instantiate() end -@time begin - if GROUP == "Core" || GROUP == "All" - @time @safetestset "Quality Assurance" include("qa.jl") - @time @safetestset "Utils Tests" include("utils_test.jl") - @time @safetestset "NamedArrayPartition Tests" include("named_array_partition_tests.jl") - @time @safetestset "Partitions Tests" include("partitions_test.jl") - @time @safetestset "VecOfArr Indexing Tests" include("basic_indexing.jl") - @time @safetestset "VecOfArr Interface Tests" include("interface_tests.jl") - @time @safetestset "Table traits" include("tabletraits.jl") - @time @safetestset "StaticArrays Tests" include("copy_static_array_test.jl") - @time @safetestset "Linear Algebra Tests" include("linalg.jl") - @time @safetestset "Adjoint Tests" include("adjoints.jl") - @time @safetestset "Mooncake Tests" include("mooncake.jl") - @time @safetestset "Measurement Tests" include("measurements.jl") +function activate_qa_env() + Pkg.activate(joinpath(@__DIR__, "QA")) + # On Julia < 1.11, the [sources] section in the QA Project.toml is not + # honored, so Pkg.develop the umbrella root path explicitly. + if VERSION < v"1.11.0-DEV.0" + Pkg.develop(PackageSpec(path = dirname(@__DIR__))) end + return Pkg.instantiate() +end - if GROUP == "SymbolicIndexingInterface" || GROUP == "All" - @time @safetestset "SymbolicIndexingInterface API test" include("symbolic_indexing_interface_test.jl") - end +@time begin + # Detect sublibrary test groups. + # GROUP can be a bare sublibrary name (Core test group) or + # "{sublibrary}_{TEST_GROUP}" for any custom group (e.g., QA, GPU, etc.). + # Sublibraries declare their groups in test/test_groups.toml. + lib_dir = joinpath(dirname(@__DIR__), "lib") - if GROUP == "RaggedArrays" - Pkg.activate(joinpath(dirname(@__DIR__), "lib", "RecursiveArrayToolsRaggedArrays")) - Pkg.develop(PackageSpec(path = dirname(@__DIR__))) - Pkg.instantiate() - Pkg.test() + # Check if GROUP matches a sublibrary, possibly with a _SUFFIX for the test group. + # Scan underscores right-to-left to find the longest matching sublibrary prefix. + function _detect_sublibrary_group(group, lib_dir) + isdir(joinpath(lib_dir, group)) && return (group, "Core") + for i in length(group):-1:1 + if group[i] == '_' && isdir(joinpath(lib_dir, group[1:(i - 1)])) + return (group[1:(i - 1)], group[(i + 1):end]) + end + end + return (group, "Core") end + base_group, test_group = _detect_sublibrary_group(GROUP, lib_dir) - if GROUP == "ArrayPartitionAnyAll" - Pkg.activate(joinpath(dirname(@__DIR__), "lib", "RecursiveArrayToolsArrayPartitionAnyAll")) - Pkg.develop(PackageSpec(path = dirname(@__DIR__))) - Pkg.instantiate() - Pkg.test() - end + if isdir(joinpath(lib_dir, base_group)) + Pkg.activate(joinpath(lib_dir, base_group)) + # On Julia < 1.11, the [sources] section in Project.toml is not supported. + # Manually Pkg.develop local path dependencies so CI tests the PR branch code. + # The sublibraries depend on the umbrella root via [sources] (../..); develop + # those local paths, skipping the active project itself. + if VERSION < v"1.11.0-DEV.0" + developed = Set{String}() + push!(developed, normpath(joinpath(lib_dir, base_group))) + specs = Pkg.PackageSpec[] + queue = [joinpath(lib_dir, base_group)] + while !isempty(queue) + pkg_dir = popfirst!(queue) + toml_path = joinpath(pkg_dir, "Project.toml") + isfile(toml_path) || continue + toml = Pkg.TOML.parsefile(toml_path) + if haskey(toml, "sources") + for (dep_name, source_spec) in toml["sources"] + if source_spec isa Dict && haskey(source_spec, "path") + dep_path = normpath(joinpath(pkg_dir, source_spec["path"])) + if isdir(dep_path) && !(dep_path in developed) + push!(developed, dep_path) + push!(specs, Pkg.PackageSpec(path = dep_path)) + push!(queue, dep_path) + end + end + end + end + end + isempty(specs) || Pkg.develop(specs) + end + withenv("RECURSIVEARRAYTOOLS_TEST_GROUP" => test_group) do + Pkg.test(base_group, julia_args = ["--check-bounds=auto"], force_latest_compatible_version = false, allow_reresolve = true) + end + else + # Root package's own test groups. + # Captured so that, in a local "All" run, the QA group's isolated env does + # not leak into the subsequent functional Core tests run in the main env. + main_test_project = Base.active_project() - if GROUP == "ShorthandConstructors" - Pkg.activate(joinpath(dirname(@__DIR__), "lib", "RecursiveArrayToolsShorthandConstructors")) - Pkg.develop(PackageSpec(path = dirname(@__DIR__))) - Pkg.instantiate() - Pkg.test() - end + if GROUP == "QA" + # QA (Aqua) runs in its own dep-adding environment (test/QA/Project.toml) + # so the QA tooling is not part of the main test dependency set. + activate_qa_env() + @time @safetestset "Quality Assurance" include("QA/qa.jl") + Pkg.activate(main_test_project) + end - if GROUP == "Downstream" - activate_downstream_env() - @time @safetestset "ODE Solve Tests" include("downstream/odesolve.jl") - @time @safetestset "Event Tests with ArrayPartition" include("downstream/downstream_events.jl") - @time @safetestset "Measurements and Units" include("downstream/measurements_and_units.jl") - @time @safetestset "TrackerExt" include("downstream/TrackerExt.jl") - # TODO: re-enable after SciMLBase compat bump for RAT v4 (SciML/SciMLBase.jl#1297) - # @time @safetestset "Downstream Adjoint Tests" include("downstream/adjoints.jl") - end + if GROUP == "Core" || GROUP == "All" + # The root Core tests use the VA[...]/AP[...] shorthand syntax, which + # lives in the RecursiveArrayToolsShorthandConstructors sublibrary. + Pkg.develop( + PackageSpec( + path = joinpath(dirname(@__DIR__), "lib", "RecursiveArrayToolsShorthandConstructors") + ) + ) + @time @safetestset "Utils Tests" include("Core/utils_test.jl") + @time @safetestset "NamedArrayPartition Tests" include("Core/named_array_partition_tests.jl") + @time @safetestset "Partitions Tests" include("Core/partitions_test.jl") + @time @safetestset "Partitions and StaticArrays Tests" include("Core/partitions_and_static_arrays.jl") + @time @safetestset "VecOfArr Indexing Tests" include("Core/basic_indexing.jl") + @time @safetestset "VecOfArr Interface Tests" include("Core/interface_tests.jl") + @time @safetestset "Table traits" include("Core/tabletraits.jl") + @time @safetestset "StaticArrays Tests" include("Core/copy_static_array_test.jl") + @time @safetestset "Linear Algebra Tests" include("Core/linalg.jl") + @time @safetestset "Adjoint Tests" include("Core/adjoints.jl") + @time @safetestset "Mooncake Tests" include("Core/mooncake.jl") + @time @safetestset "Measurement Tests" include("Core/measurements.jl") + end + + if GROUP == "SymbolicIndexingInterface" || GROUP == "All" + @time @safetestset "SymbolicIndexingInterface API test" include("SymbolicIndexingInterface/symbolic_indexing_interface_test.jl") + end - if GROUP == "SymbolicIndexingInterface" || GROUP == "Downstream" - if GROUP == "SymbolicIndexingInterface" + if GROUP == "Downstream" activate_downstream_env() + @time @safetestset "ODE Solve Tests" include("Downstream/odesolve.jl") + @time @safetestset "Event Tests with ArrayPartition" include("Downstream/downstream_events.jl") + @time @safetestset "Measurements and Units" include("Downstream/measurements_and_units.jl") + @time @safetestset "TrackerExt" include("Downstream/TrackerExt.jl") + # TODO: re-enable after SciMLBase compat bump for RAT v4 (SciML/SciMLBase.jl#1297) + # @time @safetestset "Downstream Adjoint Tests" include("Downstream/adjoints.jl") end - @time @safetestset "DiffEqArray Indexing Tests" include("downstream/symbol_indexing.jl") - end - if GROUP == "GPU" - activate_gpu_env() - @time @safetestset "VectorOfArray GPU" include("gpu/vectorofarray_gpu.jl") - @time @safetestset "ArrayPartition GPU" include("gpu/arraypartition_gpu.jl") - end + if GROUP == "SymbolicIndexingInterface" || GROUP == "Downstream" + if GROUP == "SymbolicIndexingInterface" + activate_downstream_env() + end + @time @safetestset "DiffEqArray Indexing Tests" include("Downstream/symbol_indexing.jl") + end - if GROUP == "nopre" - activate_nopre_env() - @time @safetestset "JET Tests" include("jet_tests.jl") + if GROUP == "GPU" + activate_gpu_env() + @time @safetestset "VectorOfArray GPU" include("GPU/vectorofarray_gpu.jl") + @time @safetestset "ArrayPartition GPU" include("GPU/arraypartition_gpu.jl") + end + + if GROUP == "NoPre" + activate_nopre_env() + @time @safetestset "JET Tests" include("NoPre/jet_tests.jl") + end end end