Skip to content

Commit c7bb8fe

Browse files
Merge pull request #26 from rafaqz/loose_typed_nodes
More types for nodes
2 parents 8f2a994 + b14833b commit c7bb8fe

File tree

5 files changed

+117
-18
lines changed

5 files changed

+117
-18
lines changed

README.md

+9
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,15 @@ tissue2 = construct(Tissue, deepcopy([population2, population]))
7474
embryo = construct(Embryo, deepcopy([tissue1, tissue2])) # Make an embryo from Tissues
7575
```
7676

77+
Note that tuples can be used as well. This allows for type-stable indexing with
78+
heterogeneous nodes. For example:
79+
80+
```julia
81+
tissue1 = construct(Tissue, deepcopy((population, cell3)))
82+
```
83+
84+
(of course at the cost of mutability).
85+
7786
The head node then acts as the king. It is designed to have functionality which
7887
mimics a vector in order for usage in DifferentialEquations or Optim. So for example
7988

src/indexing.jl

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
nodeselect(ns, i, I...) = ns[i][I...]
2+
nodechild(ns, i, j) = ns[i].nodes[j]
3+
14
function bisect_search(a, i)
25
first(searchsorted(a,i))
36
end
@@ -7,7 +10,7 @@ Base.IndexStyle(::Type{<:AbstractMultiScaleArray}) = IndexLinear()
710
@inline function getindex(m::AbstractMultiScaleArray, i::Int)
811
idx = bisect_search(m.end_idxs, i)
912
idx > 1 && (i -= m.end_idxs[idx-1]) # also works with values
10-
(isempty(m.values) || idx < length(m.end_idxs)) ? m.nodes[idx][i] : m.values[i]
13+
(isempty(m.values) || idx < length(m.end_idxs)) ? nodeselect(m.nodes, idx, i) : m.values[i]
1114
end
1215

1316
@inline function setindex!(m::AbstractMultiScaleArray, nodes, i::Int)
@@ -25,7 +28,7 @@ end
2528

2629
@inline function getindex(m::AbstractMultiScaleArray, i, I...)
2730
if isempty(m.values) || i < length(m.end_idxs)
28-
length(I) == 1 ? m.nodes[i].nodes[I[1]] : m.nodes[i][I...]
31+
length(I) == 1 ? nodechild(m.nodes, i, I[1]) : nodeselect(m.nodes, i, I...)
2932
else
3033
m.values[I...]
3134
end

src/shape_construction.jl

+14-16
Original file line numberDiff line numberDiff line change
@@ -24,25 +24,11 @@ end
2424
end
2525

2626
recursive_similar(x,T) = [similar(y, T) for y in x]
27+
recursive_similar(x::Tuple,T) = tuple((similar(y, T) for y in x)...)
2728

2829
construct(::Type{T}, args...) where {T<:AbstractMultiScaleArrayLeaf} = T(args...)
2930

30-
function __construct(nodes::Vector{<:AbstractMultiScaleArray})
31-
end_idxs = Vector{Int}(length(nodes))
32-
off = 0
33-
@inbounds for i in 1:length(nodes)
34-
end_idxs[i] = (off += length(nodes[i]))
35-
end
36-
end_idxs
37-
end
38-
39-
function (construct(::Type{T}, nodes::Vector{<:AbstractMultiScaleArray},args...)
40-
where {T<:AbstractMultiScaleArray})
41-
T(nodes, eltype(T)[], __construct(nodes),args...)
42-
end
43-
44-
function (construct(::Type{T}, nodes::Vector{<:AbstractMultiScaleArray}, values, args...)
45-
where {T<:AbstractMultiScaleArray})
31+
function __construct(T, nodes, values, args...)
4632
vallen = length(values)
4733
end_idxs = Vector{Int}(length(nodes) + ifelse(vallen == 0, 0, 1))
4834
off = 0
@@ -53,6 +39,18 @@ function (construct(::Type{T}, nodes::Vector{<:AbstractMultiScaleArray}, values,
5339
T(nodes, values, end_idxs, args...)
5440
end
5541

42+
(construct(::Type{T}, nodes::AbstractVector{<:AbstractMultiScaleArray}, args...)
43+
where {T<:AbstractMultiScaleArray}) = __construct(T, nodes, eltype(T)[], args...)
44+
45+
(construct(::Type{T}, nodes::AbstractVector{<:AbstractMultiScaleArray}, values, args...)
46+
where {T<:AbstractMultiScaleArray}) = __construct(T, nodes, values, args...)
47+
48+
(construct(::Type{T}, nodes::Tuple{Vararg{<:AbstractMultiScaleArray}}, args...)
49+
where {T<:AbstractMultiScaleArray}) = __construct(T, nodes, eltype(T)[], args...)
50+
51+
(construct(::Type{T}, nodes::Tuple{Vararg{<:AbstractMultiScaleArray}}, values, args...)
52+
where {T<:AbstractMultiScaleArray}) = __construct(T, nodes, values, args...)
53+
5654
vcat(m1::AbstractMultiScaleArray, m2::AbstractMultiScaleArray) =
5755
error("AbstractMultiScaleArrays cannot be concatenated")
5856

test/runtests.jl

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using MultiScaleArrays, OrdinaryDiffEq, DiffEqBase, StochasticDiffEq
22
using Base.Test
33

4+
@time @testset "Tuple Nodes" begin include("tuple_nodes.jl") end
45
@time @testset "Bisect Search Tests" begin include("bisect_search_tests.jl") end
56
@time @testset "Indexing and Creation Tests" begin include("indexing_and_creation_tests.jl") end
67
@time @testset "Values Indexing" begin include("values_indexing.jl") end

test/tuple_nodes.jl

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
using MultiScaleArrays, OrdinaryDiffEq, DiffEqBase, StochasticDiffEq
2+
using Base.Test
3+
4+
struct PlantSettings{T} x::T end
5+
struct OrganParams{T} y::T end
6+
7+
struct Organ{B<:Number,P} <: AbstractMultiScaleArrayLeaf{B}
8+
values::Vector{B}
9+
name::Symbol
10+
params::P
11+
end
12+
13+
struct Plant{B,S,N<:Tuple{Vararg{<:Organ{<:Number}}}} <: AbstractMultiScaleArray{B}
14+
nodes::N
15+
values::Vector{B}
16+
end_idxs::Vector{Int}
17+
settings::S
18+
end
19+
20+
struct Community{B,N<:Tuple{Vararg{<:Plant{<:Number}}}} <: AbstractMultiScaleArray{B}
21+
nodes::N
22+
values::Vector{B}
23+
end_idxs::Vector{Int}
24+
end
25+
26+
mutable struct Scenario{B,N<:Tuple{Vararg{<:Community{<:Number}}}} <: AbstractMultiScaleArrayHead{B}
27+
nodes::N
28+
values::Vector{B}
29+
end_idxs::Vector{Int}
30+
end
31+
32+
organ1 = Organ([1.1,2.1,3.1], :Shoot, OrganParams(:grows_up))
33+
organ2 = Organ([4.1,5.1,6.1], :Root, OrganParams("grows down"))
34+
organ3 = Organ([1.2,2.2,3.2], :Shoot, OrganParams(true))
35+
organ4 = Organ([4.2,5.2,6.2], :Root, OrganParams(1//3))
36+
plant1 = construct(Plant, (deepcopy(organ1), deepcopy(organ2)), Float64[], PlantSettings(1))
37+
plant2 = construct(Plant, (deepcopy(organ3), deepcopy(organ4)), Float64[], PlantSettings(1.0))
38+
community = construct(Community, (deepcopy(plant1), deepcopy(plant2), ))
39+
scenario = construct(Scenario, (deepcopy(community),))
40+
41+
@inferred getindex(organ1, 1)
42+
@inferred getindex(plant1, 3)
43+
@inferred getindex(community, 4)
44+
@inferred getindex(scenario, 8)
45+
46+
@test scenario[1] == 1.1
47+
@test scenario[2] == 2.1
48+
@test scenario[3] == 3.1
49+
@test scenario[4] == 4.1
50+
@test scenario[5] == 5.1
51+
@test scenario[6] == 6.1
52+
@test scenario[7] == 1.2
53+
@test scenario[8] == 2.2
54+
@test scenario[9] == 3.2
55+
@test scenario[10] == 4.2
56+
@test scenario[11] == 5.2
57+
@test scenario[12] == 6.2
58+
59+
@test getindices(scenario, 1) == 1:12
60+
@test getindices(scenario, 1, 1) == 1:6
61+
@test getindices(scenario, 1, 2) == 7:12
62+
@test getindices(scenario, 1, 1, 1) == 1:3
63+
@test getindices(scenario, 1, 1, 2) == 4:6
64+
@test getindices(scenario, 1, 2, 1) == 7:9
65+
@test getindices(scenario, 1, 2, 2) == 10:12
66+
67+
organ_ode = function (dorgan,organ,p,t)
68+
m = mean(organ)
69+
for i in eachindex(organ)
70+
dorgan[i] = -m*organ[i]
71+
end
72+
end
73+
f = function (dscenario,scenario,p,t)
74+
for (organ, y, z) in LevelIterIdx(scenario, 2)
75+
organ_ode(@view(dscenario[y:z]),organ,p,t)
76+
end
77+
end
78+
affect! = function (integrator)
79+
add_node!(integrator, integrator.u[1, 1, 1], 1, 1)
80+
end
81+
82+
println("ODE with tuple nodes")
83+
84+
prob = ODEProblem(f, scenario, (0.0, 1.0))
85+
86+
sol = solve(prob, Tsit5())
87+
88+
@test length(sol[end]) == 12

0 commit comments

Comments
 (0)