-
Notifications
You must be signed in to change notification settings - Fork 23
Changes to the iterator interface #255
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: network_solvers
Are you sure you want to change the base?
Changes from 3 commits
7a51966
245182b
7af4b25
c0ae5d0
3b9d0af
4ef4e75
e112eb4
da360e0
8bfc483
1ef8498
0a6e891
0653c47
d77321e
aff14c7
4b21cc9
e71512f
a4ce308
a8b2c51
18a8503
0c9022c
a9be11e
4d52088
613d533
20bf783
568c631
fed9137
4ce453e
c59a9c5
62195b6
917f2f1
112d55e
0a9f127
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,100 +1,158 @@ | ||||||
# | ||||||
# SweepIterator | ||||||
# | ||||||
|
||||||
mutable struct SweepIterator | ||||||
sweep_kws | ||||||
region_iter | ||||||
which_sweep::Int | ||||||
end | ||||||
|
||||||
problem(S::SweepIterator) = problem(S.region_iter) | ||||||
""" | ||||||
abstract type AbstractNetworkIterator | ||||||
Base.length(S::SweepIterator) = length(S.sweep_kws) | ||||||
A stateful iterator with two states: `increment!` and `compute!`. Each iteration begins | ||||||
with a call to `increment!` before executing `compute!`, however the initial call to | ||||||
`iterate` skips the `increment!` call as it is assumed the iterator is initalized such that | ||||||
this call is implict. Termination of the iterator is controlled by the function `done`. | ||||||
""" | ||||||
abstract type AbstractNetworkIterator end | ||||||
|
||||||
function Base.iterate(S::SweepIterator, which=nothing) | ||||||
if isnothing(which) | ||||||
sweep_kws_state = iterate(S.sweep_kws) | ||||||
else | ||||||
sweep_kws_state = iterate(S.sweep_kws, which) | ||||||
end | ||||||
isnothing(sweep_kws_state) && return nothing | ||||||
current_sweep_kws, next = sweep_kws_state | ||||||
# We use greater than or equals here as we increment the state at the start of the iteration | ||||||
done(NI::AbstractNetworkIterator) = state(NI) >= length(NI) | ||||||
|
||||||
if !isnothing(which) | ||||||
S.region_iter = region_iterator( | ||||||
problem(S.region_iter); sweep=S.which_sweep, current_sweep_kws... | ||||||
) | ||||||
end | ||||||
S.which_sweep += 1 | ||||||
return S.region_iter, next | ||||||
function Base.iterate(NI::AbstractNetworkIterator, init=true) | ||||||
done(NI) && return nothing | ||||||
# We seperate increment! from step! and demand that any AbstractNetworkIterator *must* | ||||||
# define a method for increment! This way we avoid cases where one may wish to nest | ||||||
# calls to different step! methods accidentaly incrementing multiple times. | ||||||
init || increment!(NI) | ||||||
rv = compute!(NI) | ||||||
return rv, false | ||||||
end | ||||||
|
||||||
function sweep_iterator(problem, sweep_kws) | ||||||
region_iter = region_iterator(problem; sweep=1, first(sweep_kws)...) | ||||||
return SweepIterator(sweep_kws, region_iter, 1) | ||||||
end | ||||||
function increment! end | ||||||
compute!(NI::AbstractNetworkIterator) = NI | ||||||
|
||||||
function sweep_iterator(problem, nsweeps::Integer; sweep_kws...) | ||||||
return sweep_iterator(problem, Iterators.repeated(sweep_kws, nsweeps)) | ||||||
step!(NI::AbstractNetworkIterator) = step!(identity, NI) | ||||||
function step!(f, NI::AbstractNetworkIterator) | ||||||
compute!(NI) | ||||||
f(NI) | ||||||
increment!(NI) | ||||||
return NI | ||||||
end | ||||||
|
||||||
# | ||||||
# RegionIterator | ||||||
# | ||||||
|
||||||
@kwdef mutable struct RegionIterator{Problem,RegionPlan} | ||||||
""" | ||||||
struct RegionIterator{Problem, RegionPlan} <: AbstractNetworkIterator | ||||||
""" | ||||||
mutable struct RegionIterator{Problem,RegionPlan} <: AbstractNetworkIterator | ||||||
problem::Problem | ||||||
region_plan::RegionPlan | ||||||
which_region::Int = 1 | ||||||
const sweep::Int | ||||||
which_region::Int | ||||||
function RegionIterator(problem::P, region_plan::R, sweep::Int) where {P,R} | ||||||
return new{P,R}(problem, region_plan, sweep, 1) | ||||||
end | ||||||
end | ||||||
|
||||||
state(R::RegionIterator) = R.which_region | ||||||
Base.length(R::RegionIterator) = length(R.region_plan) | ||||||
|
||||||
problem(R::RegionIterator) = R.problem | ||||||
|
||||||
current_region_plan(R::RegionIterator) = R.region_plan[R.which_region] | ||||||
current_region(R::RegionIterator) = current_region_plan(R)[1] | ||||||
region_kwargs(R::RegionIterator) = current_region_plan(R)[2] | ||||||
function previous_region(R::RegionIterator) | ||||||
R.which_region==1 ? nothing : R.region_plan[R.which_region - 1][1] | ||||||
|
||||||
function current_region(R::RegionIterator) | ||||||
region, _ = current_region_plan(R) | ||||||
return region | ||||||
end | ||||||
function next_region(R::RegionIterator) | ||||||
R.which_region==length(R.region_plan) ? nothing : R.region_plan[R.which_region + 1][1] | ||||||
|
||||||
function current_region_kwargs(R::RegionIterator) | ||||||
_, kwargs = current_region_plan(R) | ||||||
return kwargs | ||||||
end | ||||||
|
||||||
function previous_region(R::RegionIterator) | ||||||
state(R) <= 1 && return nothing | ||||||
prev, _ = R.region_plan[R.which_region - 1] | ||||||
return prev | ||||||
end | ||||||
is_last_region(R::RegionIterator) = isnothing(next_region(R)) | ||||||
|
||||||
function Base.iterate(R::RegionIterator, which=1) | ||||||
R.which_region = which | ||||||
region_plan_state = iterate(R.region_plan, which) | ||||||
isnothing(region_plan_state) && return nothing | ||||||
(current_region, region_kwargs), next = region_plan_state | ||||||
R.problem = region_step(problem(R), R; region_kwargs...) | ||||||
return R, next | ||||||
function next_region(R::RegionIterator) | ||||||
is_last_region(R) && return nothing | ||||||
next, _ = R.region_plan[R.which_region + 1] | ||||||
return next | ||||||
end | ||||||
is_last_region(R::RegionIterator) = length(R) === state(R) | ||||||
|
||||||
# | ||||||
# Functions associated with RegionIterator | ||||||
# | ||||||
|
||||||
function region_iterator(problem; sweep_kwargs...) | ||||||
return RegionIterator(; problem, region_plan=region_plan(problem; sweep_kwargs...)) | ||||||
function compute!(R::RegionIterator) | ||||||
region_kwargs = current_region_kwargs(R) | ||||||
R.problem = region_step(R; region_kwargs...) | ||||||
return R | ||||||
end | ||||||
function increment!(R::RegionIterator) | ||||||
R.which_region += 1 | ||||||
return R | ||||||
end | ||||||
|
||||||
function RegionIterator(problem; sweep, sweep_kwargs...) | ||||||
plan = region_plan(problem; sweep, sweep_kwargs...) | ||||||
return RegionIterator(problem, plan, sweep) | ||||||
end | ||||||
|
||||||
function region_step( | ||||||
problem, | ||||||
region_iterator; | ||||||
extract_kwargs=(;), | ||||||
update_kwargs=(;), | ||||||
insert_kwargs=(;), | ||||||
sweep, | ||||||
kws..., | ||||||
region_iterator; extract_kwargs=(;), update_kwargs=(;), insert_kwargs=(;), kws... | ||||||
) | ||||||
problem, local_state = extract(problem, region_iterator; extract_kwargs..., sweep, kws...) | ||||||
problem, local_state = update( | ||||||
problem, local_state, region_iterator; update_kwargs..., kws... | ||||||
) | ||||||
problem = insert(problem, local_state, region_iterator; sweep, insert_kwargs..., kws...) | ||||||
return problem | ||||||
prob = problem(region_iterator) | ||||||
|
||||||
sweep = region_iterator.sweep | ||||||
|
||||||
prob, local_state = extract(prob, region_iterator; extract_kwargs..., sweep, kws...) | ||||||
prob, local_state = update(prob, local_state, region_iterator; update_kwargs..., kws...) | ||||||
prob = insert(prob, local_state, region_iterator; sweep, insert_kwargs..., kws...) | ||||||
return prob | ||||||
end | ||||||
|
||||||
function region_plan(problem; kws...) | ||||||
return euler_sweep(state(problem); kws...) | ||||||
end | ||||||
|
||||||
# | ||||||
# SweepIterator | ||||||
# | ||||||
|
||||||
mutable struct SweepIterator{Problem} <: AbstractNetworkIterator | ||||||
sweep_kws | ||||||
|
||||||
region_iter::RegionIterator{Problem} | ||||||
which_sweep::Int | ||||||
function SweepIterator(problem, sweep_kws) | ||||||
sweep_kws = Iterators.Stateful(sweep_kws) | ||||||
|
sweep_kws = Iterators.Stateful(sweep_kws) | |
stateful_sweep_kws = Iterators.Stateful(sweep_kws) |
I would find that a bit clearer (i.e. it is easier to keep track that we are now using the stateful version of sweep_kws
).
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Related to our discussion around how the RegionIterator
gets updated and how the problem
gets passed around, maybe it could help to wrap this constructor call into a function, for example:
function update_region_iterator(iterator::RegionIterator; kwargs...)
return RegionIterator(iterator.problem; kwargs...)
end
or something like that (mostly to hide the detail of how the problem
gets passed along at this level of the code).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, what does SR
stand for? Generally I prefer lower case variable names, and also more descriptive ones, say sweep_iter
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think SR
was an abbreviation of SweepRegionIterator
which was a previous name for something I was using previously...
On the first point, I feel like update_region_iterator
is not really descriptive of what the function as implemented there does. It does not update an existing RegionIterator
, it creates a new one via the constructor. I do like however the following:
function update_region_iterator!(iterator::SweepIterator; kwargs...)
iterator.region_iter = new_region_iterator(iterator.region_iter; kwargs...)
return iterator
end
where new_region_iterator
is a renaming of the method you describe. Let me know if you agree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does not update an existing
RegionIterator
, it creates a new one via the constructor. I do like however the following:
I suppose that's a matter of perspective, i.e. we could think of it as a fancy kind of "setter" method that keeps some properties and sets other ones, which for immutable types has to create a new object by definition, but I'm ok with new_region_iterator
.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a small recommendation, I would prefer a variable name besides
NC
, maybe we could just useiterator
.