2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

一時的なデータの格納について:独自型Temporalfieldsの作成

Last updated at Posted at 2024-12-01

何がしたいのか

任意の型を「一時的なデータ」として取りまわすことを目的としています。特に、さまざまなところでデータを使い回すときに、「すでに確保している」「確保していない」がわかるようにしたいです。確保しているデータを使おうとするとエラーが出るようにしたいです。

この記事が役に立つこと

  • 具体的な独自型の定義の例
  • 不必要なメモリアロケーションをしないデータの取り回し

背景

格子量子色力学のシミュレーションを行うためのオープンソースソフトウェア群JuliaQCDを開発しています。この中の一つに、4次元の格子上に定義されたゲージ場を扱うパッケージGaugefields.jlがあります。一つのゲージ場は4次元の格子上に定義された複素3x3ユニタリー行列で表現されており、非常に大きな配列です。ですので、新しい配列を毎回定義しているとすごく時間がかかります。ですので、あらかじめ「一時的なデータ」を最初の方で確保しておき、それを使い回すことで計算を高速化できます。

現在のGaugefields.jlでは、一時データはゲージ場を要素とするVectorとして定義されています。必要に応じて複数の一時ゲージ場をここから取り出して使います。しかし、この一時ゲージ場に入っていて後で使うデータと、上書きしてしまってもいいデータがどれかがわかりにくく、コードを書く際にデバッグが困難になっていました。これを解決することを目的としています。

コード

コードは一つのmoduleとしました。

Temporalfields.jl
module Temporalfields_module

mutable struct Temporalfields{TG}
    _data::Vector{TG}
    _flagusing::Vector{Bool}
    _indices::Vector{Int64}
    Nmax::Int64

    function Temporalfields(a::TG; num=1, Nmax=1000) where {TG}
        _data = Vector{TG}(undef, num)
        _flagusing = zeros(Bool, num)
        _indices = zeros(Int64, num)
        for i = 1:num
            _data[i] = similar(a)
        end

        return new{TG}(_data, _flagusing, _indices, Nmax)
    end
end

Base.eltype(::Type{Temporalfields{TG}}) where {TG} = TG

Base.length(t::Temporalfields{TG}) where {TG} = length(t._data)

Base.size(t::Temporalfields{TG}) where {TG} = size(t._data)

function Base.firstindex(t::Temporalfields{TG}) where {TG}
    return 1
end

function Base.lastindex(t::Temporalfields{TG}) where {TG}
    return length(t._data)
end

function Base.getindex(t::Temporalfields{TG}, i::Int) where {TG}
    if i > length(t._data)
        @warn "The length of the temporalfields is shorter than the index $i. New temporal fields are created."
        ndiff = i - length(t._data)
        @assert i <= t.Nmax "The number of the tempralfields $i is larger than the maximum number $(Nmax). Change Nmax."
        for n = 1:ndiff
            push!(t._data, similar(t._data[1]))
            push!(t._flagusing, 0)
            push!(t._indices, 0)
        end
    end
    if t._indices[i] == 0
        index = findfirst(x -> x == 0, t._flagusing)
        t._flagusing[index] = true
        t._indices[i] = index
    else
        error("This index $i is being using.  You should pay attention")
    end

    return t._data[t._indices[i]]
end

function Base.getindex(t::Temporalfields{TG}, I::Vararg{Int,N}) where {TG,N}
    data = TG[]
    for i in I
        push!(data, t[i])
    end
    return data
end

function Base.getindex(t::Temporalfields{TG}, I::AbstractVector{T}) where {TG,T<:Integer}
    data = TG[]
    for i in I
        push!(data, t[i])
    end
    return data
end

function Base.display(t::Temporalfields{TG}) where {TG}
    n = length(t._data)
    println("The total number of fields: $n")
    numused = sum(t._flagusing)
    println("The total number of fields used: $numused")
    for i = 1:n
        if t._indices[i] != 0
            #println("The address $i is used as the index $(t._indices[i])")
            println("The address $(t._indices[i]) is used as the index $i")
        end
    end
    println("The flags: $(t._flagusing)")
    println("The indices: $(t._indices)")
end
function unused!(t::Temporalfields{TG}, i) where {TG}
    if t._indices[i] != 0
        index = t._indices[i]
        t._flagusing[index] = false
        t._indices[i] = 0
    end
end


function unused!(t::Temporalfields{TG}, I::AbstractVector{T}) where {TG,T<:Integer}
    for i in I
        unused!(t, i)
    end
end

function unused!(t::Temporalfields{TG}) where {TG}
    for i = 1:length(t)
        unused!(t, i)
    end
end


export Temporalfields, unused!

end

使用例

このTemporalfields型の使用例は以下のとおりです。

