Merge pull request 'Refactor' (#1) from refactoring into main
Reviewed-on: Rubydragon/MetagraphOptimization.jl#1
This commit is contained in:
commit
1d0511ecb7
@ -28,3 +28,6 @@ jobs:
|
||||
|
||||
- name: Run tests
|
||||
run: julia --project -e 'import Pkg; Pkg.test()'
|
||||
|
||||
- name: Run examples
|
||||
run: julia --project=examples/ -e 'import Pkg; Pkg.develop(Pkg.PackageSpec(path=pwd())); Pkg.instantiate(); include("examples/import_bench.jl")'
|
||||
|
@ -4,7 +4,6 @@ authors = ["Anton Reinhard <anton.reinhard@proton.me>"]
|
||||
version = "0.1.0"
|
||||
|
||||
[deps]
|
||||
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
|
||||
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
|
||||
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
|
||||
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
||||
|
63
README.md
63
README.md
@ -2,7 +2,31 @@
|
||||
|
||||
Directed Acyclic Graph optimization for QED
|
||||
|
||||
## Generate Operations from chains
|
||||
## Usage
|
||||
|
||||
Instantiate the project first:
|
||||
|
||||
`julia --project -e 'import Pkg; Pkg.instantiate()'`
|
||||
|
||||
### Run Tests
|
||||
|
||||
To run all tests, run
|
||||
|
||||
`julia --project=. -e 'import Pkg; Pkg.test()'`
|
||||
|
||||
### Run Examples
|
||||
|
||||
Get the correct environment for the examples folder:
|
||||
|
||||
`julia --project=examples/ -e 'import Pkg; Pkg.develop(Pkg.PackageSpec(path=pwd())); Pkg.instantiate();'`
|
||||
|
||||
Then execute a specific example:
|
||||
|
||||
`julia --project=examples examples/<file>.jl`
|
||||
|
||||
## Concepts
|
||||
|
||||
### Generate Operations from chains
|
||||
|
||||
We assume we have a (valid) graph given. We can generate all initially possible graph operations from it, and we can calculate the graph properties like compute effort and total data transfer.
|
||||
|
||||
@ -33,7 +57,9 @@ Times are from my home machine: AMD Ryzen 7900X3D, 64GB DDR5 RAM @ 6000MHz
|
||||
$ julia --project examples/import_bench.jl
|
||||
AB->AB:
|
||||
Graph:
|
||||
Nodes: Total: 34, DataTask: 19, ComputeTaskP: 4, ComputeTaskS2: 2, ComputeTaskV: 4, ComputeTaskU: 4, ComputeTaskSum: 1
|
||||
Nodes: Total: 34, DataTask: 19, ComputeTaskP: 4,
|
||||
ComputeTaskS2: 2, ComputeTaskV: 4, ComputeTaskU: 4,
|
||||
ComputeTaskSum: 1
|
||||
Edges: 37
|
||||
Total Compute Effort: 185
|
||||
Total Data Transfer: 102
|
||||
@ -43,7 +69,9 @@ Graph:
|
||||
|
||||
AB->ABBB:
|
||||
Graph:
|
||||
Nodes: Total: 280, DataTask: 143, ComputeTaskP: 6, ComputeTaskS2: 24, ComputeTaskV: 64, ComputeTaskU: 6, ComputeTaskSum: 1, ComputeTaskS1: 36
|
||||
Nodes: Total: 280, DataTask: 143, ComputeTaskP: 6,
|
||||
ComputeTaskS2: 24, ComputeTaskV: 64, ComputeTaskU: 6,
|
||||
ComputeTaskSum: 1, ComputeTaskS1: 36
|
||||
Edges: 385
|
||||
Total Compute Effort: 2007
|
||||
Total Data Transfer: 828
|
||||
@ -53,7 +81,9 @@ Graph:
|
||||
|
||||
AB->ABBBBB:
|
||||
Graph:
|
||||
Nodes: Total: 7854, DataTask: 3931, ComputeTaskP: 8, ComputeTaskS2: 720, ComputeTaskV: 1956, ComputeTaskU: 8, ComputeTaskSum: 1, ComputeTaskS1: 1230
|
||||
Nodes: Total: 7854, DataTask: 3931, ComputeTaskP: 8,
|
||||
ComputeTaskS2: 720, ComputeTaskV: 1956, ComputeTaskU: 8,
|
||||
ComputeTaskSum: 1, ComputeTaskS1: 1230
|
||||
Edges: 11241
|
||||
Total Compute Effort: 58789
|
||||
Total Data Transfer: 23244
|
||||
@ -63,7 +93,9 @@ Graph:
|
||||
|
||||
AB->ABBBBBBB:
|
||||
Graph:
|
||||
Nodes: Total: 438436, DataTask: 219223, ComputeTaskP: 10, ComputeTaskS2: 40320, ComputeTaskV: 109600, ComputeTaskU: 10, ComputeTaskSum: 1, ComputeTaskS1: 69272
|
||||
Nodes: Total: 438436, DataTask: 219223, ComputeTaskP: 10,
|
||||
ComputeTaskS2: 40320, ComputeTaskV: 109600, ComputeTaskU: 10,
|
||||
ComputeTaskSum: 1, ComputeTaskS1: 69272
|
||||
Edges: 628665
|
||||
Total Compute Effort: 3288131
|
||||
Total Data Transfer: 1297700
|
||||
@ -71,9 +103,20 @@ Graph:
|
||||
Graph size in memory: 118.4037 MiB
|
||||
463.082 ms (7645256 allocations: 422.57 MiB)
|
||||
|
||||
AB->ABBBBBBBBB:
|
||||
Graph:
|
||||
Nodes: Total: 39456442, DataTask: 19728227, ComputeTaskS1: 6235290, ComputeTaskP: 12, ComputeTaskU: 12, ComputeTaskV: 9864100, ComputeTaskS2: 3628800, ComputeTaskSum: 1
|
||||
Edges: 56578129
|
||||
Total Compute Effort: 295923153
|
||||
Total Data Transfer: 175407750
|
||||
Total Compute Intensity: 1.6870585991782006
|
||||
Graph size in memory: 12.7845 GiB
|
||||
|
||||
ABAB->ABAB:
|
||||
Graph:
|
||||
Nodes: Total: 3218, DataTask: 1613, ComputeTaskP: 8, ComputeTaskS2: 288, ComputeTaskV: 796, ComputeTaskU: 8, ComputeTaskSum: 1, ComputeTaskS1: 504
|
||||
Nodes: Total: 3218, DataTask: 1613, ComputeTaskP: 8,
|
||||
ComputeTaskS2: 288, ComputeTaskV: 796, ComputeTaskU: 8,
|
||||
ComputeTaskSum: 1, ComputeTaskS1: 504
|
||||
Edges: 4581
|
||||
Total Compute Effort: 24009
|
||||
Total Data Transfer: 9494
|
||||
@ -83,11 +126,15 @@ Graph:
|
||||
|
||||
ABAB->ABC:
|
||||
Graph:
|
||||
Nodes: Total: 817, DataTask: 412, ComputeTaskP: 7, ComputeTaskS2: 72, ComputeTaskV: 198, ComputeTaskU: 7, ComputeTaskSum: 1, ComputeTaskS1: 120
|
||||
Nodes: Total: 817, DataTask: 412, ComputeTaskP: 7,
|
||||
ComputeTaskS2: 72, ComputeTaskV: 198, ComputeTaskU: 7,
|
||||
ComputeTaskSum: 1, ComputeTaskS1: 120
|
||||
Edges: 1151
|
||||
Total Compute Effort: 6028
|
||||
Total Data Transfer: 2411
|
||||
Total Compute Intensity: 2.5002073828287017
|
||||
Graph size in memory: 225.0625 KiB
|
||||
286.583 μs (13996 allocations: 804.48 KiB)
|
||||
```
|
||||
```
|
||||
|
||||
|
||||
|
@ -1,103 +0,0 @@
|
||||
using metagraph_optimization
|
||||
|
||||
function main()
|
||||
graph = DAG()
|
||||
|
||||
# s to output (exit node)
|
||||
d_exit = insert_node(graph, make_node(DataTask(10)))
|
||||
|
||||
# final s compute
|
||||
s0 = insert_node(graph, make_node(ComputeTaskS2()))
|
||||
|
||||
# data from v0 and v1 to s0
|
||||
d_v0_s0 = insert_node(graph, make_node(DataTask(5)))
|
||||
d_v1_s0 = insert_node(graph, make_node(DataTask(5)))
|
||||
|
||||
# v0 and v1 compute
|
||||
v0 = insert_node(graph, make_node(ComputeTaskV()))
|
||||
v1 = insert_node(graph, make_node(ComputeTaskV()))
|
||||
|
||||
# data from uB, uA, uBp and uAp to v0 and v1
|
||||
d_uB_v0 = insert_node(graph, make_node(DataTask(3)))
|
||||
d_uA_v0 = insert_node(graph, make_node(DataTask(3)))
|
||||
d_uBp_v1 = insert_node(graph, make_node(DataTask(3)))
|
||||
d_uAp_v1 = insert_node(graph, make_node(DataTask(3)))
|
||||
|
||||
# uB, uA, uBp and uAp computes
|
||||
uB = insert_node(graph, make_node(ComputeTaskU()))
|
||||
uA = insert_node(graph, make_node(ComputeTaskU()))
|
||||
uBp = insert_node(graph, make_node(ComputeTaskU()))
|
||||
uAp = insert_node(graph, make_node(ComputeTaskU()))
|
||||
|
||||
# data from PB, PA, PBp and PAp to uB, uA, uBp and uAp
|
||||
d_PB_uB = insert_node(graph, make_node(DataTask(6)))
|
||||
d_PA_uA = insert_node(graph, make_node(DataTask(6)))
|
||||
d_PBp_uBp = insert_node(graph, make_node(DataTask(6)))
|
||||
d_PAp_uAp = insert_node(graph, make_node(DataTask(6)))
|
||||
|
||||
# P computes PB, PA, PBp and PAp
|
||||
PB = insert_node(graph, make_node(ComputeTaskP()))
|
||||
PA = insert_node(graph, make_node(ComputeTaskP()))
|
||||
PBp = insert_node(graph, make_node(ComputeTaskP()))
|
||||
PAp = insert_node(graph, make_node(ComputeTaskP()))
|
||||
|
||||
# entry nodes getting data for P computes
|
||||
d_PB = insert_node(graph, make_node(DataTask(4)))
|
||||
d_PA = insert_node(graph, make_node(DataTask(4)))
|
||||
d_PBp = insert_node(graph, make_node(DataTask(4)))
|
||||
d_PAp = insert_node(graph, make_node(DataTask(4)))
|
||||
|
||||
# now for all the edgese
|
||||
insert_edge(graph, make_edge(d_PB, PB))
|
||||
insert_edge(graph, make_edge(d_PA, PA))
|
||||
insert_edge(graph, make_edge(d_PBp, PBp))
|
||||
insert_edge(graph, make_edge(d_PAp, PAp))
|
||||
|
||||
insert_edge(graph, make_edge(PB, d_PB_uB))
|
||||
insert_edge(graph, make_edge(PA, d_PA_uA))
|
||||
insert_edge(graph, make_edge(PBp, d_PBp_uBp))
|
||||
insert_edge(graph, make_edge(PAp, d_PAp_uAp))
|
||||
|
||||
insert_edge(graph, make_edge(d_PB_uB, uB))
|
||||
insert_edge(graph, make_edge(d_PA_uA, uA))
|
||||
insert_edge(graph, make_edge(d_PBp_uBp, uBp))
|
||||
insert_edge(graph, make_edge(d_PAp_uAp, uAp))
|
||||
|
||||
insert_edge(graph, make_edge(uB, d_uB_v0))
|
||||
insert_edge(graph, make_edge(uA, d_uA_v0))
|
||||
insert_edge(graph, make_edge(uBp, d_uBp_v1))
|
||||
insert_edge(graph, make_edge(uAp, d_uAp_v1))
|
||||
|
||||
insert_edge(graph, make_edge(d_uB_v0, v0))
|
||||
insert_edge(graph, make_edge(d_uA_v0, v0))
|
||||
insert_edge(graph, make_edge(d_uBp_v1, v1))
|
||||
insert_edge(graph, make_edge(d_uAp_v1, v1))
|
||||
|
||||
insert_edge(graph, make_edge(v0, d_v0_s0))
|
||||
insert_edge(graph, make_edge(v1, d_v1_s0))
|
||||
|
||||
insert_edge(graph, make_edge(d_v0_s0, s0))
|
||||
insert_edge(graph, make_edge(d_v1_s0, s0))
|
||||
|
||||
insert_edge(graph, make_edge(s0, d_exit))
|
||||
|
||||
print(graph)
|
||||
|
||||
println("Available optimizations from here:")
|
||||
println(generate_options(graph))
|
||||
|
||||
# fuse some things
|
||||
fuse1 = node_fusion(graph, PB, d_PB_uB, uB)
|
||||
fuse2 = node_fusion(graph, PA, d_PA_uA, uA)
|
||||
fuse3 = node_fusion(graph, PBp, d_PBp_uBp, uBp)
|
||||
fuse4 = node_fusion(graph, PAp, d_PAp_uAp, uAp)
|
||||
|
||||
# on this graph, nothing can be reduced
|
||||
println("Same graph after node fusion")
|
||||
print(graph)
|
||||
|
||||
println("Available optimizations from here:")
|
||||
println(generate_options(graph))
|
||||
end
|
||||
|
||||
main()
|
5
examples/Project.toml
Normal file
5
examples/Project.toml
Normal file
@ -0,0 +1,5 @@
|
||||
[deps]
|
||||
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
|
||||
MetagraphOptimization = "3e869610-d48d-4942-ba70-c1b702a33ca4"
|
||||
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
|
||||
ProfileView = "c46f51b8-102a-5cf2-8d2c-8597cb0e0da7"
|
@ -8,14 +8,18 @@ function bench_txt(filepath::String, bench::Bool = true)
|
||||
name, _ = splitext(name)
|
||||
|
||||
filepath = joinpath(@__DIR__, filepath)
|
||||
if !isfile(filepath)
|
||||
println("File ", filepath, " does not exist, skipping bench")
|
||||
return
|
||||
end
|
||||
|
||||
println(name, ":")
|
||||
g = import_txt(filepath)
|
||||
g = parse_abc(filepath)
|
||||
print(g)
|
||||
println(" Graph size in memory: ", bytes_to_human_readable(Base.summarysize(g)))
|
||||
|
||||
if (bench)
|
||||
@btime import_txt($filepath)
|
||||
@btime parse_abc($filepath)
|
||||
println()
|
||||
end
|
||||
end
|
||||
@ -25,7 +29,7 @@ function import_bench()
|
||||
bench_txt("AB->ABBB.txt")
|
||||
bench_txt("AB->ABBBBB.txt")
|
||||
bench_txt("AB->ABBBBBBB.txt")
|
||||
bench_txt("AB->ABBBBBBBBB.txt", false)
|
||||
#bench_txt("AB->ABBBBBBBBB.txt", false)
|
||||
bench_txt("ABAB->ABAB.txt")
|
||||
bench_txt("ABAB->ABC.txt")
|
||||
end
|
||||
|
60
examples/plot_chain.jl
Normal file
60
examples/plot_chain.jl
Normal file
@ -0,0 +1,60 @@
|
||||
using MetagraphOptimization
|
||||
using Plots
|
||||
using Random
|
||||
|
||||
function gen_plot(filepath)
|
||||
name = basename(filepath)
|
||||
name, _ = splitext(name)
|
||||
|
||||
filepath = joinpath(@__DIR__, filepath)
|
||||
if !isfile(filepath)
|
||||
println("File ", filepath, " does not exist, skipping")
|
||||
return
|
||||
end
|
||||
|
||||
g = parse_abc(filepath)
|
||||
|
||||
Random.seed!(1)
|
||||
|
||||
println("Random Walking... ")
|
||||
|
||||
x = Vector{Float64}()
|
||||
y = Vector{Float64}()
|
||||
|
||||
for i = 1:30
|
||||
print("\r", i)
|
||||
# push
|
||||
opt = get_operations(g)
|
||||
|
||||
# choose one of fuse/split/reduce
|
||||
option = rand(1:3)
|
||||
if option == 1 && !isempty(opt.nodeFusions)
|
||||
push_operation!(g, rand(collect(opt.nodeFusions)))
|
||||
println("NF")
|
||||
elseif option == 2 && !isempty(opt.nodeReductions)
|
||||
push_operation!(g, rand(collect(opt.nodeReductions)))
|
||||
println("NR")
|
||||
elseif option == 3 && !isempty(opt.nodeSplits)
|
||||
push_operation!(g, rand(collect(opt.nodeSplits)))
|
||||
println("NS")
|
||||
else
|
||||
i = i-1
|
||||
end
|
||||
|
||||
props = graph_properties(g)
|
||||
push!(x, props.data)
|
||||
push!(y, props.compute_effort)
|
||||
end
|
||||
|
||||
println("\rDone.")
|
||||
|
||||
plot([x[1], x[2]], [y[1], y[2]], linestyle = :solid, linewidth = 1, color = :red, legend=false)
|
||||
# Create lines connecting the reference point to each data point
|
||||
for i in 3:length(x)
|
||||
plot!([x[i-1], x[i]], [y[i-1], y[i]], linestyle = :solid, linewidth = 1, color = :red)
|
||||
end
|
||||
|
||||
gui()
|
||||
end
|
||||
|
||||
gen_plot("AB->ABBB.txt")
|
96
examples/plot_star.jl
Normal file
96
examples/plot_star.jl
Normal file
@ -0,0 +1,96 @@
|
||||
using MetagraphOptimization
|
||||
using Plots
|
||||
using Random
|
||||
|
||||
function gen_plot(filepath)
|
||||
name = basename(filepath)
|
||||
name, _ = splitext(name)
|
||||
|
||||
filepath = joinpath(@__DIR__, filepath)
|
||||
if !isfile(filepath)
|
||||
println("File ", filepath, " does not exist, skipping")
|
||||
return
|
||||
end
|
||||
|
||||
g = parse_abc(filepath)
|
||||
|
||||
Random.seed!(1)
|
||||
|
||||
println("Random Walking... ")
|
||||
|
||||
for i = 1:30
|
||||
print("\r", i)
|
||||
# push
|
||||
opt = get_operations(g)
|
||||
|
||||
# choose one of fuse/split/reduce
|
||||
option = rand(1:3)
|
||||
if option == 1 && !isempty(opt.nodeFusions)
|
||||
push_operation!(g, rand(collect(opt.nodeFusions)))
|
||||
println("NF")
|
||||
elseif option == 2 && !isempty(opt.nodeReductions)
|
||||
push_operation!(g, rand(collect(opt.nodeReductions)))
|
||||
println("NR")
|
||||
elseif option == 3 && !isempty(opt.nodeSplits)
|
||||
push_operation!(g, rand(collect(opt.nodeSplits)))
|
||||
println("NS")
|
||||
else
|
||||
i = i-1
|
||||
end
|
||||
end
|
||||
|
||||
println("\rDone.")
|
||||
|
||||
|
||||
|
||||
|
||||
props = graph_properties(g)
|
||||
x0 = props.data
|
||||
y0 = props.compute_effort
|
||||
|
||||
x = Vector{Float64}()
|
||||
y = Vector{Float64}()
|
||||
names = Vector{String}()
|
||||
|
||||
opt = get_operations(g)
|
||||
for op in opt.nodeFusions
|
||||
push_operation!(g, op)
|
||||
props = graph_properties(g)
|
||||
push!(x, props.data)
|
||||
push!(y, props.compute_effort)
|
||||
pop_operation!(g)
|
||||
|
||||
push!(names, "NF: (" * string(props.data) * ", " * string(props.compute_effort) * ")")
|
||||
end
|
||||
for op in opt.nodeReductions
|
||||
push_operation!(g, op)
|
||||
props = graph_properties(g)
|
||||
push!(x, props.data)
|
||||
push!(y, props.compute_effort)
|
||||
pop_operation!(g)
|
||||
|
||||
push!(names, "NR: (" * string(props.data) * ", " * string(props.compute_effort) * ")")
|
||||
end
|
||||
for op in opt.nodeSplits
|
||||
push_operation!(g, op)
|
||||
props = graph_properties(g)
|
||||
push!(x, props.data)
|
||||
push!(y, props.compute_effort)
|
||||
pop_operation!(g)
|
||||
|
||||
push!(names, "NS: (" * string(props.data) * ", " * string(props.compute_effort) * ")")
|
||||
end
|
||||
|
||||
plot([x0, x[1]], [y0, y[1]], linestyle = :solid, linewidth = 1, color = :red, legend=false)
|
||||
# Create lines connecting the reference point to each data point
|
||||
for i in 2:length(x)
|
||||
plot!([x0, x[i]], [y0, y[i]], linestyle = :solid, linewidth = 1, color = :red)
|
||||
end
|
||||
#scatter!(x, y, label=names)
|
||||
|
||||
print(names)
|
||||
|
||||
gui()
|
||||
end
|
||||
|
||||
gen_plot("AB->ABBB.txt")
|
36
examples/profiling_utilities.jl
Normal file
36
examples/profiling_utilities.jl
Normal file
@ -0,0 +1,36 @@
|
||||
|
||||
function test_random_walk(g::DAG, n::Int64)
|
||||
# the purpose here is to do "random" operations and reverse them again and validate that the graph stays the same and doesn't diverge
|
||||
reset_graph!(g)
|
||||
|
||||
properties = graph_properties(g)
|
||||
|
||||
for i = 1:n
|
||||
# choose push or pop
|
||||
if rand(Bool)
|
||||
# push
|
||||
opt = get_operations(g)
|
||||
|
||||
# choose one of fuse/split/reduce
|
||||
option = rand(1:3)
|
||||
if option == 1 && !isempty(opt.nodeFusions)
|
||||
push_operation!(g, rand(collect(opt.nodeFusions)))
|
||||
elseif option == 2 && !isempty(opt.nodeReductions)
|
||||
push_operation!(g, rand(collect(opt.nodeReductions)))
|
||||
elseif option == 3 && !isempty(opt.nodeSplits)
|
||||
push_operation!(g, rand(collect(opt.nodeSplits)))
|
||||
else
|
||||
i = i - 1
|
||||
end
|
||||
else
|
||||
# pop
|
||||
if (can_pop(g))
|
||||
pop_operation!(g)
|
||||
else
|
||||
i = i - 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
reset_graph!(g)
|
||||
end
|
BIN
images/OperationChain.png
Normal file
BIN
images/OperationChain.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 85 KiB |
BIN
images/OperationEffects.png
Normal file
BIN
images/OperationEffects.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 105 KiB |
@ -1,26 +1,36 @@
|
||||
module MetagraphOptimization
|
||||
|
||||
export Node, Edge, ComputeTaskNode, DataTaskNode, DAG
|
||||
export AbstractTask, AbstractComputeTask, AbstractDataTask, DataTask, FusedComputeTask
|
||||
export make_node, make_edge, insert_node, insert_edge, is_entry_node, is_exit_node, parents, children, compute, graph_properties, get_exit_node, is_valid
|
||||
export NodeFusion, NodeReduction, NodeSplit, push_operation!, pop_operation!, can_pop, reset_graph!, get_operations
|
||||
export parse_abc, ComputeTaskP, ComputeTaskS1, ComputeTaskS2, ComputeTaskV, ComputeTaskU, ComputeTaskSum
|
||||
|
||||
export ==, in, show, isempty, delete!, length
|
||||
|
||||
export bytes_to_human_readable
|
||||
|
||||
import Base.length
|
||||
import Base.show
|
||||
import Base.==
|
||||
import Base.in
|
||||
import Base.copy
|
||||
import Base.isempty
|
||||
import Base.delete!
|
||||
|
||||
|
||||
include("tasks.jl")
|
||||
include("nodes.jl")
|
||||
include("graph.jl")
|
||||
|
||||
include("task_functions.jl")
|
||||
include("node_functions.jl")
|
||||
include("graph_functions.jl")
|
||||
include("graph_operations.jl")
|
||||
include("import.jl")
|
||||
include("utility.jl")
|
||||
|
||||
export Node, Edge, ComputeTaskNode, DataTaskNode, DAG
|
||||
export AbstractTask, AbstractComputeTask, AbstractDataTask, DataTask, ComputeTaskP, ComputeTaskS1, ComputeTaskS2, ComputeTaskV, ComputeTaskU, ComputeTaskSum, FusedComputeTask
|
||||
export make_node, make_edge, insert_node, insert_edge, is_entry_node, is_exit_node, parents, children, compute, graph_properties, get_exit_node, is_valid
|
||||
export node_fusion, node_reduction, node_split, generate_options
|
||||
export import_txt
|
||||
|
||||
export ==, in, show
|
||||
|
||||
export bytes_to_human_readable
|
||||
include("abc_model/tasks.jl")
|
||||
include("abc_model/task_functions.jl")
|
||||
include("abc_model/parse.jl")
|
||||
|
||||
end # module MetagraphOptimization
|
||||
|
@ -20,7 +20,8 @@ function parse_edges(input::AbstractString)
|
||||
return output
|
||||
end
|
||||
|
||||
function import_txt(filename::String, verbose::Bool = isinteractive())
|
||||
# reads an abc-model process from the given file
|
||||
function parse_abc(filename::String, verbose::Bool = false)
|
||||
file = open(filename, "r")
|
||||
|
||||
if (verbose) println("Opened file") end
|
||||
@ -39,9 +40,9 @@ function import_txt(filename::String, verbose::Bool = isinteractive())
|
||||
if (verbose) println("Estimating ", estimate_no_nodes, " Nodes") end
|
||||
sizehint!(graph.nodes, estimate_no_nodes)
|
||||
|
||||
sum_node = insert_node(graph, make_node(ComputeTaskSum()))
|
||||
global_data_out = insert_node(graph, make_node(DataTask(10)))
|
||||
insert_edge(graph, make_edge(sum_node, global_data_out))
|
||||
sum_node = insert_node!(graph, make_node(ComputeTaskSum()), false)
|
||||
global_data_out = insert_node!(graph, make_node(DataTask(10)), false)
|
||||
insert_edge!(graph, make_edge(sum_node, global_data_out), false)
|
||||
|
||||
# remember the data out nodes for connection
|
||||
dataOutNodes = Dict()
|
||||
@ -57,16 +58,16 @@ function import_txt(filename::String, verbose::Bool = isinteractive())
|
||||
end
|
||||
if occursin(regex_a, node)
|
||||
# add nodes and edges for the state reading to u(P(Particle))
|
||||
data_in = insert_node(graph, make_node(DataTask(4))) # read particle data node
|
||||
compute_P = insert_node(graph, make_node(ComputeTaskP())) # compute P node
|
||||
data_Pu = insert_node(graph, make_node(DataTask(6))) # transfer data from P to u
|
||||
compute_u = insert_node(graph, make_node(ComputeTaskU())) # compute U node
|
||||
data_out = insert_node(graph, make_node(DataTask(3))) # transfer data out from u
|
||||
data_in = insert_node!(graph, make_node(DataTask(4)), false) # read particle data node
|
||||
compute_P = insert_node!(graph, make_node(ComputeTaskP()), false) # compute P node
|
||||
data_Pu = insert_node!(graph, make_node(DataTask(6)), false) # transfer data from P to u
|
||||
compute_u = insert_node!(graph, make_node(ComputeTaskU()), false) # compute U node
|
||||
data_out = insert_node!(graph, make_node(DataTask(3)), false) # transfer data out from u
|
||||
|
||||
insert_edge(graph, make_edge(data_in, compute_P))
|
||||
insert_edge(graph, make_edge(compute_P, data_Pu))
|
||||
insert_edge(graph, make_edge(data_Pu, compute_u))
|
||||
insert_edge(graph, make_edge(compute_u, data_out))
|
||||
insert_edge!(graph, make_edge(data_in, compute_P), false)
|
||||
insert_edge!(graph, make_edge(compute_P, data_Pu), false)
|
||||
insert_edge!(graph, make_edge(data_Pu, compute_u), false)
|
||||
insert_edge!(graph, make_edge(compute_u, data_out), false)
|
||||
|
||||
# remember the data_out node for future edges
|
||||
dataOutNodes[node] = data_out
|
||||
@ -76,37 +77,37 @@ function import_txt(filename::String, verbose::Bool = isinteractive())
|
||||
in1 = capt.captures[1]
|
||||
in2 = capt.captures[2]
|
||||
|
||||
compute_v = insert_node(graph, make_node(ComputeTaskV()))
|
||||
data_out = insert_node(graph, make_node(DataTask(5)))
|
||||
compute_v = insert_node!(graph, make_node(ComputeTaskV()), false)
|
||||
data_out = insert_node!(graph, make_node(DataTask(5)), false)
|
||||
|
||||
if (occursin(regex_c, capt.captures[1]))
|
||||
# put an S node after this input
|
||||
compute_S = insert_node(graph, make_node(ComputeTaskS1()))
|
||||
data_S_v = insert_node(graph, make_node(DataTask(5)))
|
||||
compute_S = insert_node!(graph, make_node(ComputeTaskS1()), false)
|
||||
data_S_v = insert_node!(graph, make_node(DataTask(5)), false)
|
||||
|
||||
insert_edge(graph, make_edge(dataOutNodes[capt.captures[1]], compute_S))
|
||||
insert_edge(graph, make_edge(compute_S, data_S_v))
|
||||
insert_edge!(graph, make_edge(dataOutNodes[capt.captures[1]], compute_S), false)
|
||||
insert_edge!(graph, make_edge(compute_S, data_S_v), false)
|
||||
|
||||
insert_edge(graph, make_edge(data_S_v, compute_v))
|
||||
insert_edge!(graph, make_edge(data_S_v, compute_v), false)
|
||||
else
|
||||
insert_edge(graph, make_edge(dataOutNodes[capt.captures[1]], compute_v))
|
||||
insert_edge!(graph, make_edge(dataOutNodes[capt.captures[1]], compute_v), false)
|
||||
end
|
||||
|
||||
if (occursin(regex_c, capt.captures[2]))
|
||||
# i think the current generator only puts the combined particles in the first space, so this case might never be entered
|
||||
# put an S node after this input
|
||||
compute_S = insert_node(graph, make_node(ComputeTaskS1()))
|
||||
data_S_v = insert_node(graph, make_node(DataTask(5)))
|
||||
compute_S = insert_node!(graph, make_node(ComputeTaskS1()), false)
|
||||
data_S_v = insert_node!(graph, make_node(DataTask(5)), false)
|
||||
|
||||
insert_edge(graph, make_edge(dataOutNodes[capt.captures[2]], compute_S))
|
||||
insert_edge(graph, make_edge(compute_S, data_S_v))
|
||||
insert_edge!(graph, make_edge(dataOutNodes[capt.captures[2]], compute_S), false)
|
||||
insert_edge!(graph, make_edge(compute_S, data_S_v), false)
|
||||
|
||||
insert_edge(graph, make_edge(data_S_v, compute_v))
|
||||
insert_edge!(graph, make_edge(data_S_v, compute_v), false)
|
||||
else
|
||||
insert_edge(graph, make_edge(dataOutNodes[capt.captures[2]], compute_v))
|
||||
insert_edge!(graph, make_edge(dataOutNodes[capt.captures[2]], compute_v), false)
|
||||
end
|
||||
|
||||
insert_edge(graph, make_edge(compute_v, data_out))
|
||||
insert_edge!(graph, make_edge(compute_v, data_out), false)
|
||||
dataOutNodes[node] = data_out
|
||||
|
||||
elseif occursin(regex_m, node)
|
||||
@ -117,22 +118,22 @@ function import_txt(filename::String, verbose::Bool = isinteractive())
|
||||
in3 = capt.captures[3]
|
||||
|
||||
# in2 + in3 with a v
|
||||
compute_v = insert_node(graph, make_node(ComputeTaskV()))
|
||||
data_v = insert_node(graph, make_node(DataTask(5)))
|
||||
compute_v = insert_node!(graph, make_node(ComputeTaskV()), false)
|
||||
data_v = insert_node!(graph, make_node(DataTask(5)), false)
|
||||
|
||||
insert_edge(graph, make_edge(dataOutNodes[in2], compute_v))
|
||||
insert_edge(graph, make_edge(dataOutNodes[in3], compute_v))
|
||||
insert_edge(graph, make_edge(compute_v, data_v))
|
||||
insert_edge!(graph, make_edge(dataOutNodes[in2], compute_v), false)
|
||||
insert_edge!(graph, make_edge(dataOutNodes[in3], compute_v), false)
|
||||
insert_edge!(graph, make_edge(compute_v, data_v), false)
|
||||
|
||||
# combine with the v of the combined other input
|
||||
compute_S2 = insert_node(graph, make_node(ComputeTaskS2()))
|
||||
data_out = insert_node(graph, make_node(DataTask(10)))
|
||||
compute_S2 = insert_node!(graph, make_node(ComputeTaskS2()), false)
|
||||
data_out = insert_node!(graph, make_node(DataTask(10)), false)
|
||||
|
||||
insert_edge(graph, make_edge(data_v, compute_S2))
|
||||
insert_edge(graph, make_edge(dataOutNodes[in1], compute_S2))
|
||||
insert_edge(graph, make_edge(compute_S2, data_out))
|
||||
insert_edge!(graph, make_edge(data_v, compute_S2), false)
|
||||
insert_edge!(graph, make_edge(dataOutNodes[in1], compute_S2), false)
|
||||
insert_edge!(graph, make_edge(compute_S2, data_out), false)
|
||||
|
||||
insert_edge(graph, make_edge(data_out, sum_node))
|
||||
insert_edge!(graph, make_edge(data_out, sum_node), false)
|
||||
elseif occursin(regex_plus, node)
|
||||
if (verbose)
|
||||
println("\rReading Nodes Complete ")
|
21
src/abc_model/task_functions.jl
Normal file
21
src/abc_model/task_functions.jl
Normal file
@ -0,0 +1,21 @@
|
||||
# define compute_efforts tasks computation
|
||||
# put some "random" numbers here for now
|
||||
compute_effort(t::ComputeTaskS1) = 10
|
||||
compute_effort(t::ComputeTaskS2) = 10
|
||||
compute_effort(t::ComputeTaskU) = 6
|
||||
compute_effort(t::ComputeTaskV) = 20
|
||||
compute_effort(t::ComputeTaskP) = 15
|
||||
compute_effort(t::ComputeTaskSum) = 1
|
||||
|
||||
function show(io::IO, t::DataTask)
|
||||
print(io, "Data", t.data)
|
||||
end
|
||||
|
||||
show(io::IO, t::ComputeTaskS1) = print("ComputeS1")
|
||||
show(io::IO, t::ComputeTaskS2) = print("ComputeS2")
|
||||
show(io::IO, t::ComputeTaskP) = print("ComputeP")
|
||||
show(io::IO, t::ComputeTaskU) = print("ComputeU")
|
||||
show(io::IO, t::ComputeTaskV) = print("ComputeV")
|
||||
show(io::IO, t::ComputeTaskSum) = print("ComputeSum")
|
||||
|
||||
copy(t::DataTask) = DataTask(t.data)
|
27
src/abc_model/tasks.jl
Normal file
27
src/abc_model/tasks.jl
Normal file
@ -0,0 +1,27 @@
|
||||
struct DataTask <: AbstractDataTask
|
||||
data::UInt64
|
||||
end
|
||||
|
||||
# S task with 1 child
|
||||
struct ComputeTaskS1 <: AbstractComputeTask
|
||||
end
|
||||
|
||||
# S task with 2 children
|
||||
struct ComputeTaskS2 <: AbstractComputeTask
|
||||
end
|
||||
|
||||
# P task with 0 children
|
||||
struct ComputeTaskP <: AbstractComputeTask
|
||||
end
|
||||
|
||||
# v task with 2 children
|
||||
struct ComputeTaskV <: AbstractComputeTask
|
||||
end
|
||||
|
||||
# u task with 1 child
|
||||
struct ComputeTaskU <: AbstractComputeTask
|
||||
end
|
||||
|
||||
# task that sums all its inputs, n children
|
||||
struct ComputeTaskSum <: AbstractComputeTask
|
||||
end
|
87
src/graph.jl
87
src/graph.jl
@ -1,7 +1,92 @@
|
||||
using DataStructures
|
||||
|
||||
const Diff = NamedTuple{
|
||||
(:addedNodes, :removedNodes, :addedEdges, :removedEdges),
|
||||
Tuple{Vector{Node}, Vector{Node}, Vector{Edge}, Vector{Edge}}
|
||||
}
|
||||
|
||||
function Diff()
|
||||
return (
|
||||
addedNodes = Vector{Node}(),
|
||||
removedNodes = Vector{Node}(),
|
||||
addedEdges = Vector{Edge}(),
|
||||
removedEdges = Vector{Edge}()
|
||||
)::Diff
|
||||
end
|
||||
|
||||
# An abstract base class for operations
|
||||
# an operation can be applied to a DAG
|
||||
abstract type Operation end
|
||||
|
||||
# An abstract base class for already applied operations
|
||||
# an applied operation can be reversed iff it is the last applied operation on the DAG
|
||||
abstract type AppliedOperation end
|
||||
|
||||
struct NodeFusion <: Operation
|
||||
input::Tuple{ComputeTaskNode, DataTaskNode, ComputeTaskNode}
|
||||
end
|
||||
|
||||
struct AppliedNodeFusion <: AppliedOperation
|
||||
operation::NodeFusion
|
||||
diff::Diff
|
||||
end
|
||||
|
||||
struct NodeReduction <: Operation
|
||||
input::Vector{Node}
|
||||
end
|
||||
|
||||
struct AppliedNodeReduction <: AppliedOperation
|
||||
operation::NodeReduction
|
||||
diff::Diff
|
||||
end
|
||||
|
||||
struct NodeSplit <: Operation
|
||||
input::Node
|
||||
end
|
||||
|
||||
struct AppliedNodeSplit <: AppliedOperation
|
||||
operation::NodeSplit
|
||||
diff::Diff
|
||||
end
|
||||
|
||||
|
||||
mutable struct PossibleOperations
|
||||
nodeFusions::Set{NodeFusion}
|
||||
nodeReductions::Set{NodeReduction}
|
||||
nodeSplits::Set{NodeSplit}
|
||||
end
|
||||
|
||||
function PossibleOperations()
|
||||
return PossibleOperations(
|
||||
Set{NodeFusion}(),
|
||||
Set{NodeReduction}(),
|
||||
Set{NodeSplit}()
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
# The actual state of the DAG is the initial state given by the set of nodes
|
||||
# but with all the operations in appliedChain applied in order
|
||||
mutable struct DAG
|
||||
nodes::Set{Node}
|
||||
|
||||
# The operations currently applied to the set of nodes
|
||||
appliedOperations::Stack{AppliedOperation}
|
||||
|
||||
# The operations not currently applied but part of the current state of the DAG
|
||||
operationsToApply::Deque{Operation}
|
||||
|
||||
# The possible operations at the current state of the DAG
|
||||
possibleOperations::PossibleOperations
|
||||
|
||||
# The set of nodes whose possible operations need to be reevaluated
|
||||
dirtyNodes::Set{Node}
|
||||
|
||||
# "snapshot" system: keep track of added/removed nodes/edges since last snapshot
|
||||
# these are muted in insert_node! etc.
|
||||
diff::Diff
|
||||
end
|
||||
|
||||
function DAG()
|
||||
return DAG(Set{Node}())
|
||||
return DAG(Set{Node}(), Stack{AppliedOperation}(), Deque{Operation}(), PossibleOperations(), Set{Node}(), Diff())
|
||||
end
|
||||
|
@ -3,6 +3,31 @@ using DataStructures
|
||||
in(node::Node, graph::DAG) = node in graph.nodes
|
||||
in(edge::Edge, graph::DAG) = edge in graph.edges
|
||||
|
||||
function isempty(operations::PossibleOperations)
|
||||
return isempty(operations.nodeFusions) &&
|
||||
isempty(operations.nodeReductions) &&
|
||||
isempty(operations.nodeSplits)
|
||||
end
|
||||
|
||||
function length(operations::PossibleOperations)
|
||||
return (nodeFusions = length(operations.nodeFusions),
|
||||
nodeReductions = length(operations.nodeReductions),
|
||||
nodeSplits = length(operations.nodeSplits))
|
||||
end
|
||||
|
||||
function delete!(operations::PossibleOperations, op::NodeFusion)
|
||||
delete!(operations.nodeFusions, op)
|
||||
return operations
|
||||
end
|
||||
function delete!(operations::PossibleOperations, op::NodeReduction)
|
||||
delete!(operations.nodeReductions, op)
|
||||
return operations
|
||||
end
|
||||
function delete!(operations::PossibleOperations, op::NodeSplit)
|
||||
delete!(operations.nodeSplits, op)
|
||||
return operations
|
||||
end
|
||||
|
||||
function is_parent(potential_parent, node)
|
||||
return potential_parent in node.parents
|
||||
end
|
||||
@ -35,8 +60,8 @@ end
|
||||
# siblings = all children of any parents, no duplicates, does not include the node itself
|
||||
function siblings(node::Node)
|
||||
result = Set{Node}()
|
||||
for parent in parents(node)
|
||||
for sibling in children(parent)
|
||||
for parent in node.parents
|
||||
for sibling in parent.children
|
||||
if (sibling != node)
|
||||
push!(result, sibling)
|
||||
end
|
||||
@ -49,8 +74,8 @@ end
|
||||
# partners = all parents of any children, no duplicates, does not include the node itself
|
||||
function partners(node::Node)
|
||||
result = Set{Node}()
|
||||
for child in children(node)
|
||||
for partner in parents(node)
|
||||
for child in node.children
|
||||
for partner in child.parents
|
||||
if (partner != node)
|
||||
push!(result, partner)
|
||||
end
|
||||
@ -60,46 +85,167 @@ function partners(node::Node)
|
||||
return result
|
||||
end
|
||||
|
||||
is_entry_node(node::Node) = length(children(node)) == 0
|
||||
is_exit_node(node::Node) = length(parents(node)) == 0
|
||||
is_entry_node(node::Node) = length(node.children) == 0
|
||||
is_exit_node(node::Node) = length(node.parents) == 0
|
||||
|
||||
function insert_node(graph::DAG, node::Node)
|
||||
# function to invalidate the operation caches for a given operation
|
||||
function invalidate_caches!(graph::DAG, operation::Operation)
|
||||
delete!(graph.possibleOperations, operation)
|
||||
|
||||
# delete the operation from all caches of nodes involved in the operation
|
||||
# (we can iterate over tuples and vectors just fine)
|
||||
for node in operation.input
|
||||
filter!(!=(operation), node.operations)
|
||||
end
|
||||
|
||||
return nothing
|
||||
end
|
||||
|
||||
# function to invalidate the operation caches for a given Node Split specifically
|
||||
function invalidate_caches!(graph::DAG, operation::NodeSplit)
|
||||
delete!(graph.possibleOperations, operation)
|
||||
|
||||
# delete the operation from all caches of nodes involved in the operation
|
||||
# for node split there is only one node
|
||||
filter!(x -> x != operation, operation.input.operations)
|
||||
|
||||
return nothing
|
||||
end
|
||||
|
||||
# for graph mutating functions we need to do a few things
|
||||
# 1: mute the graph (duh)
|
||||
# 2: keep track of what was changed for the diff (if track == true)
|
||||
# 3: invalidate operation caches
|
||||
|
||||
function insert_node!(graph::DAG, node::Node, track=true)
|
||||
# 1: mute
|
||||
push!(graph.nodes, node)
|
||||
|
||||
# 2: keep track
|
||||
if (track) push!(graph.diff.addedNodes, node) end
|
||||
|
||||
# 3: invalidate caches
|
||||
push!(graph.dirtyNodes, node)
|
||||
|
||||
return node
|
||||
end
|
||||
|
||||
function insert_edge(graph::DAG, edge::Edge)
|
||||
function insert_edge!(graph::DAG, edge::Edge, track=true)
|
||||
node1 = edge.edge[1]
|
||||
node2 = edge.edge[2]
|
||||
|
||||
# 1: mute
|
||||
#=if (node2 in node1.parents) || (node1 in node2.children)
|
||||
if !(node2 in node1.parents && node1 in node2.children)
|
||||
error("One-sided edge")
|
||||
end
|
||||
error("Edge to insert already exists")
|
||||
end=#
|
||||
|
||||
# edge points from child to parent
|
||||
push!(edge.edge[1].parents, edge.edge[2])
|
||||
push!(edge.edge[2].children, edge.edge[1])
|
||||
push!(node1.parents, node2)
|
||||
push!(node2.children, node1)
|
||||
|
||||
# 2: keep track
|
||||
if (track) push!(graph.diff.addedEdges, edge) end
|
||||
|
||||
# 3: invalidate caches
|
||||
while !isempty(node1.operations)
|
||||
invalidate_caches!(graph, first(node1.operations))
|
||||
end
|
||||
while !isempty(node2.operations)
|
||||
invalidate_caches!(graph, first(node2.operations))
|
||||
end
|
||||
push!(graph.dirtyNodes, node1)
|
||||
push!(graph.dirtyNodes, node2)
|
||||
|
||||
return edge
|
||||
end
|
||||
|
||||
function remove_node(graph::DAG, node::Node)
|
||||
function remove_node!(graph::DAG, node::Node, track=true)
|
||||
# 1: mute
|
||||
#=if !(node in graph.nodes)
|
||||
error("Trying to remove a node that's not in the graph")
|
||||
end=#
|
||||
delete!(graph.nodes, node)
|
||||
|
||||
# 2: keep track
|
||||
if (track) push!(graph.diff.removedNodes, node) end
|
||||
|
||||
# 3: invalidate caches
|
||||
while !isempty(node.operations)
|
||||
invalidate_caches!(graph, first(node.operations))
|
||||
end
|
||||
delete!(graph.dirtyNodes, node)
|
||||
|
||||
return nothing
|
||||
end
|
||||
|
||||
function remove_edge(graph::DAG, edge::Edge)
|
||||
filter!(x -> x != edge.edge[2], edge.edge[1].parents)
|
||||
filter!(x -> x != edge.edge[1], edge.edge[2].children)
|
||||
function remove_edge!(graph::DAG, edge::Edge, track=true)
|
||||
node1 = edge.edge[1]
|
||||
node2 = edge.edge[2]
|
||||
|
||||
# 1: mute
|
||||
pre_length1 = length(node1.parents)
|
||||
pre_length2 = length(node2.children)
|
||||
filter!(x -> x != node2, node1.parents)
|
||||
filter!(x -> x != node1, node2.children)
|
||||
|
||||
#=removed = pre_length1 - length(node1.parents)
|
||||
if (removed > 1)
|
||||
error("removed $removed from node1's parents")
|
||||
end
|
||||
|
||||
removed = pre_length2 - length(node2.children)
|
||||
if (removed > 1)
|
||||
error("removed $removed from node2's children")
|
||||
end=#
|
||||
|
||||
# 2: keep track
|
||||
if (track) push!(graph.diff.removedEdges, edge) end
|
||||
|
||||
# 3: invalidate caches
|
||||
while !isempty(node1.operations)
|
||||
invalidate_caches!(graph, first(node1.operations))
|
||||
end
|
||||
while !isempty(node2.operations)
|
||||
invalidate_caches!(graph, first(node2.operations))
|
||||
end
|
||||
if (node1 in graph)
|
||||
push!(graph.dirtyNodes, node1)
|
||||
end
|
||||
if (node2 in graph)
|
||||
push!(graph.dirtyNodes, node2)
|
||||
end
|
||||
|
||||
return nothing
|
||||
end
|
||||
|
||||
# return the graph "difference" since last time this function was called
|
||||
function get_snapshot_diff(graph::DAG)
|
||||
return swapfield!(graph, :diff, Diff())
|
||||
end
|
||||
|
||||
function graph_properties(graph::DAG)
|
||||
# make sure the graph is fully generated
|
||||
apply_all!(graph)
|
||||
|
||||
d = 0
|
||||
ce = 0
|
||||
ed = 0
|
||||
for node in graph.nodes
|
||||
d += data(node.task)
|
||||
d += data(node.task) * length(node.parents)
|
||||
ce += compute_effort(node.task)
|
||||
ed += length(node.parents)
|
||||
end
|
||||
|
||||
ci = ce / d
|
||||
|
||||
result = (data = d,
|
||||
compute_effort = ce,
|
||||
compute_intensity = ci)
|
||||
compute_intensity = ci,
|
||||
nodes = length(graph.nodes),
|
||||
edges = ed)
|
||||
return result
|
||||
end
|
||||
|
||||
@ -112,12 +258,24 @@ function get_exit_node(graph::DAG)
|
||||
error("The given graph has no exit node! It is either empty or not acyclic!")
|
||||
end
|
||||
|
||||
function can_reduce(n1::Node, n2::Node)
|
||||
if (n1.task != n2.task)
|
||||
return false
|
||||
function can_fuse(n1::ComputeTaskNode, n2::DataTaskNode, n3::ComputeTaskNode)
|
||||
if !is_child(n1, n2) || !is_child(n2, n3)
|
||||
# the checks are redundant but maybe a good sanity check
|
||||
return false
|
||||
end
|
||||
|
||||
return parents(n1) == parents(n2)
|
||||
if length(n2.parents) != 1 || length(n2.children) != 1 || length(n1.parents) != 1
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function can_reduce(n1::Node, n2::Node)
|
||||
if (n1.task != n2.task)
|
||||
return false
|
||||
end
|
||||
return Set(n1.children) == Set(n2.children)
|
||||
end
|
||||
|
||||
function can_split(n::Node)
|
||||
@ -130,12 +288,11 @@ function is_valid(graph::DAG)
|
||||
push!(nodeQueue, get_exit_node(graph))
|
||||
seenNodes = Set{Node}()
|
||||
|
||||
while ! isempty(nodeQueue)
|
||||
while !isempty(nodeQueue)
|
||||
current = pop!(nodeQueue)
|
||||
push!(seenNodes, current)
|
||||
|
||||
childrenNodes = children(current)
|
||||
for child in childrenNodes
|
||||
for child in current.chlidren
|
||||
push!(nodeQueue, child)
|
||||
end
|
||||
end
|
||||
@ -177,12 +334,17 @@ function show(io::IO, graph::DAG)
|
||||
else
|
||||
print("Total: ", length(graph.nodes), ", ")
|
||||
first = true
|
||||
i = 0
|
||||
for (type, number) in zip(keys(nodeDict), values(nodeDict))
|
||||
i += 1
|
||||
if first
|
||||
first = false
|
||||
else
|
||||
print(", ")
|
||||
end
|
||||
if (i % 3 == 0)
|
||||
print("\n ")
|
||||
end
|
||||
print(type, ": ", number)
|
||||
end
|
||||
end
|
||||
@ -193,3 +355,20 @@ function show(io::IO, graph::DAG)
|
||||
println(io, " Total Data Transfer: ", properties.data)
|
||||
println(io, " Total Compute Intensity: ", properties.compute_intensity)
|
||||
end
|
||||
|
||||
function show(io::IO, diff::Diff)
|
||||
print(io, "Nodes: ")
|
||||
print(io, length(diff.addedNodes) + length(diff.removedNodes))
|
||||
print(io, " Edges: ")
|
||||
print(io, length(diff.addedEdges) + length(diff.removedEdges))
|
||||
end
|
||||
|
||||
# return a namedtuple of the lengths of the added/removed nodes/edges
|
||||
function length(diff::Diff)
|
||||
return (
|
||||
addedNodes = length(diff.addedNodes),
|
||||
removedNodes = length(diff.removedNodes),
|
||||
addedEdges = length(diff.addedEdges),
|
||||
removedEdges = length(diff.removedEdges)
|
||||
)
|
||||
end
|
||||
|
@ -1,5 +1,119 @@
|
||||
# Fuse nodes n1 -> n2 -> n3 together into one node, return the resulting new node
|
||||
function node_fusion(graph::DAG, n1::ComputeTaskNode, n2::DataTaskNode, n3::ComputeTaskNode)
|
||||
# outside interface
|
||||
|
||||
# applies a new operation to the end of the graph
|
||||
function push_operation!(graph::DAG, operation::Operation)
|
||||
# 1.: Add the operation to the DAG
|
||||
push!(graph.operationsToApply, operation)
|
||||
|
||||
return nothing
|
||||
end
|
||||
|
||||
# reverts the latest applied operation, essentially like a ctrl+z for
|
||||
function pop_operation!(graph::DAG)
|
||||
# 1.: Remove the operation from the appliedChain of the DAG
|
||||
if !isempty(graph.operationsToApply)
|
||||
pop!(graph.operationsToApply)
|
||||
elseif !isempty(graph.appliedOperations)
|
||||
appliedOp = pop!(graph.appliedOperations)
|
||||
revert_operation!(graph, appliedOp)
|
||||
else
|
||||
error("No more operations to pop!")
|
||||
end
|
||||
return nothing
|
||||
end
|
||||
|
||||
can_pop(graph::DAG) = !isempty(graph.operationsToApply) || !isempty(graph.appliedOperations)
|
||||
|
||||
# reset the graph to its initial state with no operations applied
|
||||
function reset_graph!(graph::DAG)
|
||||
while (can_pop(graph))
|
||||
pop_operation!(graph)
|
||||
end
|
||||
|
||||
return nothing
|
||||
end
|
||||
|
||||
# implementation detail functions, don't export
|
||||
|
||||
# applies all unapplied operations in the DAG
|
||||
function apply_all!(graph::DAG)
|
||||
while !isempty(graph.operationsToApply)
|
||||
# get next operation to apply from front of the deque
|
||||
op = popfirst!(graph.operationsToApply)
|
||||
|
||||
# apply it
|
||||
appliedOp = apply_operation!(graph, op)
|
||||
|
||||
# push to the end of the appliedOperations deque
|
||||
push!(graph.appliedOperations, appliedOp)
|
||||
end
|
||||
return nothing
|
||||
end
|
||||
|
||||
|
||||
function apply_operation!(graph::DAG, operation::Operation)
|
||||
error("Unknown operation type!")
|
||||
end
|
||||
|
||||
function apply_operation!(graph::DAG, operation::NodeFusion)
|
||||
diff = node_fusion!(graph, operation.input[1], operation.input[2], operation.input[3])
|
||||
return AppliedNodeFusion(operation, diff)
|
||||
end
|
||||
|
||||
function apply_operation!(graph::DAG, operation::NodeReduction)
|
||||
diff = node_reduction!(graph, operation.input[1], operation.input[2])
|
||||
return AppliedNodeReduction(operation, diff)
|
||||
end
|
||||
|
||||
function apply_operation!(graph::DAG, operation::NodeSplit)
|
||||
diff = node_split!(graph, operation.input)
|
||||
return AppliedNodeSplit(operation, diff)
|
||||
end
|
||||
|
||||
|
||||
function revert_operation!(graph::DAG, operation::AppliedOperation)
|
||||
error("Unknown operation type!")
|
||||
end
|
||||
|
||||
function revert_operation!(graph::DAG, operation::AppliedNodeFusion)
|
||||
revert_diff!(graph, operation.diff)
|
||||
return operation.operation
|
||||
end
|
||||
|
||||
function revert_operation!(graph::DAG, operation::AppliedNodeReduction)
|
||||
revert_diff!(graph, operation.diff)
|
||||
return operation.operation
|
||||
end
|
||||
|
||||
function revert_operation!(graph::DAG, operation::AppliedNodeSplit)
|
||||
revert_diff!(graph, operation.diff)
|
||||
return operation.operation
|
||||
end
|
||||
|
||||
|
||||
function revert_diff!(graph::DAG, diff)
|
||||
# add removed nodes, remove added nodes, same for edges
|
||||
# note the order
|
||||
for edge in diff.addedEdges
|
||||
remove_edge!(graph, edge, false)
|
||||
end
|
||||
for node in diff.addedNodes
|
||||
remove_node!(graph, node, false)
|
||||
end
|
||||
|
||||
for node in diff.removedNodes
|
||||
insert_node!(graph, node, false)
|
||||
end
|
||||
for edge in diff.removedEdges
|
||||
insert_edge!(graph, edge, false)
|
||||
end
|
||||
end
|
||||
|
||||
# Fuse nodes n1 -> n2 -> n3 together into one node, return the applied difference to the graph
|
||||
function node_fusion!(graph::DAG, n1::ComputeTaskNode, n2::DataTaskNode, n3::ComputeTaskNode)
|
||||
# clear snapshot
|
||||
get_snapshot_diff(graph)
|
||||
|
||||
if !(n1 in graph) || !(n2 in graph) || !(n3 in graph)
|
||||
error("[Node Fusion] The given nodes are not part of the given graph")
|
||||
end
|
||||
@ -11,133 +125,301 @@ function node_fusion(graph::DAG, n1::ComputeTaskNode, n2::DataTaskNode, n3::Comp
|
||||
|
||||
# save children and parents
|
||||
n1_children = children(n1)
|
||||
n2_parents = parents(n2)
|
||||
n3_parents = parents(n3)
|
||||
n3_children = children(n3)
|
||||
|
||||
if length(n2_parents) > 1
|
||||
if length(n2.parents) > 1
|
||||
error("[Node Fusion] The given data node has more than one parent")
|
||||
end
|
||||
if length(n2.children) > 1
|
||||
error("[Node Fusion] The given data node has more than one child")
|
||||
end
|
||||
if length(n1.parents) > 1
|
||||
error("[Node Fusion] The given n1 has more than one parent")
|
||||
end
|
||||
|
||||
required_edge1 = make_edge(n1, n2)
|
||||
required_edge2 = make_edge(n2, n3)
|
||||
|
||||
# remove the edges and nodes that will be replaced by the fused node
|
||||
remove_edge(graph, required_edge1)
|
||||
remove_edge(graph, required_edge2)
|
||||
remove_node(graph, n1)
|
||||
remove_node(graph, n2)
|
||||
remove_edge!(graph, required_edge1)
|
||||
remove_edge!(graph, required_edge2)
|
||||
remove_node!(graph, n1)
|
||||
remove_node!(graph, n2)
|
||||
|
||||
# get n3's children now so it automatically excludes n2
|
||||
n3_children = children(n3)
|
||||
remove_node(graph, n3)
|
||||
remove_node!(graph, n3)
|
||||
|
||||
# create new node with the fused compute task
|
||||
new_node = ComputeTaskNode(FusedComputeTask{typeof(n1.task), typeof(n3.task)}())
|
||||
insert_node(graph, new_node)
|
||||
insert_node!(graph, new_node)
|
||||
|
||||
# "repoint" children of n1 to the new node
|
||||
# use a set for combined children of n1 and n3 to not get duplicates
|
||||
n1and3_children = Set{Node}()
|
||||
|
||||
# remove edges from n1 children to n1
|
||||
for child in n1_children
|
||||
remove_edge(graph, make_edge(child, n1))
|
||||
insert_edge(graph, make_edge(child, new_node))
|
||||
remove_edge!(graph, make_edge(child, n1))
|
||||
push!(n1and3_children, child)
|
||||
end
|
||||
|
||||
# "repoint" children of n3 to the new node
|
||||
# remove edges from n3 children to n3
|
||||
for child in n3_children
|
||||
remove_edge(graph, make_edge(child, n3))
|
||||
insert_edge(graph, make_edge(child, new_node))
|
||||
remove_edge!(graph, make_edge(child, n3))
|
||||
push!(n1and3_children, child)
|
||||
end
|
||||
|
||||
for child in n1and3_children
|
||||
insert_edge!(graph, make_edge(child, new_node))
|
||||
end
|
||||
|
||||
# "repoint" parents of n3 from new node
|
||||
for parent in n3_parents
|
||||
remove_edge(graph, make_edge(n3, parent))
|
||||
insert_edge(graph, make_edge(new_node, parent))
|
||||
remove_edge!(graph, make_edge(n3, parent))
|
||||
insert_edge!(graph, make_edge(new_node, parent))
|
||||
end
|
||||
|
||||
return new_node
|
||||
return get_snapshot_diff(graph)
|
||||
end
|
||||
|
||||
function node_reduction(graph::DAG, n1::Node, n2::Node)
|
||||
if !(n1 in graph) || !(n2 in graph)
|
||||
error("[Node Reduction] The given nodes are not part of the given graph")
|
||||
end
|
||||
function node_reduction!(graph::DAG, n1::Node, n2::Node)
|
||||
# clear snapshot
|
||||
get_snapshot_diff(graph)
|
||||
|
||||
if typeof(n1) != typeof(n2)
|
||||
#=if !(n1 in graph) || !(n2 in graph)
|
||||
error("[Node Reduction] The given nodes are not part of the given graph")
|
||||
end=#
|
||||
|
||||
#=if typeof(n1) != typeof(n2)
|
||||
error("[Node Reduction] The given nodes are not of the same type")
|
||||
end
|
||||
end=#
|
||||
|
||||
# save n2 parents and children
|
||||
n2_children = children(n2)
|
||||
n2_parents = parents(n2)
|
||||
n2_parents = Set(n2.parents)
|
||||
|
||||
if n2_children != children(n1)
|
||||
#=if Set(n2_children) != Set(n1.children)
|
||||
error("[Node Reduction] The given nodes do not have equal prerequisite nodes which is required for node reduction")
|
||||
end
|
||||
end=#
|
||||
|
||||
# remove n2 and all its parents and children
|
||||
for child in n2_children
|
||||
remove_edge(graph, make_edge(child, n2))
|
||||
remove_edge!(graph, make_edge(child, n2))
|
||||
end
|
||||
|
||||
|
||||
for parent in n2_parents
|
||||
remove_edge(graph, make_edge(n2, parent))
|
||||
|
||||
# add parents of n2 to n1
|
||||
insert_edge(graph, make_edge(n1, parent))
|
||||
remove_edge!(graph, make_edge(n2, parent))
|
||||
end
|
||||
remove_node(graph, n2)
|
||||
|
||||
return n1
|
||||
for parent in n1.parents
|
||||
# delete parents in n1 that already exist in n2
|
||||
delete!(n2_parents, parent)
|
||||
end
|
||||
|
||||
for parent in n2_parents
|
||||
# now add parents of n2 to n1 without duplicates
|
||||
insert_edge!(graph, make_edge(n1, parent))
|
||||
end
|
||||
|
||||
remove_node!(graph, n2)
|
||||
|
||||
return get_snapshot_diff(graph)
|
||||
end
|
||||
|
||||
function node_split(graph::DAG, n1::Node)
|
||||
if !(n1 in graph)
|
||||
function node_split!(graph::DAG, n1::Node)
|
||||
# clear snapshot
|
||||
get_snapshot_diff(graph)
|
||||
|
||||
#=if !(n1 in graph)
|
||||
error("[Node Split] The given node is not part of the given graph")
|
||||
end
|
||||
end=#
|
||||
|
||||
n1_parents = parents(n1)
|
||||
n1_children = children(n1)
|
||||
|
||||
if length(n1_parents) <= 1
|
||||
#=if length(n1_parents) <= 1
|
||||
error("[Node Split] The given node does not have multiple parents which is required for node split")
|
||||
end=#
|
||||
|
||||
for parent in n1_parents
|
||||
remove_edge!(graph, make_edge(n1, parent))
|
||||
end
|
||||
for child in n1_children
|
||||
remove_edge!(graph, make_edge(child, n1))
|
||||
end
|
||||
remove_node!(graph, n1)
|
||||
|
||||
for parent in n1_parents
|
||||
n_copy = copy(n1)
|
||||
insert_node(graph, n_copy)
|
||||
insert_edge(graph, make_edge(n_copy, parent))
|
||||
remove_edge(graph, make_edge(n1, parent))
|
||||
insert_node!(graph, n_copy)
|
||||
insert_edge!(graph, make_edge(n_copy, parent))
|
||||
|
||||
for child in n1_children
|
||||
insert_edge(graph, make_edge(child, n_copy))
|
||||
insert_edge!(graph, make_edge(child, n_copy))
|
||||
end
|
||||
end
|
||||
|
||||
return get_snapshot_diff(graph)
|
||||
end
|
||||
|
||||
# function to find node fusions involving the given node if it's a data node
|
||||
# pushes the found fusion everywhere it needs to be and returns nothing
|
||||
function find_fusions!(graph::DAG, node::DataTaskNode)
|
||||
if length(node.parents) != 1 || length(node.children) != 1
|
||||
return nothing
|
||||
end
|
||||
|
||||
child_node = first(node.children)
|
||||
parent_node = first(node.parents)
|
||||
|
||||
#=if !(child_node in graph) || !(parent_node in graph)
|
||||
error("Parents/Children that are not in the graph!!!")
|
||||
end=#
|
||||
|
||||
if length(child_node.parents) != 1
|
||||
return nothing
|
||||
end
|
||||
|
||||
nf = NodeFusion((child_node, node, parent_node))
|
||||
push!(graph.possibleOperations.nodeFusions, nf)
|
||||
push!(child_node.operations, nf)
|
||||
push!(node.operations, nf)
|
||||
push!(parent_node.operations, nf)
|
||||
|
||||
return nothing
|
||||
end
|
||||
|
||||
# function to find node fusions involving the given node if it's a compute node
|
||||
# pushes the found fusion(s) everywhere it needs to be and returns nothing
|
||||
function find_fusions!(graph::DAG, node::ComputeTaskNode)
|
||||
# for loop that always runs once for a scoped block we can break out of
|
||||
for _ in 1:1
|
||||
# assume this node as child of the chain
|
||||
if length(node.parents) != 1
|
||||
break
|
||||
end
|
||||
node2 = first(node.parents)
|
||||
if length(node2.parents) != 1 || length(node2.children) != 1
|
||||
break
|
||||
end
|
||||
node3 = first(node2.parents)
|
||||
|
||||
#=if !(node2 in graph) || !(node3 in graph)
|
||||
error("Parents/Children that are not in the graph!!!")
|
||||
end=#
|
||||
|
||||
nf = NodeFusion((node, node2, node3))
|
||||
push!(graph.possibleOperations.nodeFusions, nf)
|
||||
push!(node.operations, nf)
|
||||
push!(node2.operations, nf)
|
||||
push!(node3.operations, nf)
|
||||
end
|
||||
|
||||
for _ in 1:1
|
||||
# assume this node as parent of the chain
|
||||
if length(node.children) < 1
|
||||
break
|
||||
end
|
||||
node2 = first(node.children)
|
||||
if length(node2.parents) != 1 || length(node2.children) != 1
|
||||
break
|
||||
end
|
||||
node1 = first(node2.children)
|
||||
if (length(node1.parents) > 1)
|
||||
break
|
||||
end
|
||||
|
||||
#=if !(node2 in graph) || !(node1 in graph)
|
||||
error("Parents/Children that are not in the graph!!!")
|
||||
end=#
|
||||
|
||||
nf = NodeFusion((node1, node2, node))
|
||||
push!(graph.possibleOperations.nodeFusions, nf)
|
||||
push!(node1.operations, nf)
|
||||
push!(node2.operations, nf)
|
||||
push!(node.operations, nf)
|
||||
end
|
||||
|
||||
return nothing
|
||||
end
|
||||
|
||||
function find_reductions!(graph::DAG, node::Node)
|
||||
reductionVector = nothing
|
||||
# possible reductions are with nodes that are partners, i.e. parents of children
|
||||
for partner in partners(node)
|
||||
if can_reduce(node, partner)
|
||||
if reductionVector === nothing
|
||||
# only when there's at least one reduction partner, insert the vector
|
||||
reductionVector = Vector{Node}()
|
||||
push!(reductionVector, node)
|
||||
end
|
||||
|
||||
push!(reductionVector, partner)
|
||||
end
|
||||
end
|
||||
|
||||
if reductionVector !== nothing
|
||||
nr = NodeReduction(reductionVector)
|
||||
push!(graph.possibleOperations.nodeReductions, nr)
|
||||
for node in reductionVector
|
||||
push!(node.operations, nr)
|
||||
end
|
||||
end
|
||||
|
||||
return nothing
|
||||
end
|
||||
|
||||
function find_splits!(graph::DAG, node::Node)
|
||||
if (can_split(node))
|
||||
ns = NodeSplit(node)
|
||||
push!(graph.possibleOperations.nodeSplits, ns)
|
||||
push!(node.operations, ns)
|
||||
end
|
||||
|
||||
return nothing
|
||||
end
|
||||
|
||||
# "clean" the operations on a dirty node
|
||||
function clean_node!(graph::DAG, node::Node)
|
||||
find_fusions!(graph, node)
|
||||
find_reductions!(graph, node)
|
||||
find_splits!(graph, node)
|
||||
|
||||
delete!(graph.dirtyNodes, node)
|
||||
end
|
||||
|
||||
# function to generate all possible optmizations on the graph
|
||||
function generate_options(graph::DAG)
|
||||
options = (fusions = Vector{Tuple{ComputeTaskNode, DataTaskNode, ComputeTaskNode}}(),
|
||||
reductions = Vector{Vector{Node}}(),
|
||||
splits = Vector{Node}())
|
||||
options = PossibleOperations()
|
||||
|
||||
# make sure the graph is fully generated through
|
||||
apply_all!(graph)
|
||||
|
||||
# find possible node fusions
|
||||
for node in graph.nodes
|
||||
if (typeof(node) <: DataTaskNode)
|
||||
node_parents = parents(node)
|
||||
if length(node_parents) != 1
|
||||
if length(node.parents) != 1
|
||||
# data node can only have a single parent
|
||||
continue
|
||||
end
|
||||
parent_node = pop!(node_parents)
|
||||
parent_node = first(node.parents)
|
||||
|
||||
node_children = children(node)
|
||||
if length(node_children) != 1
|
||||
if length(node.children) != 1
|
||||
# this node is an entry node or has multiple children which should not be possible
|
||||
continue
|
||||
end
|
||||
child_node = pop!(node_children)
|
||||
child_node = first(node.children)
|
||||
if (length(child_node.parents) != 1)
|
||||
continue
|
||||
end
|
||||
|
||||
push!(options.fusions, (child_node, node, parent_node))
|
||||
nf = NodeFusion((child_node, node, parent_node))
|
||||
push!(options.nodeFusions, nf)
|
||||
push!(child_node.operations, nf)
|
||||
push!(node.operations, nf)
|
||||
push!(parent_node.operations, nf)
|
||||
end
|
||||
end
|
||||
|
||||
@ -151,29 +433,53 @@ function generate_options(graph::DAG)
|
||||
|
||||
push!(visitedNodes, node)
|
||||
|
||||
reductionVector = missing
|
||||
reductionVector = nothing
|
||||
# possible reductions are with nodes that are partners, i.e. parents of children
|
||||
for partner in partners(node)
|
||||
if can_reduce(node, partner)
|
||||
if reductionVector == missing
|
||||
if reductionVector === nothing
|
||||
# only when there's at least one reduction partner, insert the vector
|
||||
reductionVector = Vector{Node}
|
||||
reductionVector = Vector{Node}()
|
||||
push!(reductionVector, node)
|
||||
push!(options.reductions, reductionVector)
|
||||
end
|
||||
|
||||
push!(reductionVector, partner)
|
||||
push!(visitedNodes, partner)
|
||||
end
|
||||
end
|
||||
|
||||
if reductionVector !== nothing
|
||||
nr = NodeReduction(reductionVector)
|
||||
push!(options.nodeReductions, nr)
|
||||
for node in reductionVector
|
||||
push!(node.operations, nr)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# find possible node splits
|
||||
for node in graph.nodes
|
||||
if (can_split(node))
|
||||
push!(options.splits, node)
|
||||
ns = NodeSplit(node)
|
||||
push!(options.nodeSplits, ns)
|
||||
push!(node.operations, ns)
|
||||
end
|
||||
end
|
||||
|
||||
return options
|
||||
graph.possibleOperations = options
|
||||
empty!(graph.dirtyNodes)
|
||||
end
|
||||
|
||||
function get_operations(graph::DAG)
|
||||
apply_all!(graph)
|
||||
|
||||
if isempty(graph.possibleOperations)
|
||||
generate_options(graph)
|
||||
end
|
||||
|
||||
while !isempty(graph.dirtyNodes)
|
||||
clean_node!(graph, first(graph.dirtyNodes))
|
||||
end
|
||||
|
||||
return graph.possibleOperations
|
||||
end
|
||||
|
@ -33,3 +33,18 @@ end
|
||||
function ==(e1::Edge, e2::Edge)
|
||||
return e1.edge[1] == e2.edge[1] && e1.edge[2] == e2.edge[2]
|
||||
end
|
||||
|
||||
function ==(n1::Node, n2::Node)
|
||||
return false
|
||||
end
|
||||
|
||||
function ==(n1::ComputeTaskNode, n2::ComputeTaskNode)
|
||||
return n1.id == n2.id
|
||||
end
|
||||
|
||||
function ==(n1::DataTaskNode, n2::DataTaskNode)
|
||||
return n1.id == n2.id
|
||||
end
|
||||
|
||||
copy(n::ComputeTaskNode) = ComputeTaskNode(copy(n.task), copy(n.parents), copy(n.children), UUIDs.uuid1(rng), copy(n.operations))
|
||||
copy(n::DataTaskNode) = DataTaskNode(copy(n.task), copy(n.parents), copy(n.children), UUIDs.uuid1(rng), copy(n.operations))
|
||||
|
18
src/nodes.jl
18
src/nodes.jl
@ -5,6 +5,10 @@ rng = Random.MersenneTwister(0)
|
||||
|
||||
abstract type Node end
|
||||
|
||||
# declare this type here because it's needed
|
||||
# the specific operations are declared in graph.jl
|
||||
abstract type Operation end
|
||||
|
||||
struct DataTaskNode <: Node
|
||||
task::AbstractDataTask
|
||||
|
||||
@ -13,7 +17,11 @@ struct DataTaskNode <: Node
|
||||
children::Vector{Node}
|
||||
|
||||
# need a unique identifier unique to every *constructed* node
|
||||
# however, it can be copied when splitting a node
|
||||
id::Base.UUID
|
||||
|
||||
# a vector holding references to the graph operations involving this node
|
||||
operations::Vector{Operation}
|
||||
end
|
||||
|
||||
# same as DataTaskNode
|
||||
@ -22,15 +30,11 @@ struct ComputeTaskNode <: Node
|
||||
parents::Vector{Node}
|
||||
children::Vector{Node}
|
||||
id::Base.UUID
|
||||
operations::Vector{Operation}
|
||||
end
|
||||
|
||||
function DataTaskNode(t::AbstractDataTask)
|
||||
return DataTaskNode(t, Vector{Node}(), Vector{Node}(), UUIDs.uuid1(rng))
|
||||
end
|
||||
|
||||
function ComputeTaskNode(t::AbstractComputeTask)
|
||||
return ComputeTaskNode(t, Vector{Node}(), Vector{Node}(), UUIDs.uuid1(rng))
|
||||
end
|
||||
DataTaskNode(t::AbstractDataTask) = DataTaskNode(t, Vector{Node}(), Vector{Node}(), UUIDs.uuid1(rng), Vector{Operation}())
|
||||
ComputeTaskNode(t::AbstractComputeTask) = ComputeTaskNode(t, Vector{Node}(), Vector{Node}(), UUIDs.uuid1(rng), Vector{Operation}())
|
||||
|
||||
struct Edge
|
||||
# edge points from child to parent
|
||||
|
@ -17,14 +17,6 @@ data(t::AbstractDataTask) = getfield(t, :data)
|
||||
|
||||
data(t::AbstractComputeTask) = 0
|
||||
|
||||
# define compute_efforts tasks computation
|
||||
# put some "random" numbers here for now
|
||||
compute_effort(t::ComputeTaskS1) = 10
|
||||
compute_effort(t::ComputeTaskS2) = 10
|
||||
compute_effort(t::ComputeTaskU) = 6
|
||||
compute_effort(t::ComputeTaskV) = 20
|
||||
compute_effort(t::ComputeTaskP) = 15
|
||||
compute_effort(t::ComputeTaskSum) = 1
|
||||
function compute_effort(t::FusedComputeTask)
|
||||
(T1, T2) = collect(typeof(t).parameters)
|
||||
return compute_effort(T1()) + compute_effort(T2())
|
||||
@ -40,17 +32,6 @@ function compute_intensity(t::AbstractTask)::UInt64
|
||||
return compute_effort(t) / data(t)
|
||||
end
|
||||
|
||||
function show(io::IO, t::DataTask)
|
||||
print(io, "Data", t.data)
|
||||
end
|
||||
|
||||
show(io::IO, t::ComputeTaskS1) = print("ComputeS1")
|
||||
show(io::IO, t::ComputeTaskS2) = print("ComputeS2")
|
||||
show(io::IO, t::ComputeTaskP) = print("ComputeP")
|
||||
show(io::IO, t::ComputeTaskU) = print("ComputeU")
|
||||
show(io::IO, t::ComputeTaskV) = print("ComputeV")
|
||||
show(io::IO, t::ComputeTaskSum) = print("ComputeSum")
|
||||
|
||||
function show(io::IO, t::FusedComputeTask)
|
||||
(T1, T2) = get_types(t)
|
||||
print(io, "ComputeFuse(", T1(), ", ", T2(), ")")
|
||||
@ -67,3 +48,6 @@ end
|
||||
function ==(t1::AbstractDataTask, t2::AbstractDataTask)
|
||||
return data(t1) == data(t2)
|
||||
end
|
||||
|
||||
copy(t::AbstractDataTask) = error("Need to implement copying for your data tasks!")
|
||||
copy(t::AbstractComputeTask) = typeof(t)()
|
||||
|
28
src/tasks.jl
28
src/tasks.jl
@ -3,34 +3,6 @@ abstract type AbstractTask end
|
||||
abstract type AbstractComputeTask <: AbstractTask end
|
||||
abstract type AbstractDataTask <: AbstractTask end
|
||||
|
||||
struct DataTask <: AbstractDataTask
|
||||
data::UInt64
|
||||
end
|
||||
|
||||
# S task with 1 child
|
||||
struct ComputeTaskS1 <: AbstractComputeTask
|
||||
end
|
||||
|
||||
# S task with 2 children
|
||||
struct ComputeTaskS2 <: AbstractComputeTask
|
||||
end
|
||||
|
||||
# P task with 0 children
|
||||
struct ComputeTaskP <: AbstractComputeTask
|
||||
end
|
||||
|
||||
# v task with 2 children
|
||||
struct ComputeTaskV <: AbstractComputeTask
|
||||
end
|
||||
|
||||
# u task with 1 child
|
||||
struct ComputeTaskU <: AbstractComputeTask
|
||||
end
|
||||
|
||||
# task that sums all its inputs, n children
|
||||
struct ComputeTaskSum <: AbstractComputeTask
|
||||
end
|
||||
|
||||
struct FusedComputeTask{T1<:AbstractComputeTask, T2<:AbstractComputeTask} <: AbstractComputeTask
|
||||
end
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
function bytes_to_human_readable(bytes)
|
||||
function bytes_to_human_readable(bytes::Int64)
|
||||
units = ["B", "KiB", "MiB", "GiB", "TiB"]
|
||||
unit_index = 1
|
||||
while bytes >= 1024 && unit_index < length(units)
|
||||
bytes /= 1024
|
||||
unit_index += 1
|
||||
end
|
||||
return string(round(bytes, digits=4), " ", units[unit_index])
|
||||
return string(round(bytes, sigdigits=4), " ", units[unit_index])
|
||||
end
|
||||
|
3
test/Project.toml
Normal file
3
test/Project.toml
Normal file
@ -0,0 +1,3 @@
|
||||
[deps]
|
||||
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
||||
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
@ -1,43 +1,33 @@
|
||||
using MetagraphOptimization
|
||||
using Test
|
||||
using Random
|
||||
|
||||
function test_known_graphs()
|
||||
g_ABAB = import_txt(joinpath(@__DIR__, "..", "examples", "AB->AB.txt"))
|
||||
props = graph_properties(g_ABAB)
|
||||
function test_known_graph(name::String, n, fusion_test=true)
|
||||
@testset "Test $name Graph ($n)" begin
|
||||
graph = parse_abc(joinpath(@__DIR__, "..", "examples", "$name.txt"))
|
||||
props = graph_properties(graph)
|
||||
|
||||
@test length(g_ABAB.nodes) == 34
|
||||
@test props.compute_effort == 185
|
||||
@test props.data == 102
|
||||
|
||||
@test length(generate_options(g_ABAB).fusions) == 10
|
||||
|
||||
test_node_fusion(g_ABAB)
|
||||
|
||||
|
||||
g_ABAB3 = import_txt(joinpath(@__DIR__, "..", "examples", "AB->ABBB.txt"))
|
||||
props = graph_properties(g_ABAB3)
|
||||
|
||||
@test length(g_ABAB3.nodes) == 280
|
||||
@test props.compute_effort == 2007
|
||||
@test props.data == 828
|
||||
|
||||
test_node_fusion(g_ABAB3)
|
||||
if (fusion_test)
|
||||
test_node_fusion(graph)
|
||||
end
|
||||
test_random_walk(graph, n)
|
||||
end
|
||||
end
|
||||
|
||||
function test_node_fusion(g)
|
||||
function test_node_fusion(g::DAG)
|
||||
@testset "Test Node Fusion" begin
|
||||
props = graph_properties(g)
|
||||
|
||||
options = generate_options(g)
|
||||
options = get_operations(g)
|
||||
|
||||
nodes_number = length(g.nodes)
|
||||
data = props.data
|
||||
compute_effort = props.compute_effort
|
||||
|
||||
while length(options.fusions) != 0
|
||||
fusion = options.fusions[1]
|
||||
n = node_fusion(g, fusion[1], fusion[2], fusion[3])
|
||||
@test typeof(n.task) <: FusedComputeTask
|
||||
@test length(g.nodes) == nodes_number - 2
|
||||
while !isempty(options.nodeFusions)
|
||||
fusion = first(options.nodeFusions)
|
||||
|
||||
@test typeof(fusion) <: NodeFusion
|
||||
|
||||
push_operation!(g, fusion)
|
||||
|
||||
props = graph_properties(g)
|
||||
@test props.data < data
|
||||
@ -47,8 +37,56 @@ function test_node_fusion(g)
|
||||
data = props.data
|
||||
compute_effort = props.compute_effort
|
||||
|
||||
options = generate_options(g)
|
||||
options = get_operations(g)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function test_random_walk(g::DAG, n::Int64)
|
||||
@testset "Test Random Walk ($n)" begin
|
||||
# the purpose here is to do "random" operations and reverse them again and validate that the graph stays the same and doesn't diverge
|
||||
reset_graph!(g)
|
||||
|
||||
properties = graph_properties(g)
|
||||
|
||||
for i = 1:n
|
||||
# choose push or pop
|
||||
if rand(Bool)
|
||||
# push
|
||||
opt = get_operations(g)
|
||||
|
||||
# choose one of fuse/split/reduce
|
||||
option = rand(1:3)
|
||||
if option == 1 && !isempty(opt.nodeFusions)
|
||||
push_operation!(g, rand(collect(opt.nodeFusions)))
|
||||
elseif option == 2 && !isempty(opt.nodeReductions)
|
||||
push_operation!(g, rand(collect(opt.nodeReductions)))
|
||||
elseif option == 3 && !isempty(opt.nodeSplits)
|
||||
push_operation!(g, rand(collect(opt.nodeSplits)))
|
||||
else
|
||||
i = i - 1
|
||||
end
|
||||
else
|
||||
# pop
|
||||
if (can_pop(g))
|
||||
pop_operation!(g)
|
||||
else
|
||||
i = i - 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
print(g)
|
||||
reset_graph!(g)
|
||||
|
||||
@test properties == graph_properties(g)
|
||||
end
|
||||
end
|
||||
|
||||
Random.seed!(0)
|
||||
|
||||
@testset "Test Known ABC-Graphs" begin
|
||||
test_known_graph("AB->AB", 10000)
|
||||
test_known_graph("AB->ABBB", 10000)
|
||||
test_known_graph("AB->ABBBBB", 1000, false)
|
||||
end
|
||||
println("Known Graph Testing Complete!")
|
||||
|
@ -1,6 +1,11 @@
|
||||
using MetagraphOptimization
|
||||
using Test
|
||||
|
||||
include("known_graphs.jl")
|
||||
@testset "MetagraphOptimization Tests" begin
|
||||
include("unit_tests_utility.jl")
|
||||
include("unit_tests_tasks.jl")
|
||||
include("unit_tests_nodes.jl")
|
||||
include("unit_tests_graph.jl")
|
||||
|
||||
test_known_graphs()
|
||||
include("known_graphs.jl")
|
||||
end
|
||||
|
210
test/unit_tests_graph.jl
Normal file
210
test/unit_tests_graph.jl
Normal file
@ -0,0 +1,210 @@
|
||||
import MetagraphOptimization.insert_node!
|
||||
import MetagraphOptimization.insert_edge!
|
||||
import MetagraphOptimization.make_node
|
||||
import MetagraphOptimization.make_edge
|
||||
import MetagraphOptimization.siblings
|
||||
import MetagraphOptimization.partners
|
||||
|
||||
@testset "Unit Tests Graph" begin
|
||||
graph = MetagraphOptimization.DAG()
|
||||
|
||||
@test length(graph.nodes) == 0
|
||||
@test length(graph.appliedOperations) == 0
|
||||
@test length(graph.operationsToApply) == 0
|
||||
@test length(graph.dirtyNodes) == 0
|
||||
@test length(graph.diff) == (addedNodes = 0, removedNodes = 0, addedEdges = 0, removedEdges = 0)
|
||||
@test length(get_operations(graph)) == (nodeFusions = 0, nodeReductions = 0, nodeSplits = 0)
|
||||
|
||||
# s to output (exit node)
|
||||
d_exit = insert_node!(graph, make_node(DataTask(10)), false)
|
||||
|
||||
@test length(graph.nodes) == 1
|
||||
@test length(graph.dirtyNodes) == 1
|
||||
|
||||
# final s compute
|
||||
s0 = insert_node!(graph, make_node(ComputeTaskS2()), false)
|
||||
|
||||
@test length(graph.nodes) == 2
|
||||
@test length(graph.dirtyNodes) == 2
|
||||
|
||||
# data from v0 and v1 to s0
|
||||
d_v0_s0 = insert_node!(graph, make_node(DataTask(5)), false)
|
||||
d_v1_s0 = insert_node!(graph, make_node(DataTask(5)), false)
|
||||
|
||||
# v0 and v1 compute
|
||||
v0 = insert_node!(graph, make_node(ComputeTaskV()), false)
|
||||
v1 = insert_node!(graph, make_node(ComputeTaskV()), false)
|
||||
|
||||
# data from uB, uA, uBp and uAp to v0 and v1
|
||||
d_uB_v0 = insert_node!(graph, make_node(DataTask(3)), false)
|
||||
d_uA_v0 = insert_node!(graph, make_node(DataTask(3)), false)
|
||||
d_uBp_v1 = insert_node!(graph, make_node(DataTask(3)), false)
|
||||
d_uAp_v1 = insert_node!(graph, make_node(DataTask(3)), false)
|
||||
|
||||
# uB, uA, uBp and uAp computes
|
||||
uB = insert_node!(graph, make_node(ComputeTaskU()), false)
|
||||
uA = insert_node!(graph, make_node(ComputeTaskU()), false)
|
||||
uBp = insert_node!(graph, make_node(ComputeTaskU()), false)
|
||||
uAp = insert_node!(graph, make_node(ComputeTaskU()), false)
|
||||
|
||||
# data from PB, PA, PBp and PAp to uB, uA, uBp and uAp
|
||||
d_PB_uB = insert_node!(graph, make_node(DataTask(6)), false)
|
||||
d_PA_uA = insert_node!(graph, make_node(DataTask(6)), false)
|
||||
d_PBp_uBp = insert_node!(graph, make_node(DataTask(6)), false)
|
||||
d_PAp_uAp = insert_node!(graph, make_node(DataTask(6)), false)
|
||||
|
||||
# P computes PB, PA, PBp and PAp
|
||||
PB = insert_node!(graph, make_node(ComputeTaskP()), false)
|
||||
PA = insert_node!(graph, make_node(ComputeTaskP()), false)
|
||||
PBp = insert_node!(graph, make_node(ComputeTaskP()), false)
|
||||
PAp = insert_node!(graph, make_node(ComputeTaskP()), false)
|
||||
|
||||
# entry nodes getting data for P computes
|
||||
d_PB = insert_node!(graph, make_node(DataTask(4)), false)
|
||||
d_PA = insert_node!(graph, make_node(DataTask(4)), false)
|
||||
d_PBp = insert_node!(graph, make_node(DataTask(4)), false)
|
||||
d_PAp = insert_node!(graph, make_node(DataTask(4)), false)
|
||||
|
||||
@test length(graph.nodes) == 26
|
||||
@test length(graph.dirtyNodes) == 26
|
||||
|
||||
# now for all the edgese
|
||||
insert_edge!(graph, make_edge(d_PB, PB), false)
|
||||
insert_edge!(graph, make_edge(d_PA, PA), false)
|
||||
insert_edge!(graph, make_edge(d_PBp, PBp), false)
|
||||
insert_edge!(graph, make_edge(d_PAp, PAp), false)
|
||||
|
||||
insert_edge!(graph, make_edge(PB, d_PB_uB), false)
|
||||
insert_edge!(graph, make_edge(PA, d_PA_uA), false)
|
||||
insert_edge!(graph, make_edge(PBp, d_PBp_uBp), false)
|
||||
insert_edge!(graph, make_edge(PAp, d_PAp_uAp), false)
|
||||
|
||||
insert_edge!(graph, make_edge(d_PB_uB, uB), false)
|
||||
insert_edge!(graph, make_edge(d_PA_uA, uA), false)
|
||||
insert_edge!(graph, make_edge(d_PBp_uBp, uBp), false)
|
||||
insert_edge!(graph, make_edge(d_PAp_uAp, uAp), false)
|
||||
|
||||
insert_edge!(graph, make_edge(uB, d_uB_v0), false)
|
||||
insert_edge!(graph, make_edge(uA, d_uA_v0), false)
|
||||
insert_edge!(graph, make_edge(uBp, d_uBp_v1), false)
|
||||
insert_edge!(graph, make_edge(uAp, d_uAp_v1), false)
|
||||
|
||||
insert_edge!(graph, make_edge(d_uB_v0, v0), false)
|
||||
insert_edge!(graph, make_edge(d_uA_v0, v0), false)
|
||||
insert_edge!(graph, make_edge(d_uBp_v1, v1), false)
|
||||
insert_edge!(graph, make_edge(d_uAp_v1, v1), false)
|
||||
|
||||
insert_edge!(graph, make_edge(v0, d_v0_s0), false)
|
||||
insert_edge!(graph, make_edge(v1, d_v1_s0), false)
|
||||
|
||||
insert_edge!(graph, make_edge(d_v0_s0, s0), false)
|
||||
insert_edge!(graph, make_edge(d_v1_s0, s0), false)
|
||||
|
||||
insert_edge!(graph, make_edge(s0, d_exit), false)
|
||||
|
||||
@test length(graph.nodes) == 26
|
||||
@test length(graph.appliedOperations) == 0
|
||||
@test length(graph.operationsToApply) == 0
|
||||
@test length(graph.dirtyNodes) == 26
|
||||
@test length(graph.diff) == (addedNodes = 0, removedNodes = 0, addedEdges = 0, removedEdges = 0)
|
||||
|
||||
@test is_entry_node(d_PB)
|
||||
@test is_entry_node(d_PA)
|
||||
@test is_entry_node(d_PBp)
|
||||
@test is_entry_node(d_PBp)
|
||||
@test !is_entry_node(PB)
|
||||
@test !is_entry_node(v0)
|
||||
@test !is_entry_node(d_exit)
|
||||
|
||||
@test is_exit_node(d_exit)
|
||||
@test !is_exit_node(d_uB_v0)
|
||||
@test !is_exit_node(v0)
|
||||
|
||||
@test length(children(v0)) == 2
|
||||
@test length(children(v1)) == 2
|
||||
@test length(parents(v0)) == 1
|
||||
@test length(parents(v1)) == 1
|
||||
|
||||
@test MetagraphOptimization.get_exit_node(graph) == d_exit
|
||||
|
||||
@test length(partners(s0)) == 0
|
||||
@test length(siblings(s0)) == 0
|
||||
|
||||
operations = get_operations(graph)
|
||||
@test length(operations) == (nodeFusions = 10, nodeReductions = 0, nodeSplits = 0)
|
||||
@test length(graph.dirtyNodes) == 0
|
||||
|
||||
@test operations == get_operations(graph)
|
||||
nf = first(operations.nodeFusions)
|
||||
|
||||
properties = graph_properties(graph)
|
||||
@test properties.compute_effort == 134
|
||||
@test properties.data == 62
|
||||
@test properties.compute_intensity ≈ 134/62
|
||||
@test properties.nodes == 26
|
||||
@test properties.edges == 25
|
||||
|
||||
push_operation!(graph, nf)
|
||||
# **does not immediately apply the operation**
|
||||
|
||||
@test length(graph.nodes) == 26
|
||||
@test length(graph.appliedOperations) == 0
|
||||
@test length(graph.operationsToApply) == 1
|
||||
@test first(graph.operationsToApply) == nf
|
||||
@test length(graph.dirtyNodes) == 0
|
||||
@test length(graph.diff) == (addedNodes = 0, removedNodes = 0, addedEdges = 0, removedEdges = 0)
|
||||
|
||||
# this applies pending operations
|
||||
properties = graph_properties(graph)
|
||||
|
||||
@test length(graph.nodes) == 24
|
||||
@test length(graph.appliedOperations) == 1
|
||||
@test length(graph.operationsToApply) == 0
|
||||
@test length(graph.dirtyNodes) != 0
|
||||
@test properties.nodes == 24
|
||||
@test properties.edges == 23
|
||||
@test properties.compute_effort == 134
|
||||
@test properties.data < 62
|
||||
@test properties.compute_intensity > 134/62
|
||||
|
||||
operations = get_operations(graph)
|
||||
@test length(graph.dirtyNodes) == 0
|
||||
|
||||
@test length(operations) == (nodeFusions = 9, nodeReductions = 0, nodeSplits = 0)
|
||||
@test !isempty(operations)
|
||||
|
||||
possibleNF = 9
|
||||
while !isempty(operations.nodeFusions)
|
||||
push_operation!(graph, first(operations.nodeFusions))
|
||||
operations = get_operations(graph)
|
||||
possibleNF = possibleNF - 1
|
||||
@test length(operations) == (nodeFusions = possibleNF, nodeReductions = 0, nodeSplits = 0)
|
||||
end
|
||||
|
||||
@test isempty(operations)
|
||||
|
||||
@test length(operations) == (nodeFusions = 0, nodeReductions = 0, nodeSplits = 0)
|
||||
@test length(graph.dirtyNodes) == 0
|
||||
@test length(graph.nodes) == 6
|
||||
@test length(graph.appliedOperations) == 10
|
||||
@test length(graph.operationsToApply) == 0
|
||||
|
||||
reset_graph!(graph)
|
||||
|
||||
@test length(graph.dirtyNodes) == 26
|
||||
@test length(graph.nodes) == 26
|
||||
@test length(graph.appliedOperations) == 0
|
||||
@test length(graph.operationsToApply) == 0
|
||||
|
||||
properties = graph_properties(graph)
|
||||
@test properties.nodes == 26
|
||||
@test properties.edges == 25
|
||||
@test properties.compute_effort == 134
|
||||
@test properties.data == 62
|
||||
@test properties.compute_intensity ≈ 134/62
|
||||
|
||||
operations = get_operations(graph)
|
||||
@test length(operations) == (nodeFusions = 10, nodeReductions = 0, nodeSplits = 0)
|
||||
|
||||
end
|
||||
println("Graph Unit Tests Complete!")
|
36
test/unit_tests_nodes.jl
Normal file
36
test/unit_tests_nodes.jl
Normal file
@ -0,0 +1,36 @@
|
||||
|
||||
@testset "Unit Tests Nodes" begin
|
||||
nC1 = MetagraphOptimization.make_node(MetagraphOptimization.ComputeTaskU())
|
||||
nC2 = MetagraphOptimization.make_node(MetagraphOptimization.ComputeTaskV())
|
||||
nC3 = MetagraphOptimization.make_node(MetagraphOptimization.ComputeTaskP())
|
||||
nC4 = MetagraphOptimization.make_node(MetagraphOptimization.ComputeTaskSum())
|
||||
|
||||
nD1 = MetagraphOptimization.make_node(MetagraphOptimization.DataTask(10))
|
||||
nD2 = MetagraphOptimization.make_node(MetagraphOptimization.DataTask(20))
|
||||
|
||||
@test_throws ErrorException MetagraphOptimization.make_edge(nC1, nC2)
|
||||
@test_throws ErrorException MetagraphOptimization.make_edge(nC1, nC1)
|
||||
@test_throws ErrorException MetagraphOptimization.make_edge(nC3, nC4)
|
||||
@test_throws ErrorException MetagraphOptimization.make_edge(nD1, nD2)
|
||||
@test_throws ErrorException MetagraphOptimization.make_edge(nD1, nD1)
|
||||
|
||||
ed1 = MetagraphOptimization.make_edge(nC1, nD1)
|
||||
ed2 = MetagraphOptimization.make_edge(nD1, nC2)
|
||||
ed3 = MetagraphOptimization.make_edge(nC2, nD2)
|
||||
ed4 = MetagraphOptimization.make_edge(nD2, nC3)
|
||||
|
||||
@test nC1 != nC2
|
||||
@test nD1 != nD2
|
||||
@test nC1 != nD1
|
||||
@test nC3 != nC4
|
||||
|
||||
nC1_2 = copy(nC1)
|
||||
@test nC1_2 != nC1
|
||||
|
||||
nD1_2 = copy(nD1)
|
||||
@test nD1_2 != nD1
|
||||
|
||||
nD1_c = MetagraphOptimization.make_node(MetagraphOptimization.DataTask(10))
|
||||
@test nD1_c != nD1
|
||||
end
|
||||
println("Node Unit Tests Complete!")
|
60
test/unit_tests_tasks.jl
Normal file
60
test/unit_tests_tasks.jl
Normal file
@ -0,0 +1,60 @@
|
||||
|
||||
@testset "Task Unit Tests" begin
|
||||
S1 = MetagraphOptimization.ComputeTaskS1()
|
||||
S2 = MetagraphOptimization.ComputeTaskS2()
|
||||
U = MetagraphOptimization.ComputeTaskU()
|
||||
V = MetagraphOptimization.ComputeTaskV()
|
||||
P = MetagraphOptimization.ComputeTaskP()
|
||||
Sum = MetagraphOptimization.ComputeTaskSum()
|
||||
|
||||
Data10 = MetagraphOptimization.DataTask(10)
|
||||
Data20 = MetagraphOptimization.DataTask(20)
|
||||
|
||||
@test MetagraphOptimization.compute_effort(S1) == 10
|
||||
@test MetagraphOptimization.compute_effort(S2) == 10
|
||||
@test MetagraphOptimization.compute_effort(U) == 6
|
||||
@test MetagraphOptimization.compute_effort(V) == 20
|
||||
@test MetagraphOptimization.compute_effort(P) == 15
|
||||
@test MetagraphOptimization.compute_effort(Sum) == 1
|
||||
@test MetagraphOptimization.compute_effort(Data10) == 0
|
||||
@test MetagraphOptimization.compute_effort(Data20) == 0
|
||||
|
||||
@test MetagraphOptimization.data(S1) == 0
|
||||
@test MetagraphOptimization.data(S2) == 0
|
||||
@test MetagraphOptimization.data(U) == 0
|
||||
@test MetagraphOptimization.data(V) == 0
|
||||
@test MetagraphOptimization.data(P) == 0
|
||||
@test MetagraphOptimization.data(Sum) == 0
|
||||
@test MetagraphOptimization.data(Data10) == 10
|
||||
@test MetagraphOptimization.data(Data20) == 20
|
||||
|
||||
@test MetagraphOptimization.compute_intensity(S1) == typemax(UInt64)
|
||||
@test MetagraphOptimization.compute_intensity(S2) == typemax(UInt64)
|
||||
@test MetagraphOptimization.compute_intensity(U) == typemax(UInt64)
|
||||
@test MetagraphOptimization.compute_intensity(V) == typemax(UInt64)
|
||||
@test MetagraphOptimization.compute_intensity(P) == typemax(UInt64)
|
||||
@test MetagraphOptimization.compute_intensity(Sum) == typemax(UInt64)
|
||||
@test MetagraphOptimization.compute_intensity(Data10) == 0
|
||||
@test MetagraphOptimization.compute_intensity(Data20) == 0
|
||||
|
||||
@test S1 != S2
|
||||
@test Data10 != Data20
|
||||
|
||||
Data10_2 = MetagraphOptimization.DataTask(10)
|
||||
|
||||
# two data tasks with same data are identical, their nodes need not be
|
||||
@test Data10_2 == Data10
|
||||
|
||||
@test Data10 == Data10
|
||||
@test S1 == S1
|
||||
|
||||
Data10_3 = copy(Data10)
|
||||
|
||||
@test Data10_3 == Data10
|
||||
|
||||
S1_2 = copy(S1)
|
||||
|
||||
@test S1_2 == S1
|
||||
@test S1 == MetagraphOptimization.ComputeTaskS1()
|
||||
end
|
||||
println("Task Unit Tests Complete!")
|
11
test/unit_tests_utility.jl
Normal file
11
test/unit_tests_utility.jl
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
@testset "Unit Tests Utility" begin
|
||||
@test MetagraphOptimization.bytes_to_human_readable(0) == "0.0 B"
|
||||
@test MetagraphOptimization.bytes_to_human_readable(1020) == "1020.0 B"
|
||||
@test MetagraphOptimization.bytes_to_human_readable(1025) == "1.001 KiB"
|
||||
@test MetagraphOptimization.bytes_to_human_readable(684235) == "668.2 KiB"
|
||||
@test MetagraphOptimization.bytes_to_human_readable(86214576) == "82.22 MiB"
|
||||
@test MetagraphOptimization.bytes_to_human_readable(9241457698) == "8.607 GiB"
|
||||
@test MetagraphOptimization.bytes_to_human_readable(3218598654367) == "2.927 TiB"
|
||||
end
|
||||
println("Utility Unit Tests Complete!")
|
Loading…
x
Reference in New Issue
Block a user