Piccolo matrix-free objective-constructor slice#251
Merged
Conversation
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
added 2 commits
July 2, 2026 21:14
Member
Author
|
All tests passed pre-formatting; formatter passes now; merging @jeonghun-jj-lee @Rchari1 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Piccolo matrix-free objective-constructor slice — overall summary
Branch:
feat/matrix-free-obj-constr-testinharmoniqs/Piccolo.jl(ahead offeat/matrix-free-obj-constrby one commit).Origin: part of Piccolissimo #179; scoped into two Piccolo issues:
../../inputs/matrix-free/257.md—
CoherentKetInfidelityObjectiveslice.../../inputs/matrix-free/248.md— full-op
UnitaryInfidelityObjectiveslice.Companion documents:
./dto-matrix-free-summary.v1.md— the upstream DTO Bug-ForwardDiff and sample Matrix Orientation #113
KnotHVPinterface this consumes../piccolo-issue-discrepancies.md— review-gap analysis vs. issues Full-operator UnitaryInfidelityObjective declares ConstantLowRankHVP (build A at construction) #248 / #257.
./piccolo-test-addendum.md—outcome of the review-pass edits.
./piccolo-mfree-objconstr.md—this document.
Consumer (still pending): the Piccolissimo #179 apply-side is
tracked in
./piccolissimo-todos.md; itswork has not yet started. This document only summarizes the Piccolo
half of the coordinated PR pair.
1. Overall shape
Two of Piccolo's fidelity constructors now populate the DTO
knot_hvp::Union{Nothing, KnotHVP}field on their returnedKnotPointObjective, so Piccolissimo's Altissimo backend can pickup an exact, constant, rank-2 factor and apply the per-knot Hessian
matrix-free — instead of rediscovering the same operator via the
(6 + r)nested-ForwardDiff probes + host SVD that PR #178introduced.
The constructors affected:
CoherentKetInfidelityObjective(multi-ket coherent fidelity)UnitaryInfidelityObjectiveon the full-operator branch (i.e.U_goal isa AbstractMatrix). TheEmbeddedOperatorbranch andthe
UnitaryFreePhaseInfidelityObjective(θ-parameterized) stayon
knot_hvp = nothing— deliberately out of scope, as theirHessian structures don't fit
ConstantLowRankHVP.Ket-family variants outside the coherent multi-ket case
(
KetInfidelityObjective,CoherentKetFreePhaseInfidelityObjective,KetFreePhaseInfidelityObjective), density objectives, andUnitarySensitivityObjectiveare all untouched.2. Files changed
src/control/objectives.jlCoherentKetInfidelityObjectivebuildsAvia new helper, threadsknot_hvpthroughTerminalObjective; new helper_coherent_ket_lowrank_factor(goals, state_dims)(~35 lines);UnitaryInfidelityObjective(full-op branch) buildsAvia new helper, threadsknot_hvp; new helper_unitary_lowrank_factor(U_goal)(~24 lines); trailing signature comment onU_goal::AbstractPiccoloOperatordistinguishing the full-op vs. embedded arms.src/control/objectives.jl@testitemblocks (rank-2 factor round-trips + EmbeddedOperator fallthrough) + 1 review-pass byte-identity testitem; expanded test coverage per issue ACs.No new files. No API surface change (only a new keyword argument
that was already threaded by DTO #113). No public-symbol renaming.
No changes to value or gradient paths.
3. What each constructor now does
3.1
CoherentKetInfidelityObjective(issue #257)The coherent overlap
S = (1/K) · Σᵢ ⟨gᵢ, ψᵢ⟩is a single complexscalar linear in
z, wherez = [ψ̃₁; ψ̃₂; …; ψ̃_K]is theconcatenated per-ket iso-state at the terminal knot and each
ψ̃ᵢfollows the
ket_to_isoconvention[Re(ψᵢ); Im(ψᵢ)](lengthd_iso = 2 · ketdim).Because
Sis a scalar linear functional ofz, the lossℓ = |1 − |S|²|factors exactly as|1 − ‖A · z‖²|with aconstant
Aof shape2 × sum(state_dims):AcapturesRe(S) = (1/K) · Σᵢ (Re(gᵢ)ᵀRe(ψᵢ) + Im(gᵢ)ᵀIm(ψᵢ))Im(S) = (1/K) · Σᵢ (Re(gᵢ)ᵀIm(ψᵢ) − Im(gᵢ)ᵀRe(ψᵢ))with the
1/Kfactor built into every entry ofA, so‖A · z‖² = |S|² = Fexactly (no extra normalization needed atthe consumer). This is the
:neg2_signlink-Hessian rule forPiccolissimo:
H · v = -2 · sign(1 − F) · Aᵀ · (A · v)(with thekink at
F = 1handled by explicit branch selection on thePiccolissimo side).
The constructor now stashes
ConstantLowRankHVP(A, :neg2_sign)viathe
knot_hvpkeyword forwarded toTerminalObjective.3.2
UnitaryInfidelityObjective(issue #248), full-op branchThe overlap
S = tr(U_goal† · U(z))is a single complex scalarlinear in
z, wherez = Ũ⃗follows theoperator_to_iso_vecconvention (per column
iofU, a length-2nblock[Re(U[:,i+1]); Im(U[:,i+1])], total length2n²).The fidelity is
F = |S|² / n², so the lossℓ = |1 − F|factorsexactly as
|1 − ‖A · Ũ⃗‖²|with a constantAof shape2 × 2n², with the1/nfactor built into every entry so‖A · Ũ⃗‖² = |S|² / n² = Fexactly.The constructor now stashes
ConstantLowRankHVP(A, :neg2_sign)when
U_goal isa AbstractMatrix{<:Number}. WhenU_goal isa EmbeddedOperator,knot_hvp = nothingis passed instead — theembedded case has a mixed regularizer structure
(
1 / (n(n+1)) · (|tr(M†M)| + |tr(M)|²)) that doesn't fit theconstant-rank-2 form and stays on the dense fallback.
3.3 Helper functions
Two new module-private helpers in
objectives.jl:_coherent_ket_lowrank_factor(goals, state_dims)— buildsthe
(2, sum(state_dims))factor from a vector of complex goalkets and their per-ket iso-dimensions. Handles arbitrary
Kandarbitrary per-ket dimensions (the API supports it, though in
practice all kets share the same Hilbert space dim).
_unitary_lowrank_factor(U_goal)— builds the(2, 2n²)factor from an
n × ncomplex operator, per theoperator_to_iso_veccolumn-major layout.Both produce
Matrix{Float64}(solver-precision real isorepresentation), matching the DTO
ConstantLowRankHVP.A::Matrix{Float64}field type.
4. Acceptance criteria mapping
4.1 Issue #257 (
CoherentKetInfidelityObjective)knot_hvp isa ConstantLowRankHVP,core === :neg2_sign(wasnothing)"knot_hvp matrix-free apply matches dense Hessian — CoherentKetInfidelityObjective"asserts both directly.Ais2 × sum(state_dims)real,1/Kscale,F = ‖A·x_k‖²= coherent fidelity to~1e-12for randomly sampled terminal states and goals, across ≥1 and ≥2 goal statesfor K in (1, 2)×for scale in (0.4, 1.2)(F<1, F>1) and assertssize(cap.A) == (2, K * ket_dim)plusatol = 1e-12parity. Review-pass expansion in./piccolo-test-addendum.md."knot_hvp field does not leak into KnotPointObjective value/gradient"builds twoKnotPointObjectives differing only inknot_hvpand asserts elementwise equality. Existing testitems (CoherentKetInfidelityObjective,CoherentKetFreePhaseInfidelityObjective,coherent_ket_fidelity accepts generic Complex types) still pass.[compat]admits DTO release shippingKnotHVP./piccolo-test-addendum.md"Items deferred").4.2 Issue #248 (
UnitaryInfidelityObjectivefull-op)knot_hvp isa ConstantLowRankHVP,core === :neg2_sign(wasnothing)"knot_hvp matrix-free apply matches dense Hessian — UnitaryInfidelityObjective (full-op)"asserts both.Ais2 × 2n²real,1/nscale, `F = ‖A·x_k‖² =to~1e-12**acrossn = 2, 3, 4`**knot_hvp = nothing)"knot_hvp returns nothing for EmbeddedOperator UnitaryInfidelityObjective"asserts the embedded case directly.UnitaryFreePhaseInfidelityObjectiveconstructor is unchanged in this branch (verified by inspection); an inline assertion could be added if desired.KnotPointObjectivedirectly, which is the layer at whichknot_hvplives).5. Test coverage summary
Post-review-pass,
Piccolo.jl/src/control/objectives.jlinlinetestitems total 55 assertions across 8 testitems (was 12 before
this branch, and 29 before the review pass — see
./piccolo-test-addendum.mdfor theper-testitem breakdown). Wall time 16.6 s in the manual driver.
The added test surface:
Symmetric(get_full_hessian, :U) · vatF < 1andF > 1— for coherent-ket at K ∈ {1, 2} and forfull-op unitary at n ∈ {2, 3, 4}.
EmbeddedOperatorcase returnsknot_hvp === nothing(scope guard).knot_hvp = nothingvs.
knot_hvp = ConstantLowRankHVP(random_A, :neg2_sign)construction pair.
6. Deferred / follow-up items
[compat] DirectTrajOpt = "0.9.7"inPiccolo.jl/Project.tomlPkg.resolve()fails when the compat entry demands a version the registry doesn't have.knot_hvpdeclarers (UnitaryFreePhaseInfidelityObjective,CoherentKetFreePhaseInfidelityObjective,KetFreePhaseInfidelityObjective)A(θ)is not constant — deferred with three candidate approaches (CustomKnotHVPclosure,ParametricLowRankHVPcarrier, exploit global linear constraints on θ)../todos.md§2a: filed as a future sub-issue; needs a Phase-0 audit of the per-iterate rank ofA(θ)before choosing an approach.knot_hvp(obj, traj)and applies the constant-AHVP device-side../piccolissimo-todos.md, decisions locked in via../todos-conflicts.mdand./piccolissimo-saturation-guard-determination.md.7. Non-goals
Recorded explicitly to keep the PR scope obvious:
in
./todos.md)._build_rank_r_block— item 4 in theworkstream, not this PR.
UnitarySensitivityObjective, and theket-family variants other than the coherent multi-ket case —
still
knot_hvp = nothing. All either don't fit the constant-Arank-2 structure or are affine (zero block); their carve-outs are
matched to the corresponding issue-scope statements.
coresymbols beyond:neg2_sign— none in this branch.When a new structured family (e.g. an embedded-op declarer) is
added later, the same slot can carry a new symbol paired with the
corresponding apply-branch on the Piccolissimo side.