include("Temporalfields.jl")
using .Temporalfields_module
function main()
    a = rand(10)
    tempvec = Temporalfields(a)
    display(tempvec)
    b = tempvec[1]
    println(b)
    display(tempvec)
    c = tempvec[2]
    println(c)
    display(tempvec)

    tempvec2 = Temporalfields(a; num=4)
    d1 = tempvec2[1]
    println(d1)
    display(tempvec2)
    println("---------------------")
    d2 = tempvec2[2]
    println(d2)
    display(tempvec2)
    unused!(tempvec2, 2)
    println("---------------------")
    d3 = tempvec2[3]
    println(d3)
    display(tempvec2)
    println("---------------------")
    d6 = tempvec2[6]
    println(d6)
    display(tempvec2)
    println("---------------------")
    vecd = tempvec2[3:10]
end
main()

これを実行すると、

The total number of fields: 1
The total number of fields used: 0
The flags: Bool[0]
The indices: [0]
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
The total number of fields: 1
The total number of fields used: 1
The address 1 is used as the index 1
The flags: Bool[1]
The indices: [1]
┌ Warning: The length of the temporalfields is shorter than the index 2. New temporal fields are created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:37
[2.250178182e-314, 2.38982809e-314, 2.25017819e-314, 2.38982809e-314, 2.343058413e-314, 2.3898280977e-314, 2.343058413e-314, 2.3898280977e-314, 2.343058413e-314, 2.3898280977e-314]
The total number of fields: 2
The total number of fields used: 2
The address 1 is used as the index 1
The address 2 is used as the index 2
The flags: Bool[1, 1]
The indices: [1, 2]
[2.250178182e-314, 2.38982809e-314, 2.25017819e-314, 2.38982809e-314, 2.343058413e-314, 2.3898280977e-314, 2.343058413e-314, 2.3898280977e-314, 2.343058413e-314, 2.3898280977e-314]
The total number of fields: 4
The total number of fields used: 1
The address 1 is used as the index 1
The flags: Bool[1, 0, 0, 0]
The indices: [1, 0, 0, 0]
---------------------
[2.250178182e-314, 2.38982809e-314, 2.25017819e-314, 2.38982809e-314, 2.343058413e-314, 2.3898280977e-314, 2.1569822157e-314, 2.38982809e-314, 2.343058413e-314, 2.3898280977e-314]
The total number of fields: 4
The total number of fields used: 2
The address 1 is used as the index 1
The address 2 is used as the index 2
The flags: Bool[1, 1, 0, 0]
The indices: [1, 2, 0, 0]
---------------------
[2.250178182e-314, 2.38982809e-314, 2.25017819e-314, 2.38982809e-314, 2.343058413e-314, 2.3898280977e-314, 2.1569822157e-314, 2.38982809e-314, 2.343058413e-314, 2.3898280977e-314]
The total number of fields: 4
The total number of fields used: 2
The address 1 is used as the index 1
The address 2 is used as the index 3
The flags: Bool[1, 1, 0, 0]
The indices: [1, 0, 2, 0]
---------------------
┌ Warning: The length of the temporalfields is shorter than the index 6. New temporal fields are created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:37
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
The total number of fields: 6
The total number of fields used: 3
The address 1 is used as the index 1
The address 2 is used as the index 3
The address 3 is used as the index 6
The flags: Bool[1, 1, 1, 0, 0, 0]
The indices: [1, 0, 2, 0, 0, 3]
---------------------
ERROR: LoadError: This index 3 is being using.  You should pay attention

となります。通常の配列のように

tempvec2 = Temporalfields(a; num=4)
d1 = tempvec2[1]

とすると、一時データの1番を使います。ただし、1番がすでに使われているときは、エラーを出して終了します。使われていないときは、一時データの中で使っていない要素を取り出してきて、それを1番とします。また、

unused!(tempvec2,1)

とすると、一時データの1番を解放します。つまり、自分で一時データを確保して、使い終わった後に別の用途で使用したいときにはunused!で解放してあげることになります。このunused!を使う方法によって、不用意に意図しない上書きを防ぎます。これは、定義するときにだけ使えるので

d1 = tempvec2[1]
d2 = tempvec2[2]
d3 = tempvec2[3]

d = tempvec2[1:4]

のように定義してあげた後は、通常のデータとして使用することができます。

改良

毎回インデックスを指定するのは、少し面倒です。そこで、

function get_temp(t::Temporalfields{TG}) where {TG}
    n = length(t._data)
    i = findfirst(x -> x == 0, t._indices)
    if i == nothing
        @warn "All temporal fields are used. New one is created."
        i = n + 1
    end
    return t[i], i
end

function get_temp(t::Temporalfields{TG}, num) where {TG}
    n = length(t._data)
    i_s = Int64[]
    t_s = TG[]
    for k = 1:num
        tk, i = get_temp(t)
        push!(t_s, tk)
        push!(i_s, i)
    end
    return t_s, i_s
end
export Temporalfields, unused!, get_temp

という関数を実装しました。これにより、

