Greetings.
I am seeking to replace
# generates local stiffness matrix
function genLocStiffMat(element::Element)
h = element.area
e1 = element.e1
e2 = element.e2
Iloc = SVector(e1, e1, e2, e2)
Jloc = SVector(e1, e2, e1, e2)
Aloc = SVector(1/h, -1/h, -1/h, 1/h)
return Iloc, Jloc, Aloc
end
by a more elegant version that works for larger sets of e_i values.
The version
function genLocStiffMat2(element::Element)
h = element.area
v = SVector(element.e1, element.e2)
Iloc = SVector{4}([v[i] for j=1:2, i=1:2])
Jloc = SVector{4}([v[i] for i=1:2, j=1:2])
Aloc = SVector(1/h, -1/h, -1/h, 1/h)
return Iloc, Jloc, Aloc
end
allocates memory. Advice is appreciated.
try using generator instead […]
function genLocStiffMat2(element)
h = element.area
v = SVector(element.e1, element.e2)
Iloc = SVector{4}(v[i] for j=1:2, i=1:2)
Jloc = SVector{4}(v[i] for i=1:2, j=1:2)
Aloc = SVector(1/h, -1/h, -1/h, 1/h)
return Iloc, Jloc, Aloc
end
1 Like
I don’t have access to running Julia at the moment, but the key is that the statements [v[i] for j=1:2, i=1:2]
and [v[i] for i=1:2, j=1:2]
both allocate a 2x2 matrix. I think what you want is
Iloc = SVector{4}(v[i] for j=1:2 for i=1:2)
though I may have gotten my is and js backwards.
1 Like
Hurray! What is “generator” is this context?
Yup! That does the trick. I would like to understand the underlying principle. Why does [ … ] allocate memory? How is it avoided by the suggestion you provide? Sincere thanks.
Comprehensions can also be written without the enclosing square brackets, producing an object known as a Generator
1 Like
Hurray again! Thx for bearing with me.
[...]
is julias syntax for creating an Array
, which is always (or close enough to always, there may be some exceptions, I’m not an expert) heap allocated (reported as allocations by @time
). Thus, your original expression first created an Array
and then SVector
used this allocated Array
to construct the SVector
.
1 Like
nsajko
January 24, 2024, 9:18pm
9
Playing in the REPL should be helpful:
julia> [i for j=1:2, i=1:2]
2Ă—2 Matrix{Int64}:
1 2
1 2
julia> isbits(ans)
false
julia> (i for j=1:2, i=1:2)
Base.Generator{Base.Iterators.ProductIterator{Tuple{UnitRange{Int64}, UnitRange{Int64}}}, var"#3#4"}(var"#3#4"(), Base.Iterators.ProductIterator{Tuple{UnitRange{Int64}, UnitRange{Int64}}}((1:2, 1:2)))
julia> isbits(ans)
true
I’d also note that Iterators.map
is an alternative to using the generator syntax sugar:
julia> a = (i for j=1:2, i=1:2)
Base.Generator{Base.Iterators.ProductIterator{Tuple{UnitRange{Int64}, UnitRange{Int64}}}, var"#1#2"}(var"#1#2"(), Base.Iterators.ProductIterator{Tuple{UnitRange{Int64}, UnitRange{Int64}}}((1:2, 1:2)))
julia> b = Iterators.map(last, Iterators.product(1:2, 1:2))
Base.Generator{Base.Iterators.ProductIterator{Tuple{UnitRange{Int64}, UnitRange{Int64}}}, typeof(last)}(last, Base.Iterators.ProductIterator{Tuple{UnitRange{Int64}, UnitRange{Int64}}}((1:2, 1:2)))
julia> a == b
false
julia> collect(a) == collect(b)
true
(TBH I’m not sure why a == b
returns false
? EDIT: Base.Generator
doesn’t overload ==
, so they get compared by ===
. Bug? Reported an issue on the Github: equality for iterators · Issue #53046 · JuliaLang/julia · GitHub )
a shape ALMOST the same as a=...
julia> i=1:2
1:2
julia> j=1:2
1:2
julia> c=Base.Generator((j,i)->i,Base.product(j,i))
Base.Generator{Base.Iterators.ProductIterator{Tuple{UnitRange{Int64}, UnitRange{Int64}}}, var"#41#42"}(var"#41#42"(),
Base.Iterators.ProductIterator{Tuple{UnitRange{Int64}, UnitRange{Int64}}}((1:2, 1:2)))
julia> a = (i for j=1:2, i=1:2)
Base.Generator{Base.Iterators.ProductIterator{Tuple{UnitRange{Int64}, UnitRange{Int64}}}, var"#43#44"}(var"#43#44"(),
Base.Iterators.ProductIterator{Tuple{UnitRange{Int64}, UnitRange{Int64}}}((1:2, 1:2)))
Dan
January 24, 2024, 11:28pm
11
There is also the @SVector
convenience macro:
Iloc = @SVector [e1, e1, e2, e2]
Jloc = @SVector [e1, e2, e1, e2]
Aloc = @SVector [1/h, -1/h, -1/h, 1/h]
which doesn’t allocate and looks quite clear.