Skip to content

Commit a32b5aa

Browse files
Merge pull request #434 from wupeifan/arrays-of-arrays-heterogeneity
Fix issues on heterogeneous element types inside an array
2 parents d5abfa3 + 6965ba5 commit a32b5aa

File tree

2 files changed

+54
-59
lines changed

2 files changed

+54
-59
lines changed

src/build_function.jl

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,21 @@ function _build_function(target::JuliaTarget, op::Operation, args...;
131131
end
132132
end
133133

134+
# Detect heterogeneous element types of "arrays of matrices/sparce matrices"
135+
function is_array_matrix(F)
136+
return isa(F, AbstractVector) && all(x->isa(x, AbstractArray), F)
137+
end
138+
function is_array_sparse_matrix(F)
139+
return isa(F, AbstractVector) && all(x->isa(x, AbstractSparseMatrix), F)
140+
end
141+
# Detect heterogeneous element types of "arrays of arrays of matrices/sparce matrices"
142+
function is_array_array_matrix(F)
143+
return isa(F, AbstractVector) && all(x->isa(x, AbstractArray{<:AbstractMatrix}), F)
144+
end
145+
function is_array_array_sparse_matrix(F)
146+
return isa(F, AbstractVector) && all(x->isa(x, AbstractArray{<:AbstractSparseMatrix}), F)
147+
end
148+
134149
function _build_function(target::JuliaTarget, rhss, args...;
135150
conv = simplified_expr, expression = Val{true},
136151
checkbounds = false, constructor=nothing,
@@ -171,13 +186,13 @@ function _build_function(target::JuliaTarget, rhss, args...;
171186
_rhss = rhss
172187
end
173188

174-
if eltype(eltype(rhss)) <: SparseMatrixCSC # Array of arrays of sparse matrices
189+
if is_array_array_sparse_matrix(rhss) # Array of arrays of sparse matrices
175190
ip_sys_exprs = reduce(vcat,[vec(reduce(vcat,[vec([:($X[$i][$j].nzval[$k] = $(conv(rhs))) for (k, rhs) enumerate(rhsel2.nzval)]) for (j, rhsel2) enumerate(rhsel)], init=Expr[])) for (i,rhsel) enumerate(_rhss)],init=Expr[])
176-
elseif eltype(eltype(rhss)) <: AbstractArray # Array of arrays of arrays
191+
elseif is_array_array_matrix(rhss) # Array of arrays of arrays
177192
ip_sys_exprs = reduce(vcat,[vec(reduce(vcat,[vec([:($X[$i][$j][$k] = $(conv(rhs))) for (k, rhs) enumerate(rhsel2)]) for (j, rhsel2) enumerate(rhsel)], init=Expr[])) for (i,rhsel) enumerate(_rhss)], init=Expr[])
178-
elseif eltype(rhss) <: SparseMatrixCSC # Array of sparse matrices
193+
elseif is_array_sparse_matrix(rhss) # Array of sparse matrices
179194
ip_sys_exprs = reduce(vcat,[vec([:($X[$i].nzval[$j] = $(conv(rhs))) for (j, rhs) enumerate(rhsel.nzval)]) for (i,rhsel) enumerate(_rhss)], init=Expr[])
180-
elseif eltype(rhss) <: AbstractArray # Array of arrays
195+
elseif is_array_matrix(rhss) # Array of arrays
181196
ip_sys_exprs = reduce(vcat,[vec([:($X[$i][$j] = $(conv(rhs))) for (j, rhs) enumerate(rhsel)]) for (i,rhsel) enumerate(_rhss)], init=Expr[])
182197
elseif rhss isa SparseMatrixCSC
183198
ip_sys_exprs = [:($X.nzval[$i] = $(conv(rhs))) for (i, rhs) enumerate(_rhss)]

test/build_function_arrayofarray.jl

Lines changed: 35 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@ input = [1, 2, 3]
1111
# ===== Dense tests =====
1212
# Arrays of Matrices
1313
h_dense_arraymat = [[a 1; b 0], [0 0; 0 0], [a c; 1 0]] # empty array support required
14-
function h_dense_arraymat_julia(x)
15-
a, b, c = x
16-
return [[a[1] 1; b[1] 0], [0 0; 0 0], [a[1] c[1]; 1 0]]
17-
end
1814
function h_dense_arraymat_julia!(out, x)
1915
a, b, c = x
2016
out[1] .= [a[1] 1; b[1] 0]
@@ -23,23 +19,32 @@ function h_dense_arraymat_julia!(out, x)
2319
end
2420

2521
h_dense_arraymat_str = ModelingToolkit.build_function(h_dense_arraymat, [a, b, c])
26-
h_dense_arraymat_oop = eval(h_dense_arraymat_str[1])
2722
h_dense_arraymat_ip! = eval(h_dense_arraymat_str[2])
2823
out_1_arraymat = [Array{Int64}(undef, 2, 2) for i in 1:3]
2924
out_2_arraymat = [similar(x) for x in out_1_arraymat]
30-
julia_dense_arraymat = h_dense_arraymat_julia(input)
31-
mtk_dense_arraymat = h_dense_arraymat_oop(input)
32-
@test_broken julia_dense_arraymat == mtk_dense_arraymat
3325
h_dense_arraymat_julia!(out_1_arraymat, input)
3426
h_dense_arraymat_ip!(out_2_arraymat, input)
3527
@test out_1_arraymat == out_2_arraymat
3628

37-
# Arrays of 1D Vectors
38-
h_dense_arrayvec = [[a, 0, c], [0, 0, 0], [1, a, b]] # same for empty vectors, etc.
39-
function h_dense_arrayvec_julia(x)
29+
# Arrays of Matrices, Heterogeneous element types
30+
test_exp = [exp(a) * exp(b), a]
31+
h_dense_arraymat_het = [ModelingToolkit.hessian(t, [a, b]) for t in test_exp]
32+
function h_dense_arraymat_het_julia!(out, x)
4033
a, b, c = x
41-
return [[a[1], 0, c[1]], [0, 0, 0], [1, a[1], b[1]]]
34+
out[1] .= [exp(a[1]) * exp(b[1]) exp(a[1]) * exp(b[1]); exp(a[1]) * exp(b[1]) exp(a[1]) * exp(b[1])]
35+
out[2] .= [0 0; 0 0]
4236
end
37+
38+
h_dense_arraymat_het_str = ModelingToolkit.build_function(h_dense_arraymat_het, [a, b, c])
39+
h_dense_arraymat_het_ip! = eval(h_dense_arraymat_het_str[2])
40+
out_1_arraymat_het = [Array{Float64}(undef, 2, 2) for i in 1:2]
41+
out_2_arraymat_het = [similar(x) for x in out_1_arraymat_het]
42+
h_dense_arraymat_het_julia!(out_1_arraymat_het, input)
43+
h_dense_arraymat_het_ip!(out_2_arraymat_het, input)
44+
@test out_1_arraymat_het == out_2_arraymat_het
45+
46+
# Arrays of 1D Vectors
47+
h_dense_arrayvec = [[a, 0, c], [0, 0, 0], [1, a, b]] # same for empty vectors, etc.
4348
function h_dense_arrayvec_julia!(out, x)
4449
a, b, c = x
4550
out[1] .= [a[1], 0, c[1]]
@@ -48,24 +53,15 @@ function h_dense_arrayvec_julia!(out, x)
4853
end
4954

5055
h_dense_arrayvec_str = ModelingToolkit.build_function(h_dense_arrayvec, [a, b, c])
51-
h_dense_arrayvec_oop = eval(h_dense_arrayvec_str[1])
5256
h_dense_arrayvec_ip! = eval(h_dense_arrayvec_str[2])
5357
out_1_arrayvec = [Vector{Int64}(undef, 3) for i in 1:3]
5458
out_2_arrayvec = [Vector{Int64}(undef, 3) for i in 1:3]
55-
julia_dense_arrayvec = h_dense_arrayvec_julia(input)
56-
mtk_dense_arrayvec = h_dense_arrayvec_oop(input)
57-
@test_broken julia_dense_arrayvec == mtk_dense_arrayvec
58-
mtk_dense_arrayvec = @test_broken h_dense_arrayvec_oop(input)
5959
h_dense_arrayvec_julia!(out_1_arrayvec, input)
6060
h_dense_arrayvec_ip!(out_2_arrayvec, input)
6161
@test out_1_arrayvec == out_2_arrayvec
6262

6363
# Arrays of Arrays of Matrices
6464
h_dense_arrayNestedMat = [[[a 1; b 0], [0 0; 0 0]], [[b 1; a 0], [b c; 0 1]]]
65-
function h_dense_arrayNestedMat_julia(x)
66-
a, b, c = x
67-
return [[[a[1] 1; b[1] 0], [0 0; 0 0]], [[b[1] 1; a[1] 0], [b[1] c[1]; 0 1]]]
68-
end
6965
function h_dense_arrayNestedMat_julia!(out, x)
7066
a, b, c = x
7167
out[1][1] .= [a[1] 1; b[1] 0]
@@ -75,24 +71,34 @@ function h_dense_arrayNestedMat_julia!(out, x)
7571
end
7672

7773
h_dense_arrayNestedMat_str = ModelingToolkit.build_function(h_dense_arrayNestedMat, [a, b, c])
78-
h_dense_arrayNestedMat_oop = eval(h_dense_arrayNestedMat_str[1])
7974
h_dense_arrayNestedMat_ip! = eval(h_dense_arrayNestedMat_str[2])
8075
out_1_arrayNestedMat = [[rand(Int64, 2, 2), rand(Int64, 2, 2)], [rand(Int64, 2, 2), rand(Int64, 2, 2)]] # avoid undef broadcasting issue
8176
out_2_arrayNestedMat = [[rand(Int64, 2, 2), rand(Int64, 2, 2)], [rand(Int64, 2, 2), rand(Int64, 2, 2)]]
82-
julia_dense_arrayNestedMat = h_dense_arrayNestedMat_julia(input)
83-
mtk_dense_arrayNestedMat = h_dense_arrayNestedMat_oop(input)
84-
@test_broken julia_dense_arrayNestedMat == mtk_dense_arrayNestedMat
8577
h_dense_arrayNestedMat_julia!(out_1_arrayNestedMat, input)
8678
h_dense_arrayNestedMat_ip!(out_2_arrayNestedMat, input)
8779
@test out_1_arrayNestedMat == out_2_arrayNestedMat
8880

81+
# Arrays of Arrays of Matrices, Heterogeneous element types
82+
test_exp = [exp(a) * exp(b), a]
83+
h_dense_arrayNestedMat_het = [[ModelingToolkit.hessian(t, [a, b]) for t in test_exp], [[ModelingToolkit.Constant(0) ModelingToolkit.Constant(0); ModelingToolkit.Constant(0) ModelingToolkit.Constant(0)], [ModelingToolkit.Constant(0) ModelingToolkit.Constant(0); ModelingToolkit.Constant(0) ModelingToolkit.Constant(0)]]]
84+
function h_dense_arrayNestedMat_het_julia!(out, x)
85+
a, b, c = x
86+
out[1][1] .= [exp(a[1]) * exp(b[1]) exp(a[1]) * exp(b[1]); exp(a[1]) * exp(b[1]) exp(a[1]) * exp(b[1])]
87+
out[1][2] .= [0 0; 0 0]
88+
out[2][1] .= [0 0; 0 0]
89+
out[2][2] .= [0 0; 0 0]
90+
end
91+
h_dense_arrayNestedMat_het_str = ModelingToolkit.build_function(h_dense_arrayNestedMat_het, [a, b, c])
92+
h_dense_arrayNestedMat_het_ip! = eval(h_dense_arrayNestedMat_het_str[2])
93+
out_1_arrayNestedMat_het = [[rand(Int64, 2, 2), rand(Int64, 2, 2)], [rand(Int64, 2, 2), rand(Int64, 2, 2)]] # avoid undef broadcasting issue
94+
out_2_arrayNestedMat_het = [[rand(Int64, 2, 2), rand(Int64, 2, 2)], [rand(Int64, 2, 2), rand(Int64, 2, 2)]]
95+
h_dense_arrayNestedMat_julia!(out_1_arrayNestedMat_het, input)
96+
h_dense_arrayNestedMat_ip!(out_2_arrayNestedMat_het, input)
97+
@test out_1_arrayNestedMat_het == out_2_arrayNestedMat_het
98+
8999
# ===== Sparse tests =====
90100
# Array of Matrices
91101
h_sparse_arraymat = sparse.([[a 1; b 0], [0 0; 0 0], [a c; 1 0]])
92-
function h_sparse_arraymat_julia(x)
93-
a, b, c = x
94-
return [sparse([a[1] 1; b[1] 0]), sparse([0 0; 0 0]), sparse([a[1] c[1]; 1 0])] # necessary because sparse([]) is a SparseVector, not SparseMatrix
95-
end
96102
function h_sparse_arraymat_julia!(out, x)
97103
a, b, c = x
98104
out[1][1, 1] = a[1]
@@ -105,24 +111,16 @@ function h_sparse_arraymat_julia!(out, x)
105111
end
106112

107113
h_sparse_arraymat_str = ModelingToolkit.build_function(h_sparse_arraymat, [a, b, c])
108-
h_sparse_arraymat_oop = eval(h_sparse_arraymat_str[1])
109114
h_sparse_arraymat_ip! = eval(h_sparse_arraymat_str[2])
110115
h_sparse_arraymat_sparsity_patterns = map(get_sparsity_pattern, h_sparse_arraymat)
111116
out_1_arraymat = [similar(h) for h in h_sparse_arraymat_sparsity_patterns]
112117
out_2_arraymat = [similar(h) for h in h_sparse_arraymat_sparsity_patterns] # can't do similar() because it will just be #undef, with the wrong sparsity pattern
113-
julia_sparse_arraymat = h_sparse_arraymat_julia(input)
114-
mtk_sparse_arraymat = h_sparse_arraymat_oop(input)
115-
@test_broken julia_sparse_arraymat == mtk_sparse_arraymat
116118
h_sparse_arraymat_julia!(out_1_arraymat, input)
117119
h_sparse_arraymat_ip!(out_2_arraymat, input)
118120
@test out_1_arraymat == out_2_arraymat
119121

120122
# Array of 1D Vectors
121123
h_sparse_arrayvec = sparse.([[a, 0, c], [0, 0, 0], [1, a, b]])
122-
function h_sparse_arrayvec_julia(x)
123-
a, b, c = x
124-
return sparse.([[a[1], 0, c[1]], [0, 0, 0], [1, a[1], b[1]]])
125-
end
126124
function h_sparse_arrayvec_julia!(out, x)
127125
a, b, c = x
128126
out[1][1] = a[1]
@@ -134,24 +132,16 @@ function h_sparse_arrayvec_julia!(out, x)
134132
end
135133

136134
h_sparse_arrayvec_str = ModelingToolkit.build_function(h_sparse_arrayvec, [a, b, c])
137-
h_sparse_arrayvec_oop = eval(h_sparse_arrayvec_str[1])
138135
h_sparse_arrayvec_ip! = eval(h_sparse_arrayvec_str[2])
139136
h_sparse_arrayvec_sparsity_patterns = map(get_sparsity_pattern, h_sparse_arrayvec)
140137
out_1_arrayvec = [similar(h) for h in h_sparse_arrayvec_sparsity_patterns]
141138
out_2_arrayvec = [similar(h) for h in h_sparse_arrayvec_sparsity_patterns]
142-
julia_sparse_arrayvec = h_sparse_arrayvec_julia(input)
143-
mtk_sparse_arrayvec = h_sparse_arrayvec_oop(input)
144-
@test_broken julia_sparse_arrayvec == mtk_sparse_arrayvec
145139
h_sparse_arrayvec_julia!(out_1_arrayvec, input)
146140
h_sparse_arrayvec_ip!(out_2_arrayvec, input)
147141
@test out_1_arrayvec == out_2_arrayvec
148142

149143
# Arrays of Arrays of Matrices
150144
h_sparse_arrayNestedMat = [sparse.([[a 1; b 0], [0 0; 0 0]]), sparse.([[b 1; a 0], [b c; 0 1]])]
151-
function h_sparse_arrayNestedMat_julia(x)
152-
a, b, c = x
153-
return [sparse.([[a[1] 1; b[1] 0], [0 0; 0 0]]), sparse.([[b[1] 1; a[1] 0], [b[1] c[1]; 0 1]])]
154-
end
155145
function h_sparse_arrayNestedMat_julia!(out, x)
156146
a, b, c = x
157147
out[1][1][1, 1] = a[1]
@@ -167,14 +157,10 @@ function h_sparse_arrayNestedMat_julia!(out, x)
167157
end
168158

169159
h_sparse_arrayNestedMat_str = ModelingToolkit.build_function(h_sparse_arrayNestedMat, [a, b, c])
170-
h_sparse_arrayNestedMat_oop = eval(h_sparse_arrayNestedMat_str[1])
171160
h_sparse_arrayNestedMat_ip! = eval(h_sparse_arrayNestedMat_str[2])
172161
h_sparse_arrayNestedMat_sparsity_patterns = [map(get_sparsity_pattern, h) for h in h_sparse_arrayNestedMat]
173162
out_1_arrayNestedMat = [[similar(h_sub) for h_sub in h] for h in h_sparse_arrayNestedMat_sparsity_patterns]
174163
out_2_arrayNestedMat = [[similar(h_sub) for h_sub in h] for h in h_sparse_arrayNestedMat_sparsity_patterns]
175-
julia_sparse_arrayNestedMat = h_sparse_arrayNestedMat_julia(input)
176-
mtk_sparse_arrayNestedMat = h_sparse_arrayNestedMat_oop(input)
177-
@test_broken julia_sparse_arrayNestedMat == mtk_sparse_arrayNestedMat
178164
h_sparse_arrayNestedMat_julia!(out_1_arrayNestedMat, input)
179165
h_sparse_arrayNestedMat_ip!(out_2_arrayNestedMat, input)
180166
@test out_1_arrayNestedMat == out_2_arrayNestedMat
@@ -184,26 +170,20 @@ h_sparse_arrayNestedMat_ip!(out_2_arrayNestedMat, input)
184170
# Arrays of Matrices
185171
h_empty = [[a b; c 0], Array{Expression,2}(undef, 0,0)]
186172
h_empty_str = ModelingToolkit.build_function(h_empty, [a, b, c])
187-
h_empty_oop = eval(h_empty_str[1])
188173
h_empty_ip! = eval(h_empty_str[2])
189-
@test_broken h_empty_oop(input) == [[1 2; 3 0], Array{Int64,2}(undef,0,0)]
190174
out = [Matrix{Int64}(undef, 2, 2), Matrix{Int64}(undef, 0, 0)]
191175
h_empty_ip!(out, input) # should just not fail
192176

193177
# Array of Vectors
194178
h_empty_vec = [[a, b, c, 0], Vector{Expression}(undef,0)]
195179
h_empty_vec_str = ModelingToolkit.build_function(h_empty_vec, [a, b, c])
196-
h_empty_vec_oop = eval(h_empty_vec_str[1])
197180
h_empty_vec_ip! = eval(h_empty_vec_str[2])
198-
@test_broken h_empty_vec_oop(input) == [[1, 2, 3, 0], Vector{Int64}(undef,0)]
199181
out = [Vector{Int64}(undef, 4), Vector{Int64}(undef, 0)]
200182
h_empty_vec_ip!(out, input) # should just not fail
201183

202184
# Arrays of Arrays of Matrices
203185
h_emptyNested = [[[a b; c 0]], Array{Array{Expression, 2}}(undef, 0)] # emptyNested array of arrays
204186
h_emptyNested_str = ModelingToolkit.build_function(h_emptyNested, [a, b, c])
205-
h_emptyNested_oop = eval(h_emptyNested_str[1])
206187
h_emptyNested_ip! = eval(h_emptyNested_str[2])
207-
@test_broken h_emptyNested_oop(input) == [[[1 2; 3 0]], Array{Array{Int64, 2}}(undef, 0)]
208188
out = [[[1 2;3 4]], Array{Array{Int64,2},1}(undef, 0)]
209189
h_emptyNested_ip!(out, input) # should just not fail

0 commit comments

Comments
 (0)