Skip to content

Refactor VUMPS and VOMPS to avoid boxed variables #265

Merged
lkdvos merged 7 commits intomasterfrom
box-box
Mar 21, 2025
Merged

Refactor VUMPS and VOMPS to avoid boxed variables #265
lkdvos merged 7 commits intomasterfrom
box-box

Conversation

@lkdvos
Copy link
Copy Markdown
Member

@lkdvos lkdvos commented Mar 20, 2025

I wanted to include OhMyThreads v0.8, which now throws errors for boxed variables in closures.
Since I had wanted to improve the code reuse a bit anyways, I went ahead and fully refactored VUMPS and VOMPS to make better use of the iterative solvers approach.

  • How do we feel about dominant_eigsolve, the internal function I now used to map both leading_boundary and find_groundstate to? It definitely avoids some code duplication in the different VUMPS implementations. Naming suggestions also welcome of course.
  • Is this new approach clearer/cleaner to everyone? I find it a bit easier to follow, and it definitely splits the big VUMPS functions into smaller parts so it should also be easier on the compiler etc.
  • @leburgel, do you know if this affects the boundary MPS stuff in PEPSKit?

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 20, 2025

Codecov Report

Attention: Patch coverage is 78.78788% with 42 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/algorithms/approximate/vomps.jl 73.07% 14 Missing ⚠️
src/algorithms/statmech/vomps.jl 82.53% 11 Missing ⚠️
src/algorithms/groundstate/vumps.jl 87.50% 8 Missing ⚠️
src/states/ortho.jl 38.46% 8 Missing ⚠️
src/algorithms/derivatives.jl 66.66% 1 Missing ⚠️
Files with missing lines Coverage Δ
src/algorithms/statmech/vumps.jl 40.00% <100.00%> (-45.37%) ⬇️
src/utility/iterativesolvers.jl 88.88% <100.00%> (+1.38%) ⬆️
src/algorithms/derivatives.jl 81.96% <66.66%> (ø)
src/algorithms/groundstate/vumps.jl 88.05% <87.50%> (-6.95%) ⬇️
src/states/ortho.jl 83.67% <38.46%> (-3.74%) ⬇️
src/algorithms/statmech/vomps.jl 83.58% <82.53%> (-8.31%) ⬇️
src/algorithms/approximate/vomps.jl 75.43% <73.07%> (-13.76%) ⬇️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@lkdvos lkdvos changed the title Box-box: - refactor VUMPS and VOMPS to avoid boxed variables Refactor VUMPS and VOMPS to avoid boxed variables Mar 20, 2025
Copy link
Copy Markdown
Member

@VictorVanthilt VictorVanthilt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • The dominant_eigsolve name is self-explanatory and I can't come up with a better name so I'm okay with it.
  • The usage of Base.iterate in an iterative solver is indeed a readability improvement, it's way more clear what steps the different algorithms entail.

Could you point out where Boxed variables were showing up in closures? I want to be able to avoid ruining performance when using closures in the future.

@lkdvos
Copy link
Copy Markdown
Member Author

lkdvos commented Mar 21, 2025

I can, but this really does not "ruin" performance at all, you want to avoid this in cases where your iterations take ~1ms or less, sure, but for VUMPS that's really just not the case.

The boxed variables appear whenever you are capturing a variable in a closure, but also modifying it outside of the closure. The exact mechanism of when it does and doesn't happen is also not fully clear to me, but it tends to be a combination of scoping, closures, and reassignment that does the trick. Here are some examples to give an idea:

let
    A = zeros(10)
    for _ in 1:10
        res = tmap(eachindex(A)) do i
            A[i] = i
            A[i]
        end
        A = zeros(10)
    end
end # A boxed

let
    A = zeros(10)
    for _ in 1:10
        A = zeros(10)
        res = tmap(eachindex(A)) do i
            A[i] = i
            A[i]
        end
    end
end # A boxed


let
    for _ in 1:10
        A = zeros(10)
        res = tmap(eachindex(A)) do i
            A[i] = i
            A[i]
        end
    end
end # fine

Note that the easiest way to fix this, which you might have seen a couple times in different places, is to introduce a new let block, where now the name of the variable in the inner scope is no longer tied to the same one as the outer scope, and therefore the closure doesn't need to box the variable anymore:

let
    A = zeros(10)
    for _ in 1:10
        res = let A = A
            tmap(eachindex(A)) do i
                A[i] = i
                A[i]
            end
        end
        A = zeros(10)
    end
end # A boxed

@lkdvos lkdvos merged commit ceda9d2 into master Mar 21, 2025
27 of 28 checks passed
@lkdvos lkdvos deleted the box-box branch March 21, 2025 11:39
@VictorVanthilt
Copy link
Copy Markdown
Member

Thanks for the explanation! This seems like a bit of julia black magic you just have to know about. Tomo Soejima has a nice piece about closures on his website that helped me understand them a bit more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants