Skip to content

Make the diagonal of Diagonal tangents projected onto Hermitian real - #723

Merged
devmotion merged 3 commits into
mainfrom
dmw/hermitian-diagonal-projection
Oct 28, 2026
Merged

devmotion merged 3 commits into
mainfrom
dmw/hermitian-diagonal-projection

Conversation

@devmotion

@devmotion devmotion commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

ProjectTo{Hermitian} kept the imaginary part of a Diagonal tangent, so the result was not Hermitian and differed from projecting the same tangent as a dense Matrix (#722).

The Diagonal method now takes the real part of the projected diagonal with oftype(y, real(y)), the same as LinearAlgebra.hermitian(::Number). Using oftype instead of complex keeps the element type for any number type. I checked locally that quaternions (Quaternions.jl) and complex Unitful quantities give the same result as the dense path; they are not test dependencies, so that check is not in the test suite.

  • If the tangent or the projected diagonal is already real, it is returned without taking the real part again. So Hermitian{Float64} still projects a Diagonal{Float64} without allocating, and a real tangent onto a complex Hermitian is copied only once (new @ballocated tests).
  • The Symmetric method is unchanged; a new test checks that it keeps a complex diagonal.
  • Tests cover ComplexF64 and ComplexF32, a real tangent for a complex Hermitian, a real Hermitian with a complex tangent, agreement with the dense path, and the size check.
  • Bumps the version to 1.26.7.

Found while reviewing JuliaDiff/ChainRules.jl#856, which switched the Matrix(::Hermitian) rules to ProjectTo.

Fixes #722.

🤖 Generated with Claude Code

ProjectTo{Hermitian} kept the imaginary part of a Diagonal tangent, so
its result was not Hermitian and disagreed with the dense path. Take the
real part with oftype, like LinearAlgebra.hermitian(::Number), so the
element type is kept also for e.g. quaternions and Unitful quantities.

Fixes #722.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.54%. Comparing base (2a1e65e) to head (1a2a559).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #723      +/-   ##
==========================================
+ Coverage   93.50%   93.54%   +0.03%     
==========================================
  Files          15       15              
  Lines        1016     1022       +6     
==========================================
+ Hits          950      956       +6     
  Misses         66       66              

☔ View full report in Codecov by Harness.
📢 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.

devmotion and others added 2 commits October 28, 2026 16:21
A real input diagonal is already real after projection, so this avoids
a second copy. Also check the value for a real tangent, that Symmetric
keeps a complex diagonal, and the allocations for a real tangent onto a
complex Hermitian.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@devmotion
devmotion merged commit 4e9c8aa into main Oct 28, 2026
26 of 27 checks passed
@devmotion
devmotion deleted the dmw/hermitian-diagonal-projection branch October 28, 2026 18:08
devmotion added a commit to JuliaDiff/ChainRules.jl that referenced this pull request Oct 28, 2026
ChainRulesCore 1.26.7 (JuliaDiff/ChainRulesCore.jl#723) makes the
projection of a Diagonal tangent onto Hermitian real. Without it, the
Matrix(::Hermitian) rrule would keep the imaginary part of a Diagonal
cotangent, which the old hand-written rule dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
devmotion added a commit to JuliaDiff/ChainRules.jl that referenced this pull request Oct 29, 2026
* Use natural tangents in Matrix(::HermOrSym) rules

The frule and rrule for `Matrix`/`Array` of a `Symmetric`/`Hermitian` now
project with `ProjectTo(A)`, giving the natural tangent `(Δ + Δ')/2` instead
of the structural one that double-counted off-diagonals. This matches
FiniteDifferences 0.12.35, whose `to_vec(::Symmetric/Hermitian)` now yields
natural tangents. Re-enables the `prod` tests on `Symmetric` (#475).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Require ChainRulesCore 1.26.7 and test Diagonal cotangents

ChainRulesCore 1.26.7 (JuliaDiff/ChainRulesCore.jl#723) makes the
projection of a Diagonal tangent onto Hermitian real. Without it, the
Matrix(::Hermitian) rrule would keep the imaginary part of a Diagonal
cotangent, which the old hand-written rule dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Rename result of Diagonal cotangent test

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.

ProjectTo(::Hermitian) keeps the imaginary part of a Diagonal tangent

1 participant