๐ฆ https://github.com/MikeInnes/DataFlow.jl/blob/master/docs/vertices.md ๋ฒ์ญ
DataFlow๊ฐ ํ๋ ๋๊ฐ์ง:
- ๊ทธ๋ํ ๋ฐ์ดํฐ ๊ตฌ์กฐ(a graph data structure)
- ๊ทธ๋ํ๋ฅผ ๊ธฐ์ ํ๊ธฐ ์ํ ๊ณตํต ๋ฌธ๋ฒ(a common syntax for describing graphs)
์๋ก ์ถ๋งค์ฌ ์์ง ์์ผ๋ ํธํ๊ฒ ์ฐ๋ฉด ๋๋ค; ์๋ฅผ ๋ค์ด, ์ด ๋ฌธ๋ฒ์ผ๋ก ๋ง๋ ๊ทธ๋ํ๋ฅผ ์ธ์ ํ๋ ฌ(an adjacency matrix)๋ก ๋ณํํ์ฌ ์ฒ๋ฆฌํ๋ค๊ฑฐ๋ DataFlow์ ๊ณตํต ๊ทธ๋ํ ์ฐ์ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํ์ฉํ์ฌ ๋ค๋ฅธ ๋ฐฉ์์ผ๋ก ๊ทธ๋ํ๋ฅผ ์์ฑํ ์ ์๋ค.
DataFlow๋ ๋ช ์์ ์ผ๋ก ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ๋จ์ํ๊ฒ ์ ์งํ๊ณ , ๋ค๋ฅธ ์ด๋ ํ ์๋ฏธ๋ ๋ง๋ถ์ด์ง ์๋๋ค. ๊ทธ๋ํ๋ ๋ฐ๋ฏํ ์ค๋ฆฌ์ ํ๋ก๊ทธ๋จ, ๋ฒ ์ด์์ ๋คํธ์ํฌ, ๋๋ ์ ๊ธฐ ํ๋ก์ ๊ฐ์ ๊ฒ์ ํํํ ์ ์๋ค. (Flux์ ๊ฐ์) DataFlow๋ฅผ ์ฌ์ฉํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋ฌธ๋ฒ์ ํ์ฅํ ์ ์๊ธฐ๋ฅผ ์ํ๋ฉฐ ์์ฉ ํ๋ก๊ทธ๋จ์ ์ ์ ํ ์ฝ๋๋ฅผ ์์ฑ์ํค๋ ๊ทธ๋ํ๋ฅผ ๋ค๋ฃฐ ๊ฒ์ด๋ค.
๋ฐ์ดํฐ ๊ตฌ์กฐ
DataFlow๋ DVertex
์ IVertex
๋ผ๋ ๋ ๊ฐ์ ๋ฐ์ดํฐ ๊ตฌ์กฐ๊ฐ ์๋ค. ๋์ ๊ทธ๋ํ์ ๋
ธ๋(nodes)๊ฐ ๋ค๋ฅธ ๋
ธ๋์ ์
๋ ฅ/์ถ๋ ฅ(inputs/outputs)์ด ์ด๋์/์ด๋์์(to/from) ์ด๋ค์ง ๊ฒ์ธ์ง ํํํ๋ค. IVertex
๋ ๋งํฌ๋ ๋ฆฌ์คํธ(a linked list) ์ฒ๋ผ ์
๋ ฅ-์ฐ๊ฒฐ(input-linked) ์ด๋ค - ์
๋ ฅ์ผ๋ก ์ฌ์ฉ๋๋ ๋
ธ๋์ ๋ํ ์ฐธ์กฐ(a reference)๋ฅผ ์ ์งํ๋ค. DVertex
๋ ์ด์ค์ผ๋ก ์ฐ๊ฒฐํ ๊ฒ์ผ๋ก ๋๋ธ-๋งํฌ๋ ๋ฆฌ์คํธ(doubly-linked list)์ ํด๋นํ๋ค - ์
๋ ฅ๊ณผ ์ด๊ฒ์ ์
๋ ฅ์ผ๋ก ๊ฐ๋ ๋ชจ๋ ๋
ธ๋๋ฅผ ์ฐธ์กฐํ๋ค. DVertex๋ ๊ธฐ์ ์ ์ผ๋ก ํํ๋ ฅ์ด ๋ ์ข์ง๋ง ์์
ํ๊ธฐ๋ ๋ ๋นก์ธ๋๊น, ๊ฐ๋ฅํ๋ค๋ฉด ์
๋ ฅ-์ฐ๊ฒฐ(input-linked)๋ก ๋ฐ๊ฟ์ ์ฐ๋๊ฒ ์ต์ ์ด๋ค (DataFlow.il()
๋ก ํ ์ ์๋ค).
# src/graph/graph.jl
abstract type Vertex{T} end
# src/graph/ilgraph.jl
struct IVertex{T} <: Vertex{T}
value::T
inputs::Vector{IVertex{T}}
# outputs::Set{IVertex{T}} # DVertex๋ ์๊ฑธ ์ถ๊ฐ
end
IVertex
๋ ์ค๋ฆฌ์์ Expr
๊ฐ์ฒด์ ์ ์ฌํ๋ค. ์๋ฅผ ๋ค์ด, ๋ค์๊ณผ ๊ฐ์ด ํํ์ x+length(xs)
๋ฅผ ๋น์ทํ ๋ฐฉ์์ผ๋ก์ ์ ์ฅํ๋ค.
julia> using DataFlow
julia> x = 2
2
julia> Expr(:call, :+, x, Expr(:call, :length, :xs))
:(2 + length(xs))
julia> IVertex(:+, IVertex(:x), IVertex(:length, IVertex(:xs)))
IVertex{Symbol}(x() + length(xs()))
์ฃผ์ํ ์ฐจ์ด๋ ๊ฐ์ฒด ์์ด๋ดํฐํฐ(object identity)๊ฐ DataFlow ๊ทธ๋ํ์์๋ ์ค์ํ๋ค๋ ๊ฒ์ด๋ค. ๋ค์๊ณผ ๊ฐ์ด ๊ตฌ๋ฌธ ํํ ํธ๋ฆฌ(an expression tree)๋ฅผ ๋ง๋ค๋ฉด:
julia> foo = Expr(:call, :length, :xs)
:(length(xs))
julia> Expr(:call, :+, foo, foo)
:(length(xs) + length(xs))
length(xs)
ํํ์์ ์ฌ์ฌ์ฉ ํ์์๋ length(xs)+length(xs)
๋ฅผ ์ถ๋ ฅํ๋ค. DataFlow์ ์ฌ์ฌ์ฉ์ ํฐ ์ฐจ์ด๊ฐ ์๋ค:
julia> g = IVertex{Any}
IVertex
julia> g(:+, g(:length, g(:xs)), g(:length, g(:xs)))
IVertex(length(xs()) + length(xs()))
julia> foo = g(:length, g(:xs))
IVertex(length(xs()))
julia> g(:+, foo, foo)
IVertex(
eland = length(xs())
eland + eland)
์ฌ์ฌ์ฉ ํ ๊ฒ์ด ํ๋ก๊ทธ๋จ ๊ทธ๋ํ์ ์ธ์ฝ๋ ๋์๋ค. ์์ ๋ฐ์ดํฐ ๊ตฌ์กฐ์์๋ "๋ณ์" ๊ฐ๋ ์ด ์๋๋ฐ ๋ฐ์ดํฐ์ ํ๋ฆ์ด ์ง์ ํํ๋์๊ธฐ ๋๋ฌธ์ด๋ค; ๋์ ๋ฌธ๋ฒ ๋ณํ์์ ๋ณ์๊ฐ ํ์ํด์ง๋ฉด ๊ทธ ๋ ์์ฑ๋ ๊ฒ์ด๋ค.
์๊ณ ๋ฆฌ์ฆ
DataFlow ๊ทธ๋ํ๋ฅผ ๋ค๋ฃจ๋ ๊ธฐ๋ณธ ์ ๊ทผ ๋ฐฉ์์ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์์ ํธ๋ฆฌ(tree)๋ฅผ ๋ค๋ฃจ๋ ํ
ํฌ๋๊ณผ ๊ฐ์ ๊ฒ์ ์ฌ์ฉํ๋ค. ๊ทธ๋ฌ๋๊น, ์ฌ๊ท์ ์ผ๋ก(recursively) ์ด์ ๊ฒ์ ๋ฐ์๋๊ฐ๋ฉฐ ์๋ก์ด ๊ทธ๋ํ๋ฅผ ์์ฑํ๋ ์๊ณ ๋ฆฌ์ฆ์ ๋ง๋ค๋๋ก ํ์. ๊ทธ๋ํ์ ์๋ ๊ฐ ๋
ธ๋์ ํน์ ํจ์๋ฅผ ์ ์ฉ(apply) ์ํค๋ ํจ์๋ก์, prewalk
์ postwalk
๊ฐ์๊ฒ ํจํค์ง์ ๋ค์ด ์๋ค.
์๋ฅผ ๋ณด์:
julia> using DataFlow: postwalk, value
julia> foo = g(:+, g(:length, g(:xs)), g(:length, g(:ys)))
IVertex(length(xs()) + length(ys()))
julia> postwalk(foo) do v
value(v) == :length && value(v[1]) == :xs ? g(:lx) : v
end
IVertex(lx() + length(ys()))
(pre
- ์ postwalk
์ ์ฐจ์ด๋ ์ํ(traversal)ํ๋ ์์์ ์๋ค. @show
๋ฅผ ํตํด์ ๋ณผ ์ ์๋ค.) ์ด ๋ฐฉ๋ฒ์ผ๋ก ๊ทธ๋ํ์์ ์ฐพ๊ธฐ(find), ๋ฐ๊พธ๊ธฐ(replace) ๊ฐ์ ๊ฒ์ ํ๊ฑฐ๋, ๋์ฑ ๋ณต์กํ ๊ตฌ์กฐ ๋ณํ์ ์ ์ฉํ ์ ์๋ค. ๊ทธ๋ผ ์ด์ ๊ณตํต ๋ถ๋ถ ํํ์ ์ ๊ฑฐ(cse, common subexpression elimination)๋ฅผ ํ๋ ๊ธฐ๋ณธ์ ์ธ ๋ฐฉ๋ฒ์ ์์ฐ ์ค์ ํฐ๋ํ์ผ๋ ํ๋ฒ ๊ตฌํํด ๋ณด์:
julia> cse(v::IVertex, cache = Dict()) =
postwalk(x -> get!(cache, x, x), v)
cse (generic function with 2 methods)
(์ญ์ฃผ: get!
์ ์ฌ์ (Dict)์ ์๋ ํค๋ฅผ ์ ์ฅํ๋ค.)
julia> d = Dict("a"=>1, "b"=>2, "c"=>3);
julia> get!(d, "a", 5)
1
julia> get!(d, "d", 4)
4
julia> d
Dict{String,Int64} with 4 entries:
"c" => 3
"b" => 2
"a" => 1
"d" => 4
๊ทธ๋ํ์ ๊ฐ ๋
ธ๋๊ฐ ์ฌ์ (Dict) ํ์
์ ๋์ด๋ค์ด๊ณ ๊ฐ๋ค์ ์๊ธฐ ์์ ์ ์ฐธ์กฐ(refer) ํ๋ค. ์ด๊ฒ์ผ๋ก ๊ฒฐ๊ณผ ๊ทธ๋ํ์ ์ด๋ ๊ฐ์ด๋ ==
๋ ===
์ ๋ง์ฐฌ๊ฐ์ง์ธ๊ฒ (===
๋ identical ๋น๊ต) ํ์คํด์ง๋ฉฐ, ๊ณตํต ํํ์์ ์ฌ์ฌ์ฉ๋๋ค.
julia> foo = @flow length(xs)+length(xs)
IVertex(length(xs) + length(xs))
julia> cse(foo)
IVertex(
eland = length(xs)
eland + eland)
์ผ๋ฐ์ ์ผ๋ก DataFlow์ postwalk
์ ๊ฐ์ ๊ณ ๊ธ ์ฐ์ฐ์ ๋ฅ์ํด์ผ ํ์ง๋ง, ์ด๋ค ๊ฒฝ์ฐ์๋ ์ฒ์๋ถํฐ ์ฌ๊ท ์๊ณ ๋ฆฌ์ฆ์ ์ง์ ์ง์ผ ํ ๋๋ ์๋ค. ํธ๋ฆฌ(a tree)์ ์ ์ฉํ๋ ์๊ณ ๋ฆฌ์ฆ๊ณผ ๊ฐ์ ๋ณด์ด์ง๋ง ์ฃผ์ ์ฌํญ์ด ์๋๋ฐ (1) ๋์ผํ ๋
ธ๋(identical nodes)๊ฐ ํธ๋ฆฌ์์ ์ฌ๋ฌ ๋ฒ ๋๋ฌํ ์ ์๋ค. (2) ์ฌ๊ทํ๋ค ๊ทธ๋ํ์์ ์ฌ์ดํด(cyle)์ด ๋ฐ์ํ์ฌ ๋ฌดํ ๋ฃจํ์ ๋น ์ง ์ ์๋ค.
์๋ชปํ๋ฉด ์ ๋ชฝ์ฒ๋ผ ๋ณด์ด์ง๋ง ์ฌ์ค์ ์ผ์์ด์กฐ์ธ ๊ฒ์ด; ํจ์๋ฅผ memoize ํ์ฌ ๋ ธ๋๋ฅผ ๋ฐ๋ณตํด์ ๋ฐฉ๋ฌธํ๋ฉด ์ฌ๊ท๋ฅผ ๋๋ด๊ฒ ํ์. ๊ทธ๋ฆฌ๊ณ ์ฌ๊ทํ๊ธฐ ์ ์๋ ํ์ฌ ํธ์ถ์ ๊ฒฐ๊ณผ๋ฅผ ๊ผญ ์บ์(cache) ํ๋๋ก ํ๋ค.
julia> using DataFlow: value, inputs, thread!
julia> function replace_xs(g, cache = ObjectIdDict())
# ์ด ๋
ธ๋๋ฅผ ์ด๋ฏธ ์ฒ๋ฆฌํ ๊ฒฝ์ฐ์๋ ๋น ๋ฅธ ์ข
๋ฃ
haskey(cache, g) && return cache[g]
# ์๋ก์ด (๋น์ด์๋) ๋
ธ๋๋ฅผ ๋ง๋ค๊ณ ์บ์ ํด ๋
cache[g] = gโฒ = typeof(g)(value(g) == :xs ? :foo : value(g))
# ์๋ ๋
ธ๋์ ์
๋ ฅ์ ์ฒ๋ฆฌํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ์๋ก์ด ๋
ธ๋์ ์ถ๊ฐ
thread!(gโฒ, (replace_xs(v, cache) for v in inputs(g))...)
end
replace_xs (generic function with 2 methods)
julia> foo = DataFlow.cse(@flow length(xs)+length(xs))
IVertex(
alligator = length(xs)
alligator + alligator)
julia> replace_xs(foo)
IVertex(
alligator = length(xs)
alligator + alligator)
์ฌ๊ธฐ์๋ ์บ์ํ๋ ๊ฒ์ ์์ด๋ length(foo)+length(foo)
ํ๋ค ๋งํ์ง ์๋๋ฐ, ๋ค๋ฅธ ๊ฒฝ์ฐ์๋ ๋์ค์ ๋ฉ์ถ ์ ์๋ค.
๐ฆ ๋ฒ์ญ ์๋ฃ 2018-03-15