|
| 1 | +using ModelingToolkit, Test, SparseArrays |
| 2 | +@variables a b c |
| 3 | + |
| 4 | +# Auxiliary Functions and Constants |
| 5 | +get_sparsity_pattern(h::Array{Expression}) = sparse(Int64.(map(~, h .=== ModelingToolkit.Constant(0)))) |
| 6 | +get_sparsity_pattern(h::SparseMatrixCSC{Expression,Int64}) = sparse(Int64.(map(~, h .=== ModelingToolkit.Constant(0)))) |
| 7 | +get_sparsity_pattern(h::SparseVector{Expression,Int64}) = sparse(Int64.(map(~, h .=== ModelingToolkit.Constant(0)))) |
| 8 | + |
| 9 | +input = [1, 2, 3] |
| 10 | + |
| 11 | +# ===== Dense tests ===== |
| 12 | +# Arrays of Matrices |
| 13 | +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 |
| 18 | +function h_dense_arraymat_julia!(out, x) |
| 19 | + a, b, c = x |
| 20 | + out[1] .= [a[1] 1; b[1] 0] |
| 21 | + out[2] .= [0 0; 0 0] |
| 22 | + out[3] .= [a[1] c[1]; 1 0] |
| 23 | +end |
| 24 | + |
| 25 | +h_dense_arraymat_str = ModelingToolkit.build_function(h_dense_arraymat, [a, b, c]) |
| 26 | +h_dense_arraymat_oop = eval(h_dense_arraymat_str[1]) |
| 27 | +h_dense_arraymat_ip! = eval(h_dense_arraymat_str[2]) |
| 28 | +out_1_arraymat = [Array{Int64}(undef, 2, 2) for i in 1:3] |
| 29 | +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 |
| 33 | +h_dense_arraymat_julia!(out_1_arraymat, input) |
| 34 | +h_dense_arraymat_ip!(out_2_arraymat, input) |
| 35 | +@test out_1_arraymat == out_2_arraymat |
| 36 | + |
| 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) |
| 40 | + a, b, c = x |
| 41 | + return [[a[1], 0, c[1]], [0, 0, 0], [1, a[1], b[1]]] |
| 42 | +end |
| 43 | +function h_dense_arrayvec_julia!(out, x) |
| 44 | + a, b, c = x |
| 45 | + out[1] .= [a[1], 0, c[1]] |
| 46 | + out[2] .= [0, 0, 0] |
| 47 | + out[3] .= [1, a[1], b[1]] |
| 48 | +end |
| 49 | + |
| 50 | +h_dense_arrayvec_str = ModelingToolkit.build_function(h_dense_arrayvec, [a, b, c]) |
| 51 | +h_dense_arrayvec_oop = eval(h_dense_arrayvec_str[1]) |
| 52 | +h_dense_arrayvec_ip! = eval(h_dense_arrayvec_str[2]) |
| 53 | +out_1_arrayvec = [Vector{Int64}(undef, 3) for i in 1:3] |
| 54 | +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) |
| 59 | +h_dense_arrayvec_julia!(out_1_arrayvec, input) |
| 60 | +h_dense_arrayvec_ip!(out_2_arrayvec, input) |
| 61 | +@test out_1_arrayvec == out_2_arrayvec |
| 62 | + |
| 63 | +# Arrays of Arrays of Matrices |
| 64 | +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 |
| 69 | +function h_dense_arrayNestedMat_julia!(out, x) |
| 70 | + a, b, c = x |
| 71 | + out[1][1] .= [a[1] 1; b[1] 0] |
| 72 | + out[1][2] .= [0 0; 0 0] |
| 73 | + out[2][1] .= [b[1] 1; a[1] 0] |
| 74 | + out[2][2] .= [b[1] c[1]; 0 1] |
| 75 | +end |
| 76 | + |
| 77 | +h_dense_arrayNestedMat_str = ModelingToolkit.build_function(h_dense_arrayNestedMat, [a, b, c]) |
| 78 | +h_dense_arrayNestedMat_oop = eval(h_dense_arrayNestedMat_str[1]) |
| 79 | +h_dense_arrayNestedMat_ip! = eval(h_dense_arrayNestedMat_str[2]) |
| 80 | +out_1_arrayNestedMat = [[rand(Int64, 2, 2), rand(Int64, 2, 2)], [rand(Int64, 2, 2), rand(Int64, 2, 2)]] # avoid undef broadcasting issue |
| 81 | +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 |
| 85 | +h_dense_arrayNestedMat_julia!(out_1_arrayNestedMat, input) |
| 86 | +h_dense_arrayNestedMat_ip!(out_2_arrayNestedMat, input) |
| 87 | +@test out_1_arrayNestedMat == out_2_arrayNestedMat |
| 88 | + |
| 89 | +# ===== Sparse tests ===== |
| 90 | +# Array of Matrices |
| 91 | +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 |
| 96 | +function h_sparse_arraymat_julia!(out, x) |
| 97 | + a, b, c = x |
| 98 | + out[1][1, 1] = a[1] |
| 99 | + out[1][1, 2] = 1 |
| 100 | + out[1][2, 1] = b[1] |
| 101 | + out[2] = sparse([0 0; 0 0]) # no undef constructor for SparseMatrixCSC |
| 102 | + out[3][1, 1] = a[1] |
| 103 | + out[3][1, 2] = c[1] |
| 104 | + out[3][2, 1] = 1 |
| 105 | +end |
| 106 | + |
| 107 | +h_sparse_arraymat_str = ModelingToolkit.build_function(h_sparse_arraymat, [a, b, c]) |
| 108 | +h_sparse_arraymat_oop = eval(h_sparse_arraymat_str[1]) |
| 109 | +h_sparse_arraymat_ip! = eval(h_sparse_arraymat_str[2]) |
| 110 | +h_sparse_arraymat_sparsity_patterns = map(get_sparsity_pattern, h_sparse_arraymat) |
| 111 | +out_1_arraymat = [similar(h) for h in h_sparse_arraymat_sparsity_patterns] |
| 112 | +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 |
| 116 | +h_sparse_arraymat_julia!(out_1_arraymat, input) |
| 117 | +h_sparse_arraymat_ip!(out_2_arraymat, input) |
| 118 | +@test out_1_arraymat == out_2_arraymat |
| 119 | + |
| 120 | +# Array of 1D Vectors |
| 121 | +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 |
| 126 | +function h_sparse_arrayvec_julia!(out, x) |
| 127 | + a, b, c = x |
| 128 | + out[1][1] = a[1] |
| 129 | + out[1][3] = c[1] |
| 130 | + out[2] = sparse([0, 0, 0]) # necessary because sparsity pattern is 3 elements with 0 stored, not 0 elements |
| 131 | + out[3][1] = 1 |
| 132 | + out[3][2] = a[1] |
| 133 | + out[3][3] = b[1] |
| 134 | +end |
| 135 | + |
| 136 | +h_sparse_arrayvec_str = ModelingToolkit.build_function(h_sparse_arrayvec, [a, b, c]) |
| 137 | +h_sparse_arrayvec_oop = eval(h_sparse_arrayvec_str[1]) |
| 138 | +h_sparse_arrayvec_ip! = eval(h_sparse_arrayvec_str[2]) |
| 139 | +h_sparse_arrayvec_sparsity_patterns = map(get_sparsity_pattern, h_sparse_arrayvec) |
| 140 | +out_1_arrayvec = [similar(h) for h in h_sparse_arrayvec_sparsity_patterns] |
| 141 | +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 |
| 145 | +h_sparse_arrayvec_julia!(out_1_arrayvec, input) |
| 146 | +h_sparse_arrayvec_ip!(out_2_arrayvec, input) |
| 147 | +@test out_1_arrayvec == out_2_arrayvec |
| 148 | + |
| 149 | +# Arrays of Arrays of Matrices |
| 150 | +h_sparse_arrayNestedMat = [[[a 1; b 0], [0 0; 0 0]], [[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 |
| 155 | +function h_sparse_arrayNestedMat_julia!(out, x) |
| 156 | + a, b, c = x |
| 157 | + out[1][1][1, 1] = a[1] |
| 158 | + out[1][1][1, 2] = 1 |
| 159 | + out[1][1][2, 1] = b[1] |
| 160 | + out[1][2] = sparse([0 0; 0 0]) |
| 161 | + out[2][1][1, 1] = b[1] |
| 162 | + out[2][1][1, 2] = 1 |
| 163 | + out[2][1][2, 1] = a[1] |
| 164 | + out[2][2][1, 1] = b[1] |
| 165 | + out[2][2][1, 2] = c[1] |
| 166 | + out[2][2][2, 2] = 1 |
| 167 | +end |
| 168 | + |
| 169 | +h_sparse_arrayNestedMat_str = ModelingToolkit.build_function(h_sparse_arrayNestedMat, [a, b, c]) |
| 170 | +h_sparse_arrayNestedMat_oop = eval(h_sparse_arrayNestedMat_str[1]) |
| 171 | +h_sparse_arrayNestedMat_ip! = eval(h_sparse_arrayNestedMat_str[2]) |
| 172 | +h_sparse_arrayNestedMat_sparsity_patterns = [map(get_sparsity_pattern, h) for h in h_sparse_arrayNestedMat] |
| 173 | +out_1_arrayNestedMat = [[similar(h_sub) for h_sub in h] for h in h_sparse_arrayNestedMat_sparsity_patterns] |
| 174 | +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 |
| 178 | +h_sparse_arrayNestedMat_julia!(out_1_arrayNestedMat, input) |
| 179 | +h_sparse_arrayNestedMat_ip!(out_2_arrayNestedMat, input) |
| 180 | +@test out_1_arrayNestedMat == out_2_arrayNestedMat |
| 181 | + |
| 182 | +# Additional Tests |
| 183 | +# Returning 0-element structures (corresponding to empty Jacobians) |
| 184 | +# Arrays of Matrices |
| 185 | +h_empty = [[a b; c 0], Array{Expression,2}(undef, 0,0)] |
| 186 | +h_empty_str = ModelingToolkit.build_function(h_empty, [a, b, c]) |
| 187 | +h_empty_oop = eval(h_empty_str[1]) |
| 188 | +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)] |
| 190 | +out = [Matrix{Int64}(undef, 2, 2), Matrix{Int64}(undef, 0, 0)] |
| 191 | +h_empty_ip!(out, input) # should just not fail |
| 192 | + |
| 193 | +# Array of Vectors |
| 194 | +h_empty_vec = [[a, b, c, 0], Vector{Expression}(undef,0)] |
| 195 | +h_empty_vec_str = ModelingToolkit.build_function(h_empty_vec, [a, b, c]) |
| 196 | +h_empty_vec_oop = eval(h_empty_vec_str[1]) |
| 197 | +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)] |
| 199 | +out = [Vector{Int64}(undef, 4), Vector{Int64}(undef, 0)] |
| 200 | +h_empty_vec_ip!(out, input) # should just not fail |
| 201 | + |
| 202 | +# Arrays of Arrays of Matrices |
| 203 | +h_emptyNested = [[[a b; c 0]], Array{Array{Expression, 2}}(undef, 0)] # emptyNested array of arrays |
| 204 | +h_emptyNested_str = ModelingToolkit.build_function(h_emptyNested, [a, b, c]) |
| 205 | +h_emptyNested_oop = eval(h_emptyNested_str[1]) |
| 206 | +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)] |
| 208 | +out = [[[1 2;3 4]], Array{Array{Int64,2},1}(undef, 0)] |
| 209 | +h_emptyNested_ip!(out, input) # should just not fail |
0 commit comments