Skip to content

Use adjoint map for Hermitian PSD variable bridge - #3141

Merged
blegat merged 3 commits into
masterfrom
bl/hermitian-adjoint-bridge
Nov 14, 2026
Merged

blegat merged 3 commits into
masterfrom
bl/hermitian-adjoint-bridge

Conversation

@blegat

@blegat blegat commented Nov 12, 2026

Copy link
Copy Markdown
Member

Closes #3139

Represent Hermitian PSD variables by the adjoint of the constraint bridge's real PSD embedding, removing the structural equality constraints. Reuse the constraint bridge's map methods through Variable.SetMapBridge and retain support for partial variable starts.

Use half the real PSD embedding as the inverse adjoint, preserving positive semidefiniteness of starts. Add regression coverage for unstructured PSD representatives, rank-one preimages, triangle inner products, and affine substitutions.

Validation: both affected bridge test files pass (267 assertions). All 15 temporary line markers were hit, including numeric and VectorOfVariables adjoint paths. JuliaFormatter 2.14.10 was run over the repository; a second pass left git diff empty.
Query the dimension with output_dim instead of constructing a scalar iterator. Build eachscalar once for the full adjoint and reuse its cache when mapping components, avoiding repeated scans of affine and quadratic terms.

Add symbolic affine and quadratic regression tests for full and indexed adjoint evaluation. Both affected bridge test files pass (277 assertions); temporary line and type markers verified both symbolic paths and were removed. Repository-wide JuliaFormatter 2.14.10 passes with no unstaged diff.
@araujoms

araujoms commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Wow. I was planning to do a PR, but it would have taken me considerably longer. Thanks!

@blegat

blegat commented Nov 13, 2026 •

Copy link
Copy Markdown
Member Author

I did a few benchmarks. I tested the explicit dual: max t subject to C − tI ⪰ 0, using the constraint bridge. SCS slightly prefers the old bridge:

Hermitian order Old primal New primal Dualization
25 0.021 s 0.155 s 0.047 s
100 1.34 s 2.96 s 1.82 s
200 9.14 s 12.99 s 55.20 s

For Clarabel it's mostly unchanged and for CSDP, the new bridge is way better

Solver Hermitian order Before After
CSDP 25 0.0451 0.00191
CSDP 100 60.67 0.0389
CSDP 200 Memory limit¹ 0.246
Clarabel 25 1.024 1.036
Clarabel 50² 126.65 125.89

Since the new bridge Variable bridge is the equivalent of the old constraint bridge it's definitely better to have it like that. Since it's slightly better for SCS, one could try having the primal-dual pair of the old bridge as variable and constraint bridge add them to JuMP and remove the ones in MOI but for simplicity let's not add them in MOI unless there is a strong incentive.

@araujoms

araujoms commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

Why should it make any difference for SCS? It doesn't use this bridge, it has a native complex PSD cone.

I tested to check whether SCS was accidentally using the bridge, and it indeed wasn't. I then ran the benchmarks anyway and got no difference:

Hermitian order Old primal New primal
25 0.037 s 0.039 s
100 0.22 s 2.92 s
200 2.57 s 2.55 s

Perhaps you used an AI to do it and it just hallucinated some numbers instead of running the code?

Use incremented triangular indices for the full-vector adjoint instead of
calling the indexed method for every component. Avoid repeated matrix-size
calculations and inverse_trimap/isqrt calls. Keep the indexed method for
individual components, coefficient-type-aware scalar operations, and one
cached scalar iterator for symbolic inputs. Extend full-map tests to include
VectorOfVariables alongside affine and quadratic functions.

Compare the exact indexed implementation at bc1d764f with the sequential loop using
identical dependencies, Julia 1.13.3, and BenchmarkTools 1.8.0 on the
Core i7-12700K. Compilation is warmed; use medians of up to 100 samples /
2 seconds with one full-vector evaluation per sample. n is the Hermitian
order; the input is a Float64 triangle of real PSD order 2n.

| n | Indexed (ms) | Sequential (ms) | Speedup | Bytes (both) | Allocations (both) |
| ---: | ---: | ---: | ---: | ---: | ---: |
| 100 | 0.195760 | 0.030335 | 6.45x | 80072 | 3 |
| 500 | 4.782259 | 0.545091 | 8.77x | 2000072 | 3 |
| 1000 | 20.064708 | 3.352298 | 5.99x | 8000072 | 3 |

Validation: both affected bridge test files pass (282 assertions). All four
added-loop coverage markers were hit, including numeric vectors, variables,
affine functions, and quadratic functions; markers were removed. Full maps
agree exactly with indexed components at all three benchmark sizes.
JuliaFormatter 2.14.10 run repository-wide with a stable second pass.

Performance probes and raw results are kept on the companion perf branch.
@blegat

blegat commented Nov 13, 2026

Copy link
Copy Markdown
Member Author

Yes SCS is not using that bridge, I used a MOI.Bridges.SingleBridgeOptimizer to force that formulation for SCS to check the impact with an ADMM solver. But indeed, for SCS, it won't matter in practice, maybe for COSMO which is similar it would.

For the performance of the bridge itself, with Hermitian of size 500, comparing the original implementation with the final version:

Operation Before After
Variable bridge creation alone 21.9 ms 7.44 ms
Full variable model build 616 ms 736 ms
Full constraint model build 91.6 ms 97.9 ms
Numeric constraint adjoint 0.370 ms 0.347 ms

The full variable is a bit slower because when we substitute variables into the bridged variables, we now have two terms so it's a bit slower, but this is to be expected so it's ok

@araujoms

Copy link
Copy Markdown
Contributor

COSMO also has a native complex PSD cone.

@blegat
blegat force-pushed the bl/hermitian-adjoint-bridge branch from a96f117 to 1136b04 Compare November 13, 2026 14:12
@blegat

blegat commented Nov 14, 2026

Copy link
Copy Markdown
Member Author

Ahah then a hypothetical ADMM solver then ^^

@blegat
blegat merged commit 2aecedb into master Nov 14, 2026
31 checks passed
@blegat
blegat deleted the bl/hermitian-adjoint-bridge branch November 14, 2026 09:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

More efficient HermitianToSymmetricPSDBridge?

2 participants