Skip to content

Commit fe3ac5c

Browse files
committed
Simplify parallel code.
1 parent dbc6067 commit fe3ac5c

5 files changed

Lines changed: 140 additions & 74 deletions

File tree

ext/ITensorNetworksNextDaggerExt/ITensorNetworksNextDaggerExt.jl

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,24 @@ import ITensorNetworksNext.ITensorNetworksNextParallel as ITNNP
66
using Dagger
77
using ITensorNetworksNext.ITensorNetworksNextParallel:
88
DaggerNestedAlgorithm, DaggerState, ITensorNetworksNextParallel
9+
using Dictionaries: set!
910

10-
function ITNNP.DaggerNestedAlgorithm(f::Function, iterable; workers = workers(), kwargs...)
11-
return DaggerNestedAlgorithm(; algorithms = map(f, iterable), workers, kwargs...)
11+
function ITNNP.DaggerNestedAlgorithm(f, iterable; kwargs...)
12+
return DaggerNestedAlgorithm(; algorithms = map(f, iterable), kwargs...)
1213
end
1314

14-
function initialize_dagger_state(problem::AIE.Problem, algorithm::AIE.Algorithm; iterate)
15+
function ITNNP.dagger_algorithm(f::Base.Callable, iterable; kwargs...)
16+
return DaggerNestedAlgorithm(f, iterable; kwargs...)
17+
end
18+
19+
function ITNNP.initialize_dagger_state(
20+
problem::AIE.Problem, algorithm::AIE.Algorithm; iterate
21+
)
1522
stopping_criterion_state = AI.initialize_state(
1623
problem, algorithm, algorithm.stopping_criterion
1724
)
1825

19-
remote_results = Dict{Int, Dagger.DTask}()
26+
remote_results = Dictionary{Int, Dagger.DTask}()
2027