function main2()
    a = rand(10)
    tempvec = Temporalfields(a)
    display(tempvec)
    b, it_b = get_temp(tempvec)
    println(b)
    display(tempvec)
    c, it_c = get_temp(tempvec)
    println(c)
    display(tempvec)

    tempvec2 = Temporalfields(a; num=4)
    d1, it_d1 = get_temp(tempvec2)
    println(d1)
    display(tempvec2)
    println("---------------------")
    d2, it_d2 = get_temp(tempvec2)
    println(d2)
    display(tempvec2)
    unused!(tempvec2, it_d2)
    println("---------------------")
    #d3 = tempvec2[3]
    d3, it_d3 = get_temp(tempvec2)
    println(d3)
    display(tempvec2)
    println("---------------------")
    vecd, its_vecd = get_temp(tempvec2, 7)
    display(tempvec2)

    unused!(tempvec2, its_vecd[3])
    unused!(tempvec2, its_vecd[7])
    display(tempvec2)
    unused!(tempvec2, its_vecd)
    display(tempvec2)

end
main2()

の実行結果は、

The total number of fields: 1
The total number of fields used: 0
The flags: Bool[0]
The indices: [0]
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
The total number of fields: 1
The total number of fields used: 1
The address 1 is used as the index 1
The flags: Bool[1]
The indices: [1]
┌ Warning: All temporal fields are used. New one is created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:92
┌ Warning: The length of the temporalfields is shorter than the index 2. New temporal fields are created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:37
[2.2338109335e-314, 2.2338130204e-314, 2.233810965e-314, 2.233813036e-314, 2.2338109967e-314, 2.2411677607e-314, 2.2338110283e-314, 2.23381106e-314, 2.2338110916e-314, 2.2411677686e-314]
The total number of fields: 2
The total number of fields used: 2
The address 1 is used as the index 1
The address 2 is used as the index 2
The flags: Bool[1, 1]
The indices: [1, 2]
[2.349793168e-314, 2.34979765e-314, 2.349793168e-314, 2.34979765e-314, 2.34979765e-314, 2.34979765e-314, 2.3497931996e-314, 2.3497931996e-314, 2.2338106647e-314, 2.34979765e-314]
The total number of fields: 4
The total number of fields used: 1
The address 1 is used as the index 1
The flags: Bool[1, 0, 0, 0]
The indices: [1, 0, 0, 0]
---------------------
[2.32407147e-314, 2.32407147e-314, 2.32407147e-314, 2.32407147e-314, 2.32407147e-314, 2.32407147e-314, 2.231098837e-314, 2.231098932e-314, 2.32407147e-314, 2.32407147e-314]
The total number of fields: 4
The total number of fields used: 2
The address 1 is used as the index 1
The address 2 is used as the index 2
The flags: Bool[1, 1, 0, 0]
The indices: [1, 2, 0, 0]
---------------------
[2.32407147e-314, 2.32407147e-314, 2.32407147e-314, 2.32407147e-314, 2.32407147e-314, 2.32407147e-314, 2.231098837e-314, 2.231098932e-314, 2.32407147e-314, 2.32407147e-314]
The total number of fields: 4
The total number of fields used: 2
The address 1 is used as the index 1
The address 2 is used as the index 2
The flags: Bool[1, 1, 0, 0]
The indices: [1, 2, 0, 0]
---------------------
┌ Warning: All temporal fields are used. New one is created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:92
┌ Warning: The length of the temporalfields is shorter than the index 5. New temporal fields are created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:37
┌ Warning: All temporal fields are used. New one is created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:92
┌ Warning: The length of the temporalfields is shorter than the index 6. New temporal fields are created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:37
┌ Warning: All temporal fields are used. New one is created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:92
┌ Warning: The length of the temporalfields is shorter than the index 7. New temporal fields are created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:37
┌ Warning: All temporal fields are used. New one is created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:92
┌ Warning: The length of the temporalfields is shorter than the index 8. New temporal fields are created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:37
┌ Warning: All temporal fields are used. New one is created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:92
┌ Warning: The length of the temporalfields is shorter than the index 9. New temporal fields are created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:37
The total number of fields: 9
The total number of fields used: 9
The address 1 is used as the index 1
The address 2 is used as the index 2
The address 3 is used as the index 3
The address 4 is used as the index 4
The address 5 is used as the index 5
The address 6 is used as the index 6
The address 7 is used as the index 7
The address 8 is used as the index 8
The address 9 is used as the index 9
The flags: Bool[1, 1, 1, 1, 1, 1, 1, 1, 1]
The indices: [1, 2, 3, 4, 5, 6, 7, 8, 9]
The total number of fields: 9
The total number of fields used: 7
The address 1 is used as the index 1
The address 2 is used as the index 2
The address 3 is used as the index 3
The address 4 is used as the index 4
The address 6 is used as the index 6
The address 7 is used as the index 7
The address 8 is used as the index 8
The flags: Bool[1, 1, 1, 1, 0, 1, 1, 1, 0]
The indices: [1, 2, 3, 4, 0, 6, 7, 8, 0]
The total number of fields: 9
The total number of fields used: 2
The address 1 is used as the index 1
The address 2 is used as the index 2
The flags: Bool[1, 1, 0, 0, 0, 0, 0, 0, 0]
The indices: [1, 2, 0, 0, 0, 0, 0, 0, 0]

となります。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?