From 43d07b10f1a35b3144e305d83bf970546783d1bc Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 28 Oct 2025 10:37:57 -0400 Subject: [PATCH 1/5] Fix bug with AbstractNetworkIterator types of length 1 not executing their single `compute!` call --- src/solvers/iterators.jl | 5 ++++- test/solvers/test_iterators.jl | 13 +++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/solvers/iterators.jl b/src/solvers/iterators.jl index 19b99c9a..b7edae78 100644 --- a/src/solvers/iterators.jl +++ b/src/solvers/iterators.jl @@ -8,7 +8,10 @@ abstract type AbstractNetworkIterator end islaststep(iterator::AbstractNetworkIterator) = state(iterator) >= length(iterator) function Base.iterate(iterator::AbstractNetworkIterator, init = true) - islaststep(iterator) && return nothing + # The assumption is that first "increment!" is implicit, therefore we must skip the + # the termination check for the first iteration, i.e. `AbstractNetworkIterator` is not + # defined when length < 1, + init || islaststep(iterator) && 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. diff --git a/test/solvers/test_iterators.jl b/test/solvers/test_iterators.jl index 8095f81d..0556007d 100644 --- a/test/solvers/test_iterators.jl +++ b/test/solvers/test_iterators.jl @@ -47,6 +47,19 @@ end import .TestIteratorUtils @testset "`AbstractNetworkIterator` Interface" begin + + @testset "Edge cases" begin + TI = TestIteratorUtils.TestIterator(1, 1, []) + cb = [] + @test islaststep(TI) + for _ in TI + @test islaststep(TI) + push!(cb, state(TI)) + end + @test length(cb) == 1 + @test length(TI.output) == 1 + @test only(cb) == 1 + end TI = TestIteratorUtils.TestIterator(1, 4, []) @test !islaststep((TI)) From ca1b22ab7399254c1095b7c3dac5195e54704876 Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 28 Oct 2025 10:39:06 -0400 Subject: [PATCH 2/5] Constructing a `SweepIterator` and `RegionIterator` of length 0 now gives `BoundsError`. Constructing `RegionIterator` of length 0 is now undefined. Adjust region iterator `BoundsError` message to align with `SweepIterator`. --- src/solvers/iterators.jl | 12 +++++++++++- test/solvers/test_iterators.jl | 9 +++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/solvers/iterators.jl b/src/solvers/iterators.jl index b7edae78..c1be61fe 100644 --- a/src/solvers/iterators.jl +++ b/src/solvers/iterators.jl @@ -40,6 +40,9 @@ mutable struct RegionIterator{Problem, RegionPlan} <: AbstractNetworkIterator which_region::Int const which_sweep::Int function RegionIterator(problem::P, region_plan::R, sweep::Int) where {P, R} + if length(region_plan) == 0 + throw(BoundsError("Cannot construct a region iterator with 0 elements.")) + end return new{P, R}(problem, region_plan, 1, sweep) end end @@ -115,8 +118,15 @@ mutable struct SweepIterator{Problem, Iter} <: AbstractNetworkIterator which_sweep::Int function SweepIterator(problem::Prob, sweep_kwargs::Iter) where {Prob, Iter} stateful_sweep_kwargs = Iterators.Stateful(sweep_kwargs) - first_kwargs, _ = Iterators.peel(stateful_sweep_kwargs) + first_state = Iterators.peel(stateful_sweep_kwargs) + + if isnothing(first_state) + throw(BoundsError("Cannot construct a sweep iterator with 0 elements.")) + end + + first_kwargs, _ = first_state region_iter = RegionIterator(problem; sweep = 1, first_kwargs...) + return new{Prob, Iter}(region_iter, stateful_sweep_kwargs, 1) end end diff --git a/test/solvers/test_iterators.jl b/test/solvers/test_iterators.jl index 0556007d..eb8ee75c 100644 --- a/test/solvers/test_iterators.jl +++ b/test/solvers/test_iterators.jl @@ -1,5 +1,5 @@ -using ITensorNetworks: SweepIterator, compute!, eachregion, increment!, islaststep, state -using Test: @test, @testset +using Test: @test, @testset, @test_throws +using ITensorNetworks: SweepIterator, RegionIterator, islaststep, state, increment!, compute!, eachregion module TestIteratorUtils @@ -59,7 +59,12 @@ end @test length(cb) == 1 @test length(TI.output) == 1 @test only(cb) == 1 + + prob = TestIteratorUtils.TestProblem([]) + @test_throws BoundsError SweepIterator(prob, 0) + @test_throws BoundsError RegionIterator(prob, [], 1) end + TI = TestIteratorUtils.TestIterator(1, 4, []) @test !islaststep((TI)) From 9884215a8961999bd9068a7821aee0a4eb26e844 Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 28 Oct 2025 12:50:02 -0400 Subject: [PATCH 3/5] Add test for single element `EachRegion` types --- test/solvers/test_iterators.jl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/solvers/test_iterators.jl b/test/solvers/test_iterators.jl index eb8ee75c..f63b6c6e 100644 --- a/test/solvers/test_iterators.jl +++ b/test/solvers/test_iterators.jl @@ -183,6 +183,18 @@ end @test prob.data[1:2:end] == fill(1, 5) @test prob.data[2:2:end] == fill(2, 5) + + + let i = 1, prob = TestIteratorUtils.TestProblem([]) + SI = SweepIterator(prob, 1) + cb = [] + for _ in eachregion(SI) + push!(cb, i) + i += 1 + end + @test length(cb) == 2 + end + end end end From b5b4a096b993e318b7ab62d1e604c34010236609 Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 14 Apr 2026 14:33:48 -0400 Subject: [PATCH 4/5] Formatting --- test/solvers/test_iterators.jl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test/solvers/test_iterators.jl b/test/solvers/test_iterators.jl index f63b6c6e..de9522b2 100644 --- a/test/solvers/test_iterators.jl +++ b/test/solvers/test_iterators.jl @@ -1,5 +1,6 @@ -using Test: @test, @testset, @test_throws -using ITensorNetworks: SweepIterator, RegionIterator, islaststep, state, increment!, compute!, eachregion +using ITensorNetworks: + RegionIterator, SweepIterator, compute!, eachregion, increment!, islaststep, state +using Test: @test, @test_throws, @testset module TestIteratorUtils @@ -47,7 +48,6 @@ end import .TestIteratorUtils @testset "`AbstractNetworkIterator` Interface" begin - @testset "Edge cases" begin TI = TestIteratorUtils.TestIterator(1, 1, []) cb = [] @@ -184,7 +184,6 @@ end @test prob.data[1:2:end] == fill(1, 5) @test prob.data[2:2:end] == fill(2, 5) - let i = 1, prob = TestIteratorUtils.TestProblem([]) SI = SweepIterator(prob, 1) cb = [] @@ -194,7 +193,6 @@ end end @test length(cb) == 2 end - end end end From 8bf5f8f6b120b94172c5ff21fa06b5132b1d7b4d Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 14 Apr 2026 14:44:05 -0400 Subject: [PATCH 5/5] Version bump. --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 6a3e077b..afd02cb4 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "ITensorNetworks" uuid = "2919e153-833c-4bdc-8836-1ea460a35fc7" -version = "0.15.26" +version = "0.15.27" authors = ["Matthew Fishman , Joseph Tindall and contributors"] [workspace]