2128
return ITNNP.DaggerState(;
2229
iterate,
@@ -30,37 +37,23 @@ function AI.initialize_state(
3037
algorithm::ITNNP.DaggerNestedAlgorithm;
3138
kwargs...
3239
)
33-
return initialize_dagger_state(problem, algorithm; kwargs...)
40+
return ITNNP.initialize_dagger_state(problem, algorithm; kwargs...)
3441
end
3542

36-
function AIE.get_subproblem(
43+
function AI.step!(
3744
problem::AIE.Problem,
3845
algorithm::ITNNP.DaggerNestedAlgorithm,
39-
state::ITNNP.DaggerState
46+
state::ITNNP.DaggerState;
47+
kwargs...
4048
)
4149
subproblem = problem
4250
subalgorithm = algorithm.algorithms[state.iteration]
4351

44-
# This might be a Dagger.chun object.
4552
iterate = ITNNP.get_subiterate(subproblem, subalgorithm, state)
4653

47-
substate = Dagger.@spawn AI.initialize_state(subproblem, subalgorithm; iterate)
48-
49-
return subproblem, subalgorithm, substate
50-
end
51-
52-
function AI.step!(
53-
problem::AI.Problem,
54-
algorithm::ITNNP.DaggerNestedAlgorithm,
55-
state::ITNNP.DaggerState;
56-
kwargs...
57-
)
58-
subproblem, subalgorithm, substate_future =
59-
AIE.get_subproblem(problem, algorithm, state)
60-
61-
dtask = Dagger.@spawn AI.solve(subproblem, subalgorithm, substate_future)
54+
dtask = Dagger.@spawn AI.solve(subproblem, subalgorithm; iterate)
6255

63-
state.remote_results[state.iteration] = dtask
56+
set!(state.remote_results, state.iteration, dtask)
6457

6558
return state
6659
end

ext/ITensorNetworksNextDaggerExt/daggerbeliefpropagation.jl

Lines changed: 68 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,49 @@
11
import ITensorNetworksNext.ITensorNetworksNextParallel as ITNNP
22
using Dagger
3-
using DataGraphs: DataGraphs, get_edge_data, get_vertex_data, is_edge_assigned,
3+
using DataGraphs: DataGraphs, edge_data, get_edge_data, get_vertex_data, is_edge_assigned,
44
is_vertex_assigned, set_edge_data!, set_vertex_data!, underlying_graph
5-
using Dictionaries: Indices
5+
using Dictionaries: Dictionary, Indices, getindices
66
using Graphs: AbstractEdge, AbstractGraph, dst, edges, src, vertices
77
using ITensorNetworksNext: ITensorNetworksNext, BeliefPropagation, BeliefPropagationCache,
88
BeliefPropagationProblem, BeliefPropagationState, beliefpropagation,
9-
forest_cover_edge_sequence, select_algorithm
9+
forest_cover_edge_sequence, select_algorithm, subcache
1010
using NamedGraphs.GraphsExtensions: boundary_edges
1111
using NamedGraphs.PartitionedGraphs: QuotientVertex, quotientedges, quotientvertices
1212
using NamedGraphs: NamedGraphs
1313

14-
function ITNNP.DaggerBeliefPropagationCache(network::AbstractGraph)
14+
const DaggerBeliefPropagation = BeliefPropagation{<:ITNNP.DaggerNestedAlgorithm};
15+
16+
function ITNNP.DaggerBeliefPropagationCache(
17+
network::AbstractGraph;
18+
workers = nothing,
19+
scopes = nothing
20+
)
1521
underlying_cache = BeliefPropagationCache(network)
1622

1723
keys = Indices(quotientvertices(underlying_cache))
1824

19-
workers = Iterators.cycle(Dagger.Distributed.workers())
20-
worker_dict = similar(keys, Int)
25+
if isnothing(scopes)
26+
workers = isnothing(workers) ? Dagger.Distributed.workers() : workers
2127

22-
for quotient_vertex in keys
23-
worker, workers = Iterators.peel(workers)
24-
worker_dict[quotient_vertex] = worker
28+
sorted_workers = Iterators.take(Iterators.cycle(workers), length(keys))
29+
30+
scopes = map(Dagger.ProcessScope, collect(sorted_workers))
31+
else
32+
if length(keys) != length(scopes)
33+
throw(
34+
ArgumentError(
35+
"Number of provided scopes must match the number of vertex partitions of underlying graph"
36+
)
37+
)
38+
end
2539
end
2640

41+
scope_dict = Dictionary(keys, scopes)
42+
2743
quotient_chunks = map(keys) do quotient_vertex
28-
worker = worker_dict[quotient_vertex]
44+
scope = scope_dict[quotient_vertex]
2945
iterate = subcache(underlying_cache, quotient_vertex)
30-
chunk = Dagger.@mutable worker = worker BeliefPropagationState(; iterate)
46+
chunk = Dagger.@mutable scope = scope BeliefPropagationState(; iterate)
3147
return chunk
3248
end
3349

@@ -42,7 +58,7 @@ function DataGraphs.is_vertex_assigned(bpc::ITNNP.DaggerBeliefPropagationCache,
4258
return is_vertex_assigned(bpc.underlying_cache, vertex)
4359
end
4460
function DataGraphs.is_edge_assigned(bpc::ITNNP.DaggerBeliefPropagationCache, edge)
45-
return is_edge_assigned(bpc.undelying_cache, edge)
61+
return is_edge_assigned(bpc.underlying_cache, edge)
4662
end
4763

4864
function DataGraphs.get_vertex_data(bpc::ITNNP.DaggerBeliefPropagationCache, vertex)
@@ -52,7 +68,7 @@ function DataGraphs.get_edge_data(
5268
bpc::ITNNP.DaggerBeliefPropagationCache,
5369
edge::AbstractEdge
5470
)
55-
return get_edge_data(bpc.undelying_caches, edge)
71+
return get_edge_data(bpc.underlying_cache, edge)
5672
end
5773

5874
function DataGraphs.set_vertex_data!(bpc::ITNNP.DaggerBeliefPropagationCache, val, vertex)
@@ -73,13 +89,11 @@ end
7389
function ITensorNetworksNext.beliefpropagation_sweep(
7490
cache::ITNNP.DaggerBeliefPropagationCache;
7591
edges,
76-
workers = workers(),
7792
kwargs...
7893
)
79-
keys = collect(quotientvertices(cache))
80-
81-
return ITNNP.dagger_algorithm(keys; keys, workers) do quotient_vertex
82-
subcache = fetch(cache[quotient_vertex]).iterate
94+
return ITNNP.dagger_algorithm(quotientvertices(cache)) do quotient_vertex
95+
substate = fetch(cache[quotient_vertex])
96+
subcache = substate.iterate
8397

8498
subcache_edges = forest_cover_edge_sequence(subcache) edges
8599
incoming_edges = boundary_edges(cache, vertices(cache, quotient_vertex); dir = :in)
@@ -120,44 +134,61 @@ function ITNNP.get_subiterate(
120134
end
121135

122136
function AIE.set_substate!(
123-
::BeliefPropagationProblem,
124-
::AIE.NestedAlgorithm,
137+
problem::BeliefPropagationProblem,
138+
algorithm::AIE.NestedAlgorithm,
125139
state::AIE.State,
126140
substate::ITNNP.DaggerState
127141
)
128142
dst_cache = state.iterate.iterate
129143

130144
state.iterate.maxdiff = 0.0
131145

132-
for remote_result in substate.remote_results
133-
get_maxdiff = dtask -> dtask.iterate.maxdiff
134-
src_maxdiff = fetch(Dagger.@spawn get_maxdiff(remote_result))
135-
136-
if src_maxdiff > state.iterate.maxdiff
137-
state.iterate.maxdiff = src_maxdiff
138-
end
146+
maxdiff_dtasks = map(substate.remote_results) do remote_result
147+
return Dagger.spawn(dtask -> dtask.iterate.maxdiff, remote_result)
139148
end
140149

141-
function transfer_edges!(dst_chunk, src_chunk, edges)
142-
src_subcache = src_chunk.iterate
143-
dst_subcache = dst_chunk.iterate
144-
for edge in edges
145-
dst_subcache[edge] = src_subcache[edge]
146-
end
147-
return
150+
maxdiff = maximum(fetch, maxdiff_dtasks)
151+
152+
if maxdiff > state.iterate.maxdiff
153+
state.iterate.maxdiff = maxdiff
148154
end
149155

150156
transfer_dtasks = map(quotientedges(dst_cache)) do quotient_edge
151157
src_subcache = dst_cache[src(quotient_edge)]
152158
dst_subcache = dst_cache[dst(quotient_edge)]
153-
return Dagger.@spawn transfer_edges!(
159+
160+
src_subcache = fetch(src_subcache)
161+
162+
return Dagger.spawn(
154163
dst_subcache,
155164
fetch(src_subcache),
156165
edges(dst_cache, quotient_edge)
157-
)
166+
) do dst, src, edges
167+
src_subcache = src.iterate
168+
dst_subcache = dst.iterate
169+
for edge in edges
170+
dst_subcache[edge] = src_subcache[edge]
171+
end
172+
end
158173
end
159174

160-
wait.(transfer_dtasks)
175+
foreach(wait, transfer_dtasks)
176+
177+
return state
178+
end
179+
180+
function ITNNP.finalize_state!(
181+
::BeliefPropagationProblem,
182+
::BeliefPropagation,
183+
state::ITNNP.DaggerState
184+
)
185+
dst_cache = state.iterate.iterate
186+
187+
for quotient_vertex in quotientvertices(dst_cache)
188+
substate = fetch(dst_cache[quotient_vertex])
189+
subcache = substate.iterate
190+
edge_data(dst_cache) .= edge_data(subcache)
191+
end
161192

162193
return state
163194
end

src/ITensorNetworksNextParallel/ITensorNetworksNextParallel.jl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
module ITensorNetworksNextParallel
22

33
import AlgorithmsInterface as AI
4+
import ITensorNetworksNext.AlgorithmsInterfaceExtensions as AIE
5+
6+
abstract type ParallelAlgorithm{Child} <: AIE.NestedAlgorithm{Child} end
7+
const IterativeParallelAlgorithm{Child <: ParallelAlgorithm} = AIE.NestedAlgorithm{Child}
48

59
"""
610
get_subiterate(subproblem::AI.Problem, subalgorithm::AI.Algorithm, state::AI.State)
@@ -11,6 +15,25 @@ The returned value of this function is then pass to a remote call of `initialize
1115
"""
1216
get_subiterate(::AI.Problem, ::AI.Algorithm, state::AI.State) = state.iterate
1317

18+
finalize_state!(::AI.Problem, ::AI.Algorithm, state::AI.State) = state
19+
20+
function AI.is_finished!(
21+
problem::AI.Problem,
22+
algorithm::IterativeParallelAlgorithm,
23+
state::AI.State
24+
)
25+
c = algorithm.stopping_criterion
26+
st = state.stopping_criterion_state
27+
28+
isfinished = AI.is_finished!(problem, algorithm, state, c, st)
29+
30+
if isfinished
31+
finalize_state!(problem, algorithm, state)
32+
end
33+
34+
return isfinished
35+
end
36+
1437
include("dagger.jl")
1538

1639
end # ITensorNetworksNextParallel

src/ITensorNetworksNextParallel/dagger.jl

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,40 @@
11
import ..ITensorNetworksNext.AlgorithmsInterfaceExtensions as AIE
22
import AlgorithmsInterface as AI
3+
using Dictionaries: Dictionary
34
using ITensorNetworksNext: AbstractBeliefPropagationCache
45

56
@kwdef mutable struct DaggerState{
6-
Iterate, StoppingCriterionState <: AI.StoppingCriterionState, Chunk, DTask,
7+
Iterate, StoppingCriterionState <: AI.StoppingCriterionState, DTask,
78
} <: AIE.State
89
iterate::Iterate # DaggerBeliefPropagationCache
910
iteration::Int = 0
1011
stopping_criterion_state::StoppingCriterionState
11-
# remote_subiterates::Dict{Int, Chunk} = Dict{Int, Any}()
12-
remote_results::Dict{Int, DTask} = Dict{Int, Any}()
12+
remote_results::Dictionary{Int, DTask} = Dict{Int, Any}()
13+
end
14+
15+
function initialize_dagger_state(problem, algorithm; kwargs...)
16+
throw(
17+
ErrorException(
18+
"Package Dagger not loaded. Please install and load the Dagger package."
19+
)
20+
)
1321
end
1422

1523
@kwdef struct DaggerNestedAlgorithm{
1624
ChildAlgorithm <: AIE.Algorithm,
1725
Algorithms <: AbstractVector{ChildAlgorithm},
1826
StoppingCriterion <: AI.StoppingCriterion,
19-
KeyType,
20-
} <: AIE.NestedAlgorithm
27+
} <: ParallelAlgorithm{ChildAlgorithm}
2128
algorithms::Algorithms
2229
stopping_criterion::StoppingCriterion = AI.StopAfterIteration(length(algorithms))
23-
workers::Vector{Int}
24-
keys::Vector{KeyType} = collect(1:length(algorithms))
2530
end
2631

27-
function dagger_algorithm(f::Function, iterable; kwargs...)
28-
return DaggerNestedAlgorithm(f, iterable; kwargs...)
32+
function dagger_algorithm(f, iterable; kwargs...)
33+
throw(
34+
ErrorException(
35+
"Package Dagger not loaded. Please install and load the Dagger package."
36+
)
37+
)
2938
end
3039

3140
# ================================== belief propagation ================================== #

src/beliefpropagation/beliefpropagationproblem.jl

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ function AI.initialize_state!(
3030
end
3131

3232
function AI.is_finished!(
33-
::AIE.Problem,
34-
::AIE.Algorithm,
33+
problem::AIE.Problem,
34+
algorithm::AIE.Algorithm,
3535
state::AIE.State,
3636
c::StopWhenConverged,
3737
st::StopWhenConvergedState
@@ -42,6 +42,16 @@ function AI.is_finished!(
4242
st.delta = state.iterate.maxdiff
4343
end
4444

45+
return AI.is_finished(problem, algorithm, state, c, st)
46+
end
47+
48+
function AI.is_finished(
49+
::AIE.Problem,
50+
::AIE.Algorithm,
51+
::AIE.State,
52+
c::StopWhenConverged,
53+
st::StopWhenConvergedState
54+
)
4555
return st.delta < c.tol
4656
end
4757

@@ -60,7 +70,7 @@ end
6070
ChildAlgorithm <: AIE.Algorithm,
6171
Algorithms <: AbstractVector{ChildAlgorithm},
6272
StoppingCriterion <: AI.StoppingCriterion,
63-
} <: AIE.NestedAlgorithm
73+
} <: AIE.NestedAlgorithm{ChildAlgorithm}
6474
algorithms::Algorithms
6575
stopping_criterion::StoppingCriterion = AI.StopAfterIteration(length(algorithms))
6676
end
@@ -100,7 +110,7 @@ end
100110
struct BeliefPropagationSweep{
101111
ChildAlgorithm <: AIE.Algorithm,
102112
Algorithms <: AbstractVector{ChildAlgorithm},
103-
} <: AIE.NestedAlgorithm
113+
} <: AIE.NestedAlgorithm{ChildAlgorithm}
104114
algorithms::Algorithms
105115
stopping_criterion::AI.StopAfterIteration
106116
function BeliefPropagationSweep(; algorithms)

0 commit comments

Comments
 (0)