DA Algorithm

中田 竜明

Many to one


In [1]:
using DA
using Plots
plotlyjs()


Plotly javascript loaded.

To load again call

init_notebook(true)

Out[1]:
Plots.PlotlyJSBackend()

one to oneとみなせる場合についてテストを行います.


In [2]:
Pkg.test("DA")


INFO: Testing DA
Test Summary:               | Pass  Total
Testing deferred acceptance |   18     18
INFO: DA tests passed

In [3]:
num = 10^4
prop_prefs, resp_prefs = generate_random_prefs(num, num)
caps = ones(Int, num)
deferred_acceptance(prop_prefs, resp_prefs, caps)
Profile.clear()
@profile deferred_acceptance(prop_prefs, resp_prefs, caps)
Profile.print()


191 ./task.jl:335; (::IJulia.##11#14)()
 191 ...Julia/src/eventloop.jl:8; eventloop(::ZMQ.Socket)
  191 ...rc/execute_request.jl:160; execute_request(::ZMQ.Socket, ::...
   191 ./loading.jl:515; include_string(::String, ::String)
    190 ./<missing>:?; anonymous
     190 ./profile.jl:23; macro expansion
      163 .../DA/src/matchings.jl:43; deferred_acceptance(::Array{Ar...
       66 .../DA/src/matchings.jl:5; create_resp_ranks(::Int64, ::In...
        52 ./array.jl:227; fill!(::Array{UInt16,2}, ::UI...
        14 ./array.jl:228; fill!(::Array{UInt16,2}, ::UI...
       32 .../DA/src/matchings.jl:7; create_resp_ranks(::Int64, ::In...
        29 ./iterators.jl:63; next
       65 .../DA/src/matchings.jl:8; create_resp_ranks(::Int64, ::In...
      1   .../DA/src/matchings.jl:61; deferred_acceptance(::Array{Ar...
      5   .../DA/src/matchings.jl:65; deferred_acceptance(::Array{Ar...
      5   .../DA/src/matchings.jl:67; deferred_acceptance(::Array{Ar...
       2 ...v0.6/DA/src/tools.jl:20; top
       2 ...v0.6/DA/src/tools.jl:23; top
      9   .../DA/src/matchings.jl:68; deferred_acceptance(::Array{Ar...
      1   .../DA/src/matchings.jl:76; deferred_acceptance(::Array{Ar...
       1 ...v0.6/DA/src/tools.jl:14; push!(::DA.DATools.FixedSizeBin...
      4   .../DA/src/matchings.jl:77; deferred_acceptance(::Array{Ar...
      1   .../DA/src/matchings.jl:81; deferred_acceptance(::Array{Ar...
      1   .../DA/src/matchings.jl:85; deferred_acceptance(::Array{Ar...
       1 .../DA/src/matchings.jl:21; convert_into_matched(::Int64, :...
    1   ./inference.jl:2603; typeinf_ext(::Core.MethodInsta...
     1 ./inference.jl:2564; typeinf_code(::Core.MethodInsta...
      1 ./inference.jl:2494; typeinf_frame(::Core.MethodIns...
       1 ./inference.jl:2641; typeinf_loop(::Core.Inference....
        1 ./inference.jl:2778; typeinf_frame(::Core.Inferenc...
         1 ./inference.jl:1935; abstract_eval(::Any, ::Array{...
          1 ./inference.jl:1886; abstract_eval_call(::Expr, :...
           1 ./abstractarray.jl:572; copy!(::Array{Any,1}, ::Core...
            1 ./inference.jl:1935; abstract_eval(::Any, ::Arra...
             1 ./inference.jl:1912; abstract_eval_call(::Expr,...
              1 ./inference.jl:1882; abstract_call(::Any, ::Arr...
               1 ./inference.jl:1401; abstract_call_gf_by_type(...
                1 ./inference.jl:2517; typeinf_edge(::Method, :...
                 1 ./inference.jl:2494; typeinf_frame(::Core.Met...
                  1 ./inference.jl:2624; typeinf_loop(::Core.Inf...
                   1 ./inference.jl:2725; typeinf_frame(::Core.In...
                    1 ./inference.jl:2072; abstract_interpret(::A...
                     1 ./inference.jl:1935; abstract_eval(::Any, ...
                      1 ./inference.jl:1886; abstract_eval_call(::...
                       1 ...tractarray.jl:572; copy!(::Array{Any,1},...
                        1 ./inference.jl:1927; abstract_eval(::Any,...
                         1 ./inference.jl:2034; abstract_eval_globa...

時間の殆どをマッチング前のデータ整形に使っていることがわかります.(matchings.jlのline43)

ここで,one-to-oneとみなせるmany-to-many(capsがすべて1のケース)に関して, マッチングの規模を変えながらその実行時間を計測してみます.


In [4]:
Pkg.checkout("DA", "forcomparison")


INFO: Checking out DA forcomparison...
INFO: Pulling DA latest forcomparison...
INFO: No packages to install, update or remove

In [5]:
Pkg.status("DA")


 - DA                            0.0.0-             forcomparison (unregistered)

このままだとブランチの変更が反映されないのでNotebookをRestartします.


In [1]:
using DA
using Plots
plotlyjs()


Plotly javascript loaded.

To load again call

init_notebook(true)

Out[1]:
Plots.PlotlyJSBackend()

In [2]:
num_range = [50, 100, 200, 500, 700, 1000]
loop = 20
elapsed = Array{Float64}(loop, length(num_range), 4)

for (i, num) in enumerate(num_range)
    caps = ones(Int, num)
    for l in 1:loop
        prop_prefs, resp_prefs = generate_random_prefs(num, num)
        _, elapsed[l, i, 1], _, _ = @timed deferred_acceptance(prop_prefs, resp_prefs)
        _, elapsed[l, i, 2], _, _ = @timed deferred_acceptance_rev(prop_prefs, resp_prefs, caps)
        _, elapsed[l, i, 3], _, _ = @timed deferred_acceptance_rev_offheap(prop_prefs, resp_prefs, caps)
        _, elapsed[l, i, 4], _, _ = @timed deferred_acceptance(prop_prefs, resp_prefs, caps)
    end
end

In [3]:
plot(num_range,
     log.(squeeze(mean(sort(elapsed, 1)[1:loop-10, :, :], 1), 1)),
     xlabel="num",
     ylabel="time (log scale)",
     title="average time",
     labels=reshape(["one-to-one", "v2017", "v2017+DS.heap", "v2016"], (1, 4)))


WARNING: filter(flt, itr) is deprecated, use Iterators.filter(flt, itr) instead.
Stacktrace:
 [1] depwarn(::String, ::Symbol) at ./deprecated.jl:70
 [2] filter(::Function, ::Base.KeyIterator{Dict{Symbol,Any}}) at ./deprecated.jl:57
 [3] _apply_style_axis!(::PlotlyJS.Plot{PlotlyJS.GenericTrace{Dict{Symbol,Any}}}, ::String, ::Bool) at /Users/neon/.julia/v0.6/PlotlyJS/src/json.jl:8
 [4] lower(::PlotlyJS.Plot{PlotlyJS.GenericTrace{Dict{Symbol,Any}}}) at /Users/neon/.julia/v0.6/PlotlyJS/src/json.jl:52
 [5] script_content(::PlotlyJS.Plot{PlotlyJS.GenericTrace{Dict{Symbol,Any}}}) at /Users/neon/.julia/v0.6/PlotlyJS/src/display.jl:18
 [6] _show(::Base.AbstractIOBuffer{Array{UInt8,
Out[3]:
1}}, ::MIME{Symbol("image/svg+xml")}, ::Plots.Plot{Plots.PlotlyJSBackend}) at /Users/neon/.julia/v0.6/Plots/src/backends/plotlyjs.jl:90
 [7] show(::Base.AbstractIOBuffer{Array{UInt8,1}}, ::MIME{Symbol("image/svg+xml")}, ::Plots.Plot{Plots.PlotlyJSBackend}) at /Users/neon/.julia/v0.6/Plots/src/output.jl:197
 [8] show(::Base.AbstractIOBuffer{Array{UInt8,1}}, ::MIME{Symbol("text/html")}, ::Plots.Plot{Plots.PlotlyJSBackend}) at /Users/neon/.julia/v0.6/Plots/src/output.jl:177
 [9] show(::Base.AbstractIOBuffer{Array{UInt8,1}}, ::String, ::Plots.Plot{Plots.PlotlyJSBackend}) at ./multimedia.jl:39
 [10] #sprint#228(::Void, ::Function, ::Int64, ::Function, ::String, ::Vararg{Any,N} where N) at ./strings/io.jl:66
 [11] display_dict(::Plots.Plot{Plots.PlotlyJSBackend}) at /Users/neon/.julia/v0.6/Plots/src/output.jl:266
 [12] execute_request(::ZMQ.Socket, ::IJulia.Msg) at /Users/neon/.julia/v0.6/IJulia/src/execute_request.jl:188
 [13] eventloop(::ZMQ.Socket) at /Users/neon/.julia/v0.6/IJulia/src/eventloop.jl:8
 [14] (::IJulia.##11#14)() at ./task.jl:335
while loading /Users/neon/.julia/v0.6/IJulia/src/kernel.jl, in expression starting on line 31

In [4]:
plot(num_range,
     squeeze(mean(sort(elapsed, 1)[1:loop-10, :, :], 1), 1),
     xlabel="num",
     ylabel="time",
     title="average time",
     labels=reshape(["one-to-one", "v2017", "v2017+DS.heap", "v2016"], (1, 4)))


Out[4]:

one-to-oneとして実装したものがやはり一番早いようです.

(v2017+DS.heapはDataStructuresのbinary_maxheapを使用したバージョン, v2017は自分で実装したHeapを使用したバージョンです.)


In [5]:
Pkg.checkout("DA", "master")


INFO: Checking out DA master...
INFO: Pulling DA latest master...
INFO: No packages to install, update or remove

In [6]:
Pkg.status("DA")


 - DA                            0.0.0-             master (unregistered)

preferenceからrankを作る関数(内の代入)に一番時間がかかっているので,rankの型をUInt16型にしてみました.

関数の引数に制限がかかってしまいますが(many-to-manyのケースだとnum_respsとsum(prop_caps)が共に2^16-1= 65535以下),少し早くなるはずです.

one-to-oneとみなせるmany-to-oneのケースについて,提案側・受ける側の人数numを変えて実行時間を測ってみます.


In [1]:
using DA
using Plots
plotlyjs()


Plotly javascript loaded.

To load again call

init_notebook(true)

Out[1]:
Plots.PlotlyJSBackend()

In [2]:
num_range = collect(100:200:1100)
loop = 20
elapsed = Array{Float64}(loop, length(num_range))

for (i, num) in enumerate(num_range)
    caps = ones(Int, num)
    for l in 1:loop
        prop_prefs, resp_prefs = generate_random_prefs(num, num)
        _, elapsed[l, i], _, _ = @timed deferred_acceptance(prop_prefs, resp_prefs, caps)
    end
end

In [3]:
plot(num_range, 
    squeeze(mean(sort(elapsed, 1)[1:loop-15, :], 1), 1),
    xlabel="scale",
    ylabel="time",
    title="average time")


WARNING: filter(flt, itr) is deprecated, use Iterators.filter(flt, itr) instead.
Stacktrace:
 [1] depwarn(::String, ::Symbol) at ./deprecated.jl:70
 [2] filter(::Function, ::Base
Out[3]:
.KeyIterator{Dict{Symbol,Any}}) at ./deprecated.jl:57
 [3] _apply_style_axis!(::PlotlyJS.Plot{PlotlyJS.GenericTrace{Dict{Symbol,Any}}}, ::String, ::Bool) at /Users/neon/.julia/v0.6/PlotlyJS/src/json.jl:8
 [4] lower(::PlotlyJS.Plot{PlotlyJS.GenericTrace{Dict{Symbol,Any}}}) at /Users/neon/.julia/v0.6/PlotlyJS/src/json.jl:52
 [5] script_content(::PlotlyJS.Plot{PlotlyJS.GenericTrace{Dict{Symbol,Any}}}) at /Users/neon/.julia/v0.6/PlotlyJS/src/display.jl:18
 [6] _show(::Base.AbstractIOBuffer{Array{UInt8,1}}, ::MIME{Symbol("image/svg+xml")}, ::Plots.Plot{Plots.PlotlyJSBackend}) at /Users/neon/.julia/v0.6/Plots/src/backends/plotlyjs.jl:90
 [7] show(::Base.AbstractIOBuffer{Array{UInt8,1}}, ::MIME{Symbol("image/svg+xml")}, ::Plots.Plot{Plots.PlotlyJSBackend}) at /Users/neon/.julia/v0.6/Plots/src/output.jl:197
 [8] show(::Base.AbstractIOBuffer{Array{UInt8,1}}, ::MIME{Symbol("text/html")}, ::Plots.Plot{Plots.PlotlyJSBackend}) at /Users/neon/.julia/v0.6/Plots/src/output.jl:177
 [9] show(::Base.AbstractIOBuffer{Array{UInt8,1}}, ::String, ::Plots.Plot{Plots.PlotlyJSBackend}) at ./multimedia.jl:39
 [10] #sprint#228(::Void, ::Function, ::Int64, ::Function, ::String, ::Vararg{Any,N} where N) at ./strings/io.jl:66
 [11] display_dict(::Plots.Plot{Plots.PlotlyJSBackend}) at /Users/neon/.julia/v0.6/Plots/src/output.jl:266
 [12] execute_request(::ZMQ.Socket, ::IJulia.Msg) at /Users/neon/.julia/v0.6/IJulia/src/execute_request.jl:188
 [13] eventloop(::ZMQ.Socket) at /Users/neon/.julia/v0.6/IJulia/src/eventloop.jl:8
 [14] (::IJulia.##11#14)() at ./task.jl:335
while loading /Users/neon/.julia/v0.6/IJulia/src/kernel.jl, in expression starting on line 31

ちなみに以下の図がone-to-one用に実装したdeferred acceptance algorithmの処理時間のグラフです.

one-to-oneとして実装したものと比べ,人数が多いところでは二倍ほど早くなりました.


In [ ]: