Skip to content

Replace Nonlinear.ReverseAD.Coloring by SparseMatrixColorings - #3105

Open
blegat wants to merge 4 commits into
masterfrom
bl/smc
Open

blegat wants to merge 4 commits into
masterfrom
bl/smc

Conversation

@blegat

@blegat blegat commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

Backport of blegat/ArrayDiff.jl#102

We can now easily use the star coloring 🎉

using JuMP, Ipopt
import MathOptInterface as MOI
import SparseMatrixColorings as SMC

model = Model(Ipopt.Optimizer)
set_silent(model)

# Direct decompression selects star coloring.
algorithm = SMC.GreedyColoringAlgorithm(; decompression = :direct)
set_attribute(
    model,
    MOI.AutomaticDifferentiationBackend(),
    MOI.Nonlinear.SparseReverseMode(algorithm),
)

@variable(model, x, start = -1.2)
@variable(model, y, start = 1.0)
@objective(model, Min, (1 - x)^2 + 100 * (y - x^2)^2)

optimize!(model)

@show termination_status(model)  # LOCALLY_SOLVED
@show value(x) value(y) objective_value(model) # (x, y) ≈ (1, 1), objective ≈ 1.52e-22.

Include workarounds for

Found performance regressions which should be fixed by:

We should wait for them to be merged and revisit before merging

Closes #2527
Closes #3104

@gdalle

gdalle commented Oct 16, 2026

Copy link
Copy Markdown

Any clue how this compares in terms of speed to the existing implementation (acyclic coloring, if memory serves?)?

@mlubin

mlubin commented Oct 17, 2026

Copy link
Copy Markdown
Member

Great to see the old code deleted. We need extensive benchmarks before merging.

@blegat

blegat commented Oct 19, 2026 •

Copy link
Copy Markdown
Member Author

Raw benchmark results by GPT 6 Astra

The existing implementation uses acyclic coloring. This compares it with the PR’s default SMC acyclic coloring (decompression = :substitution) and optional SMC star coloring (decompression = :direct).

The results are workload-dependent: dense Hessians and larger OPF cases benefit, but the default SMC path is 23–31% slower on the block-dense Hessian evaluations. Star coloring improves those cases, while adding colors and derivative work on chains and grids. The block-dense substitution-recovery regression deserves investigation before merging.

Revisions and environment

  • Baseline: 9470528bb94bb0f733167ad8977a31f3391cd155 (the PR parent).
  • PR: e29bc6838878eb822c6bea5d3ef349c4ba2c8ab6.
  • Julia 1.13.1; SparseMatrixColorings 0.4.28; JuMP 1.32.1; Ipopt 1.16.0; PowerModels 0.21.7; PGLib 0.2.2. Dependencies are identical across the three configurations.
  • Linux, Intel Core i7-12700K, pinned to performance CPU 8, one Julia thread and one BLAS thread. Runs were serial.
  • 24 synthetic cases (eight families at three sizes), six PGLib AC-OPF cases, and seven complete Ipopt solve cases.

How to read the tables

Phase timings are median (minimum), after warmup. Speedup is baseline median / candidate median: above 1 means faster, below 1 means slower. L / A / S denotes legacy / SMC acyclic / SMC star. Sample counts are shown explicitly; † marks a phase with fewer than five samples in at least one configuration. Those measurements are preliminary.

BenchmarkTools used evals = 1, a 0.4-second budget per phase, and caps of 80 initialization samples or 10,000 Hessian/recovery samples. Timings exclude model construction and compilation. They do not measure time to first solve. These are local measurements on one machine, without statistical confidence intervals.

Evaluator initialization

Fresh evaluator construction and MOI.initialize, including sparsity analysis, coloring, and derivative workspace creation. This is not isolated coloring time.

Case Legacy (ms) SMC acyclic (ms) SMC star (ms) Acyclic speedup Star speedup Samples L / A / S
diagonal_100 0.057 (0.054) 0.082 (0.070) 0.083 (0.067) 0.69× 0.68× 80 / 80 / 80
diagonal_1000 0.762 (0.529) 0.858 (0.681) 0.871 (0.697) 0.89× 0.87× 80 / 80 / 80
diagonal_10000 6.979 (5.601) 8.930 (5.963) 7.804 (6.196) 0.78× 0.89× 50 / 46 / 45
rosenbrock_100 0.144 (0.133) 0.109 (0.101) 0.106 (0.099) 1.32× 1.36× 80 / 80 / 80
rosenbrock_1000 1.448 (1.000) 1.198 (1.055) 1.436 (1.056) 1.21× 1.01× 80 / 80 / 80
rosenbrock_10000 13.243 (11.448) 15.958 (11.091) 16.296 (11.184) 0.83× 0.81× 26 / 26 / 26
bilinear_100 0.038 (0.035) 0.038 (0.037) 0.035 (0.033) 1.00× 1.08× 80 / 80 / 80
bilinear_1000 0.367 (0.314) 0.347 (0.317) 0.327 (0.298) 1.06× 1.12× 80 / 80 / 80
bilinear_10000 3.844 (3.509) 3.760 (3.295) 3.540 (3.197) 1.02× 1.09× 79 / 80 / 80
arrowhead_100 0.078 (0.072) 0.095 (0.091) 0.068 (0.064) 0.82× 1.16× 80 / 80 / 80
arrowhead_1000 2.884 (2.791) 3.430 (3.349) 1.249 (1.212) 0.84× 2.31× 80 / 80 / 80
arrowhead_10000 † 433.203 (433.203) 295.676 (294.249) 76.825 (75.584) 1.47× 5.64× 1 / 2 / 6
grid_10 0.122 (0.114) 0.125 (0.117) 0.118 (0.110) 0.97× 1.03× 80 / 80 / 80
grid_30 1.497 (1.234) 1.475 (1.186) 1.325 (1.058) 1.01× 1.13× 80 / 80 / 80
grid_70 7.996 (7.357) 7.080 (6.666) 6.544 (6.319) 1.13× 1.22× 40 / 46 / 48
blocks_100 0.266 (0.250) 0.181 (0.166) 0.138 (0.126) 1.47× 1.93× 80 / 80 / 80
blocks_1000 2.570 (2.461) 1.813 (1.730) 1.721 (1.358) 1.42× 1.49× 80 / 80 / 80
blocks_5000 16.342 (13.726) 9.758 (9.044) 9.236 (7.348) 1.67× 1.77× 25 / 36 / 44
dense_50 0.586 (0.547) 0.324 (0.311) 0.236 (0.226) 1.81× 2.49× 80 / 80 / 80
dense_150 10.960 (10.416) 5.220 (4.877) 4.099 (3.710) 2.10× 2.67× 34 / 74 / 80
dense_300 77.731 (74.727) 33.399 (31.869) 27.499 (25.163) 2.33× 2.83× 6 / 12 / 15
ocp_20 0.033 (0.032) 0.045 (0.043) 0.042 (0.041) 0.74× 0.79× 80 / 80 / 80
ocp_100 0.167 (0.157) 0.225 (0.211) 0.211 (0.199) 0.74× 0.79× 80 / 80 / 80
ocp_500 0.844 (0.770) 1.189 (1.044) 1.076 (0.979) 0.71× 0.78× 80 / 80 / 80
case14_ieee 0.380 (0.364) 0.361 (0.340) 0.331 (0.315) 1.05× 1.15× 80 / 80 / 80
case118_ieee 4.497 (3.653) 5.409 (3.428) 5.158 (3.246) 0.83× 0.87× 67 / 70 / 72
case300_ieee 12.767 (8.149) 12.009 (7.864) 12.045 (7.859) 1.06× 1.06× 29 / 31 / 32
case1354_pegase 56.191 (45.630) 60.073 (42.433) 60.694 (40.150) 0.94× 0.93× 6 / 7 / 7
case2869_pegase † 169.650 (107.082) 147.454 (102.101) 139.922 (94.707) 1.15× 1.21× 3 / 3 / 3
case10000_goc † 473.387 (361.064) 453.629 (311.030) 413.527 (349.503) 1.04× 1.14× 2 / 2 / 2

Repeated Lagrangian Hessian evaluation

Time per evaluation, obtained from a pair of calls at alternating inputs with nonzero objective and constraint multipliers. Alternation prevents the cached iterate from hiding recomputation. All three configurations reported zero Julia allocations in every case.

Case Legacy (µs) SMC acyclic (µs) SMC star (µs) Acyclic speedup Star speedup Samples L / A / S
diagonal_100 9.34 (8.83) 9.28 (8.85) 9.57 (9.31) 1.01× 0.98× 10,000 / 10,000 / 10,000
diagonal_1000 88.11 (84.09) 89.67 (86.33) 92.40 (89.19) 0.98× 0.95× 2,242 / 2,221 / 2,149
diagonal_10000 926.65 (890.30) 936.35 (903.84) 979.52 (954.97) 0.99× 0.95× 211 / 213 / 201
rosenbrock_100 14.20 (13.58) 14.00 (13.50) 15.09 (14.60) 1.01× 0.94× 10,000 / 10,000 / 10,000
rosenbrock_1000 147.02 (139.82) 145.43 (139.32) 156.71 (149.09) 1.01× 0.94× 1,353 / 1,374 / 1,264
rosenbrock_10000 1,667.47 (1,552.22) 1,573.60 (1,489.56) 1,831.65 (1,740.78) 1.06× 0.91× 117 / 126 / 108
bilinear_100 4.16 (4.04) 4.24 (4.08) 4.36 (4.22) 0.98× 0.96× 10,000 / 10,000 / 10,000
bilinear_1000 39.02 (37.26) 39.69 (38.10) 42.80 (40.53) 0.98× 0.91× 5,110 / 5,011 / 4,560
bilinear_10000 402.76 (383.74) 407.65 (393.66) 436.05 (420.93) 0.99× 0.92× 495 / 489 / 455
arrowhead_100 9.13 (8.78) 9.43 (8.95) 9.23 (8.93) 0.97× 0.99× 10,000 / 10,000 / 10,000
arrowhead_1000 88.39 (85.14) 90.44 (87.32) 89.19 (85.38) 0.98× 0.99× 2,251 / 2,199 / 2,219
arrowhead_10000 904.18 (872.49) 932.49 (902.41) 918.45 (872.86) 0.97× 0.98× 220 / 213 / 217
grid_10 16.95 (16.42) 16.94 (16.48) 18.47 (17.88) 1.00× 0.92× 10,000 / 10,000 / 10,000
grid_30 164.34 (158.91) 166.43 (159.64) 186.72 (176.08) 0.99× 0.88× 1,207 / 1,200 / 1,067
grid_70 980.02 (928.04) 1,004.27 (953.53) 1,116.89 (1,079.29) 0.98× 0.88× 202 / 199 / 179
blocks_100 8.94 (8.63) 10.97 (10.60) 7.38 (7.05) 0.81× 1.21× 10,000 / 10,000 / 10,000
blocks_1000 88.88 (85.51) 116.46 (110.85) 78.75 (74.96) 0.76× 1.13× 2,229 / 1,712 / 2,521
blocks_5000 514.74 (486.57) 660.34 (639.39) 451.81 (410.60) 0.78× 1.14× 387 / 302 / 444
dense_50 13.27 (12.75) 12.74 (12.34) 8.09 (7.68) 1.04× 1.64× 10,000 / 10,000 / 10,000
dense_150 173.30 (165.66) 123.50 (119.56) 79.84 (77.30) 1.40× 2.17× 1,126 / 1,603 / 2,469
dense_300 1,268.50 (1,147.39) 501.15 (481.74) 330.76 (324.24) 2.53× 3.84× 147 / 398 / 598
ocp_20 4.74 (4.54) 4.97 (4.75) 4.81 (4.62) 0.95× 0.98× 10,000 / 10,000 / 10,000
ocp_100 22.70 (21.99) 23.37 (22.52) 22.91 (22.32) 0.97× 0.99× 8,766 / 8,489 / 8,642
ocp_500 127.00 (123.19) 135.75 (130.11) 134.77 (128.43) 0.94× 0.94× 1,556 / 1,458 / 1,464
case14_ieee 40.69 (39.22) 40.68 (38.77) 38.91 (37.46) 1.00× 1.05× 4,860 / 4,873 / 5,058
case118_ieee 480.51 (454.29) 462.91 (441.69) 452.97 (433.53) 1.04× 1.06× 412 / 431 / 434
case300_ieee 2,007.71 (1,387.93) 1,329.83 (1,185.69) 1,290.82 (1,036.76) 1.51× 1.56× 97 / 148 / 147
case1354_pegase 15,386.79 (14,830.75) 13,163.07 (13,032.92) 12,218.16 (11,403.60) 1.17× 1.26× 13 / 16 / 17
case2869_pegase 37,541.92 (36,948.72) 34,495.87 (34,255.83) 30,045.82 (29,399.14) 1.09× 1.25× 6 / 6 / 7
case10000_goc † 108,935.27 (108,909.22) 98,002.50 (97,953.72) 86,786.67 (86,620.97) 1.11× 1.26× 2 / 3 / 3

Isolated Hessian recovery

Recovery of all function Hessians from already compressed products, excluding Hessian-vector products and seed preparation. Every measurement reported zero Julia allocations.

Case Legacy (µs) SMC acyclic (µs) SMC star (µs) Acyclic speedup Star speedup Samples L / A / S
diagonal_100 0.071 (0.068) 0.104 (0.097) 0.173 (0.171) 0.68× 0.41× 10,000 / 10,000 / 10,000
diagonal_1000 0.528 (0.510) 0.818 (0.808) 1.523 (1.484) 0.65× 0.35× 10,000 / 10,000 / 10,000
diagonal_10000 5.058 (4.900) 7.782 (7.571) 14.796 (14.116) 0.65× 0.34× 10,000 / 10,000 / 10,000
rosenbrock_100 0.257 (0.246) 0.389 (0.375) 0.254 (0.250) 0.66× 1.01× 10,000 / 10,000 / 10,000
rosenbrock_1000 2.716 (2.687) 3.724 (3.391) 2.387 (2.375) 0.73× 1.14× 10,000 / 10,000 / 10,000
rosenbrock_10000 28.087 (26.887) 35.289 (34.047) 23.976 (23.240) 0.80× 1.17× 10,000 / 10,000 / 10,000
bilinear_100 0.267 (0.263) 0.294 (0.284) 0.188 (0.176) 0.91× 1.42× 10,000 / 10,000 / 10,000
bilinear_1000 2.667 (2.640) 2.970 (2.825) 1.694 (1.619) 0.90× 1.57× 10,000 / 10,000 / 10,000
bilinear_10000 28.185 (27.550) 28.732 (26.587) 17.009 (15.924) 0.98× 1.66× 10,000 / 10,000 / 10,000
arrowhead_100 0.244 (0.235) 0.386 (0.371) 0.265 (0.260) 0.63× 0.92× 10,000 / 10,000 / 10,000
arrowhead_1000 2.223 (2.183) 3.519 (3.461) 2.345 (2.266) 0.63× 0.95× 10,000 / 10,000 / 10,000
arrowhead_10000 23.250 (22.499) 36.425 (35.150) 23.587 (22.101) 0.64× 0.99× 10,000 / 10,000 / 10,000
grid_10 0.301 (0.288) 0.639 (0.603) 0.419 (0.343) 0.47× 0.72× 10,000 / 10,000 / 10,000
grid_30 3.114 (3.048) 6.391 (6.115) 3.206 (3.117) 0.49× 0.97× 10,000 / 10,000 / 10,000
grid_70 20.238 (19.714) 36.023 (34.373) 16.240 (15.917) 0.56× 1.25× 10,000 / 10,000 / 10,000
blocks_100 2.558 (2.404) 4.697 (4.557) 1.267 (1.159) 0.54× 2.02× 10,000 / 10,000 / 10,000
blocks_1000 17.037 (16.393) 48.197 (46.730) 12.456 (11.332) 0.35× 1.37× 10,000 / 8,194 / 10,000
blocks_5000 98.289 (95.219) 250.601 (241.772) 62.666 (57.055) 0.39× 1.57× 3,995 / 1,581 / 6,387
dense_50 5.867 (5.615) 5.842 (5.714) 1.514 (1.376) 1.00× 3.88× 10,000 / 10,000 / 10,000
dense_150 101.098 (96.388) 55.447 (54.102) 13.408 (12.530) 1.82× 7.54× 3,797 / 7,205 / 10,000
dense_300 907.269 (819.847) 222.109 (215.387) 54.993 (52.363) 4.08× 16.50× 425 / 1,778 / 7,168
ocp_20 0.189 (0.184) 0.212 (0.198) 0.218 (0.210) 0.89× 0.87× 10,000 / 10,000 / 10,000
ocp_100 1.097 (1.081) 1.261 (1.219) 1.200 (1.170) 0.87× 0.91× 10,000 / 10,000 / 10,000
ocp_500 5.418 (5.375) 6.896 (6.236) 5.855 (5.712) 0.79× 0.93× 10,000 / 10,000 / 10,000
case14_ieee 3.616 (3.464) 3.958 (3.828) 1.907 (1.835) 0.91× 1.90× 10,000 / 10,000 / 10,000
case118_ieee 70.630 (68.147) 74.448 (68.136) 34.194 (32.810) 0.95× 2.07× 5,472 / 5,349 / 10,000
case300_ieee 280.595 (194.580) 179.463 (158.502) 89.471 (80.509) 1.56× 3.14× 1,397 / 2,118 / 4,254
case1354_pegase 3,617.634 (3,483.630) 3,367.776 (3,103.445) 1,193.475 (859.913) 1.07× 3.03× 111 / 118 / 337
case2869_pegase 10,251.228 (9,941.418) 8,394.939 (8,185.173) 4,633.145 (4,367.110) 1.22× 2.21× 39 / 48 / 86
case10000_goc 29,232.430 (28,395.734) 24,467.076 (24,160.435) 13,029.897 (12,735.325) 1.19× 2.24× 14 / 17 / 31

Complete Ipopt solves

Each configuration used a fresh model for one warmup solve and three timed solves. Model construction is excluded; derivative initialization during optimize! is included. Times are median [minimum–maximum] in milliseconds. All solves terminated with LOCALLY_SOLVED. Iteration counts were identical across configurations and repetitions.

Case Legacy (ms) SMC acyclic (ms) SMC star (ms) Acyclic speedup Star speedup Iterations
rosenbrock_100 19.754 [19.590–19.867] 19.884 [19.097–20.058] 20.672 [20.209–21.038] 0.99× 0.96× 158
diagonal_1000 4.501 [4.370–4.503] 4.459 [4.422–4.648] 4.446 [4.437–4.816] 1.01× 1.01× 3
grid_30 6.732 [6.614–6.854] 6.472 [6.340–6.699] 6.516 [6.477–6.957] 1.04× 1.03× 5
ocp_100 2.269 [2.179–2.509] 2.347 [2.332–2.409] 2.430 [2.413–2.435] 0.97× 0.93× 4
case14_ieee 7.114 [7.081–7.286] 7.122 [7.072–7.255] 6.964 [6.915–7.120] 1.00× 1.02× 15
case118_ieee 86.707 [81.084–88.261] 78.957 [77.882–79.051] 87.339 [87.166–90.123] 1.10× 0.99× 29
case300_ieee 275.456 [271.873–276.253] 240.009 [237.716–243.478] 259.982 [252.928–266.044] 1.15× 1.06× 33
Solve objectives and Julia allocations

Values below are medians over the three timed solves. Objective values agree to relative/absolute tolerance 1e-6. Allocated bytes cover Julia-managed allocations during optimize!, not native Ipopt allocations or peak memory. MiB = 2²⁰ bytes.

Case Legacy objective Acyclic objective Star objective Legacy allocated MiB Acyclic allocated MiB Star allocated MiB
rosenbrock_100 3.9866238543 3.9866238543 3.9866238543 0.710 0.720 0.737
diagonal_1000 1.64887159137e-21 1.64887159137e-21 1.64887159137e-21 2.652 2.780 2.764
grid_30 3.5865693253e-28 3.5865711647e-28 3.5865711647e-28 5.018 5.093 5.232
ocp_100 24.163216988 24.163216988 24.163216988 0.868 1.084 1.015
case14_ieee 2178.08041082 2178.08041082 2178.08041082 1.773 1.611 1.492
case118_ieee 97213.606939 97213.606939 97213.606939 16.377 14.845 13.743
case300_ieee 565219.971865 565219.971865 565219.971865 36.031 32.618 30.184
Problem dimensions, Hessian structure, and color counts — all 30 cases

n is the number of variables; m counts nonlinear constraints in the evaluator. Hessian entry counts include per-function duplicates. The old implementation adds some structurally zero diagonals that the PR omits. Maximum colors is the largest count over individual objective/constraint functions; total colors and seed entries are summed over those functions. Synthetic grid suffixes are side lengths, and OCP suffixes are horizon lengths.

