Skip to content

Conversation

@lkdvos
Copy link
Contributor

@lkdvos lkdvos commented Nov 4, 2025

What it says in the title :)

@codecov
Copy link

codecov bot commented Nov 4, 2025

Codecov Report

❌ Patch coverage is 79.45205% with 15 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.21%. Comparing base (06d9939) to head (f6a302a).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/kroneckerarray.jl 78.72% 10 Missing ⚠️
src/linearalgebra.jl 81.25% 3 Missing ⚠️
src/matrixalgebrakit.jl 77.77% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #54      +/-   ##
==========================================
- Coverage   81.87%   81.21%   -0.67%     
==========================================
  Files           9        9              
  Lines         618      628      +10     
==========================================
+ Hits          506      510       +4     
- Misses        112      118       +6     
Flag Coverage Δ
docs 0.00% <0.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

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

Comment on lines +360 to +361
# TODO: this definition doesn't fully retain the original meaning:
# ‖a - b‖ < atol could be true even if the following check isn't
Copy link
Member

Choose a reason for hiding this comment

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

Good catch, I was a bit lazy with this definition but as you say we would need to be more careful about the tolerances. This is a case where it would be easier to special case for SectorArray.

Copy link
Member

@mtfishman mtfishman Nov 4, 2025

Choose a reason for hiding this comment

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

The best I could come up with is:

using LinearAlgebra: promote_leaf_eltypes
function Base.isapprox(
        a::AbstractKroneckerArray, b::AbstractKroneckerArray;
        atol::Real = 0,
        rtol::Real = Base.rtoldefault(promote_leaf_eltypes(a), promote_leaf_eltypes(b), atol),
        norm::Function = norm
    )
    a1, a2 = arg1(a), arg2(a)
    b1, b2 = arg1(b), arg2(b)
    # Approximation of:
    # norm(a - b) = norm(a1 ⊗ a2 - b1 ⊗ b2)
    #             = norm((a1 - b1) ⊗ a2 + b1 ⊗ (a2 - b2) + (a1 - b1) ⊗ (a2 - b2))
    diff1 = norm(a1 - b1)
    diff2 = norm(a2 - b2)
    d = diff1 * norm(a2) + norm(b1) * diff2 + diff1 * diff2
    return iszero(rtol) ? d <= atol : d <= max(atol, rtol * max(norm(a), norm(b)))
end

which would work for SectorArrays since a1 == b1.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure I'm fully following your derivation, I don't see how you can split the norms like that?
In principle there is the following formula:

$$|| a1 \otimes a2 - b1 \otimes b2 ||^2 = || a1 \otimes a2 ||^2 + || b1 \otimes b2 ||^2 - 2 Re \langle a1 \otimes a2, b1 \otimes b2 \rangle$$

But this doesn't behave well with finite precision, since we are basically subtracting equal magnitude numbers to attempt to find something of much smaller magnitude

Comment on lines +12 to +22
@doc """
arg1(AB::AbstractKroneckerArray{T, N})
Extract the first factor (`A`) of the Kronecker array `AB = A ⊗ B`.
""" arg1

@doc """
arg2(AB::AbstractKroneckerArray{T, N})
Extract the second factor (`B`) of the Kronecker array `AB = A ⊗ B`.
""" arg2
Copy link
Member

Choose a reason for hiding this comment

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

I was realizing that if we are using these functions outside of KroneckerArrays.jl, these names are a bit too general. We can of course call them as KroneckerArrays.arg1(a), etc. explicitly, or rename them to something more explicit like kron_arg1(a), etc. Any opinion on that?

Copy link
Member

Choose a reason for hiding this comment

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

I guess arg1 and arg2 are also used for non-Kronecker data structures such as CartesianProduct so maybe kron_arg* isn't the best name.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

factors(x, [i])?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But presumably better to change that in a separate PR, this is currently non-breaking

Copy link
Member

Choose a reason for hiding this comment

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

Agreed that kind of change should be done in a separate PR. factors(x, [i]) is probably a better interface.

Comment on lines +29 to +31
arguments(a::AbstractKroneckerArray) = (arg1(a), arg2(a))
arguments(a::AbstractKroneckerArray, n::Int) = arguments(a)[n]
argument_types(a::AbstractKroneckerArray) = argument_types(typeof(a))
Copy link
Member

Choose a reason for hiding this comment

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

I think in practice these aren't used so maybe they can be removed for now, I think I was undecided whether I would use arguments vs. arg1/arg2 but in practice I found arg1/arg2 were easier to use. That could be a separate PR of course, just bringing it up here since this PR reminded me of this issue.

Copy link
Member

Choose a reason for hiding this comment

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

Your suggestion of factors(x, [i]) would address this issue.

@mtfishman
Copy link
Member

Looks good to me, ready to merge?

I see fillarrays.jl should also be made abstract but that is a deeper change that would require changing that code to catch cases where there is a Kronecker product involving Zeros in the broadcast style (which would be made easier by JuliaArrays/FillArrays.jl#385, but I think we could do something ourselves in the meantime). Those definitions are useful for us since the blockwise broadcasting/mapping code in BlockSparseArrays.jl creates Zeros blocks when there are unstored blocks, and those definitions help with removing those from the broadcast expression.

@mtfishman mtfishman merged commit 9f6111a into main Nov 4, 2025
12 of 14 checks passed
@mtfishman mtfishman deleted the abstractkroneckerarray branch November 4, 2025 21:13
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.

3 participants