Case n m Legacy Hessian entries PR Hessian entries Max colors L / A / S Total colors L / A / S Seed entries L / A / S
diagonal_100 100 0 100 100 1 / 1 / 1 1 / 1 / 1 100 / 100 / 100
diagonal_1000 1,000 0 1,000 1,000 1 / 1 / 1 1 / 1 / 1 1,000 / 1,000 / 1,000
diagonal_10000 10,000 0 10,000 10,000 1 / 1 / 1 1 / 1 / 1 10,000 / 10,000 / 10,000
rosenbrock_100 100 0 199 199 2 / 2 / 3 2 / 2 / 3 200 / 200 / 300
rosenbrock_1000 1,000 0 1,999 1,999 2 / 2 / 3 2 / 2 / 3 2,000 / 2,000 / 3,000
rosenbrock_10000 10,000 0 19,999 19,999 2 / 2 / 3 2 / 2 / 3 20,000 / 20,000 / 30,000
bilinear_100 100 0 199 99 2 / 2 / 3 2 / 2 / 3 200 / 200 / 300
bilinear_1000 1,000 0 1,999 999 2 / 2 / 3 2 / 2 / 3 2,000 / 2,000 / 3,000
bilinear_10000 10,000 0 19,999 9,999 2 / 2 / 3 2 / 2 / 3 20,000 / 20,000 / 30,000
arrowhead_100 100 0 199 198 2 / 2 / 2 2 / 2 / 2 200 / 200 / 200
arrowhead_1000 1,000 0 1,999 1,998 2 / 2 / 2 2 / 2 / 2 2,000 / 2,000 / 2,000
arrowhead_10000 10,000 0 19,999 19,998 2 / 2 / 2 2 / 2 / 2 20,000 / 20,000 / 20,000
grid_10 100 0 280 280 4 / 4 / 5 4 / 4 / 5 400 / 400 / 500
grid_30 900 0 2,640 2,640 4 / 4 / 5 4 / 4 / 5 3,600 / 3,600 / 4,500
grid_70 4,900 0 14,560 14,560 4 / 4 / 5 4 / 4 / 5 19,600 / 19,600 / 24,500
blocks_100 100 0 1,050 1,050 20 / 20 / 20 20 / 20 / 20 2,000 / 2,000 / 2,000
blocks_1000 1,000 0 10,500 10,500 20 / 20 / 20 20 / 20 / 20 20,000 / 20,000 / 20,000
blocks_5000 5,000 0 52,500 52,500 20 / 20 / 20 20 / 20 / 20 100,000 / 100,000 / 100,000
dense_50 50 0 1,275 1,275 50 / 50 / 50 50 / 50 / 50 2,500 / 2,500 / 2,500
dense_150 150 0 11,325 11,325 150 / 150 / 150 150 / 150 / 150 22,500 / 22,500 / 22,500
dense_300 300 0 45,150 45,150 300 / 300 / 300 300 / 300 / 300 90,000 / 90,000 / 90,000
ocp_20 41 22 61 61 1 / 1 / 1 23 / 21 / 21 61 / 61 / 61
ocp_100 201 102 301 301 1 / 1 / 1 103 / 101 / 101 301 / 301 / 301
ocp_500 1,001 502 1,501 1,501 1 / 1 / 1 503 / 501 / 501 1,501 / 1,501 / 1,501
case14_ieee 118 80 800 710 4 / 4 / 4 320 / 320 / 320 1,280 / 1,280 / 1,280
case118_ieee 1,088 744 7,440 6,678 4 / 4 / 4 2,976 / 2,976 / 2,976 11,904 / 11,904 / 11,904
case300_ieee 2,382 1,644 16,440 14,668 4 / 4 / 4 6,576 / 6,576 / 6,576 26,304 / 26,304 / 26,304
case1354_pegase 11,192 7,964 79,640 71,674 4 / 4 / 4 31,856 / 31,856 / 31,856 127,424 / 127,424 / 127,424
case2869_pegase 25,086 18,328 183,280 164,680 4 / 4 / 4 73,312 / 73,312 / 73,312 293,248 / 293,248 / 293,248
case10000_goc 76,804 52,772 527,720 474,948 4 / 4 / 4 211,088 / 211,088 / 211,088 844,352 / 844,352 / 844,352
Initialization allocations and evaluator storage — all 30 cases

Initialization bytes/counts are reported by BenchmarkTools. Evaluator storage is Base.summarysize(evaluator) after initialization and evaluation; it is not peak process memory. MiB = 2²⁰ bytes. Repeated Hessian evaluation and isolated recovery allocate zero bytes in every configuration.

Case Legacy init MiB Acyclic init MiB Star init MiB Init allocation counts L / A / S Legacy evaluator MiB Acyclic evaluator MiB Star evaluator MiB
diagonal_100 0.265 0.282 0.279 1,872 / 1,987 / 1,951 0.137 0.143 0.142
diagonal_1000 2.607 2.738 2.721 17,210 / 17,338 / 17,300 1.314 1.376 1.360
diagonal_10000 25.476 26.740 26.587 170,227 / 170,359 / 170,321 12.472 13.083 12.930
rosenbrock_100 0.470 0.482 0.499 3,202 / 3,284 / 3,246 0.236 0.246 0.268
rosenbrock_1000 4.475 4.561 4.740 30,259 / 30,339 / 30,289 2.250 2.346 2.567
rosenbrock_10000 45.067 46.007 47.850 300,295 / 300,365 / 300,321 20.083 21.049 23.261
bilinear_100 0.177 0.179 0.179 1,406 / 1,483 / 1,446 0.064 0.068 0.072
bilinear_1000 1.733 1.723 1.727 12,267 / 12,342 / 12,296 0.533 0.568 0.614
bilinear_10000 16.822 16.602 16.690 120,302 / 120,363 / 120,323 5.833 6.188 6.645
arrowhead_100 0.300 0.315 0.305 2,201 / 2,289 / 2,245 0.121 0.131 0.128
arrowhead_1000 2.765 2.883 2.805 20,249 / 20,343 / 20,288 1.151 1.248 1.217
arrowhead_10000 28.103 29.223 28.459 200,277 / 200,369 / 200,314 10.919 11.885 11.579
grid_10 0.559 0.565 0.579 3,447 / 3,440 / 3,397 0.307 0.318 0.340
grid_30 4.948 5.025 5.164 29,573 / 29,526 / 29,476 2.788 2.897 3.105
grid_70 28.441 28.831 29.619 160,914 / 160,832 / 160,782 14.317 14.942 16.088
blocks_100 0.590 0.441 0.358 3,980 / 480 / 432 0.206 0.174 0.151
blocks_1000 3.860 3.799 3.023 5,560 / 1,882 / 1,836 1.467 1.672 1.439
blocks_5000 21.759 22.528 18.519 12,912 / 7,904 / 7,862 7.001 8.319 7.155
dense_50 1.216 0.467 0.357 21,117 / 392 / 344 0.332 0.178 0.149
dense_150 11.507 4.567 3.637 190,404 / 525 / 482 2.815 1.381 1.123
dense_300 46.016 18.113 14.441 763,071 / 723 / 681 11.104 5.318 4.286
ocp_20 0.172 0.218 0.203 2,558 / 3,684 / 3,288 0.078 0.093 0.087
ocp_100 0.820 1.038 0.968 11,710 / 16,997 / 15,161 0.363 0.437 0.407
ocp_500 3.940 5.027 4.682 57,345 / 83,445 / 74,407 1.636 2.021 1.864
case14_ieee 1.663 1.502 1.383 24,787 / 19,969 / 17,871 0.478 0.483 0.445
case118_ieee 15.685 14.165 13.063 230,875 / 185,553 / 166,191 4.452 4.501 4.144
case300_ieee 34.577 31.190 28.756 508,901 / 408,679 / 365,917 9.757 9.859 9.070
case1354_pegase 168.023 151.631 139.842 2,472,911 / 1,987,169 / 1,780,087 46.736 47.315 43.487
case2869_pegase 387.910 345.876 318.748 5,687,432 / 4,569,484 / 4,092,938 107.275 106.899 98.092
case10000_goc 1097.793 991.406 913.297 16,385,836 / 13,166,806 / 11,794,716 299.848 304.979 279.614

Correctness

Assembled Hessians were compared against legacy at two inputs per case, summing duplicate entries and removing structural zeros. All 120 comparisons passed with relative Frobenius-norm error at most 2.61e-15 (tolerance 1e-12). All 90 repeated-Hessian configurations and 90 isolated-recovery configurations reported zero Julia allocations. Solver objectives and iteration counts agree across all seven solve cases; successful local solves do not imply global optimality.

Interpretation

  • The default SMC path is close to legacy on many simple sparse cases and faster on dense and several OPF cases, but it is not a uniform speedup.
  • The block-dense regression is largely visible in recovery: at 5,000 variables, isolated recovery increases from about 98 to 251 µs, while the whole Hessian evaluation increases from 515 to 660 µs, with identical color counts and seed sizes.
  • Star coloring is a tradeoff: on Rosenbrock chains it uses three colors instead of two, and on grids five instead of four. Cheaper recovery does not always offset the extra derivative work.
  • Setup costs also matter: default SMC initialization is 35–41% slower on the optimal-control examples, while dense cases benefit substantially.
  • These chosen cases do not represent the distribution of all MOI workloads. Longer sampling is needed for the marked largest cases before making precise performance claims.

@blegat

blegat commented Oct 19, 2026

Copy link
Copy Markdown
Member Author

I made PR to SMC to address the performance regressions, see update PR description

This branch has not been deployed

No deployments
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.

Precision loss in Hessian computation [Nonlinear] sparsity pattern of Hessian with :(x * y)

3 participants