Skip to content

Latest commit

 

History

History
167 lines (114 loc) · 40.3 KB

File metadata and controls

167 lines (114 loc) · 40.3 KB

Implementation Status

2026-06-13: Hardened HPRLP.run_batched_dataset against per-instance failures: parser/solver/GPU errors are now caught inside the dataset loop, logged with a stacktrace, recorded as ERROR: ... rows in both aggregate and per-column CSV outputs, followed by best-effort memory cleanup, and the runner continues to the next .mps/.mps.gz file while still allowing user interrupts to stop the run.

2026-06-13: Added a true-batched dataset runner: HPRLP.run_batched_dataset reads each .mps/.mps.gz file in a directory, constructs copy or objective-perturbed matrix-form batches, runs optimize_batched_gpu, writes aggregate benchmark results to HPRLP_batched_result.csv, writes per-column results to HPRLP_batched_columns.csv, logs to HPRLP_batched_log.txt, and is exposed through scripts/run_dataset_batched.jl.

2026-06-13: Added params.warm_up support to the true-batched GPU API: the raw matrix-form entrypoint now performs an unmeasured capped batched setup+solve warm-up before timing the reported setup/solve path, the prebuilt shared/batch/scaling entrypoint performs an unmeasured solve warm-up, and scripts/run_single_file_batched.jl now prints whether script wall time includes warm-up while reported solver times exclude it.

2026-06-13: Reworked the true-batched GPU loop to follow the original solver workflow more closely: dense x/z and y updates now use explicit batched CUDA kernels with check/normal variants around SpMM calls, residual assembly uses batched kernels, scheduled residual checks also prepare check-state for print-frequency boundaries, per-column restart/adaptive-sigma state is tracked, completed batch columns are frozen through the active mask, and CUDA graph capture remains disabled for batched mode.

2026-06-13: Added scripts/run_single_file_batched.jl for the true matrix-form batched GPU API; it reads one LP instance, builds C/L/U as n x B and AL/AU as m x B using either exact copies or perturbed objective columns, runs optimize_batched_gpu, and prints per-column status/objective/residual timing summaries.

2026-06-13: Superseded by the later workflow-alignment entry above: the first true-batched GPU loop originally used SpMM plus dense matrix updates and fixed per-column sigma, but restart/adaptive-sigma handling and explicit update kernels have now been added.

2026-06-13: Updated BATCHED_HPRLP_ANALYSIS.md to remove the custom-CSR fallback from the first implementation plan; the true batched path should use cuSPARSE SpMM only for A * X and AT * Y at this stage.

2026-06-13: Rewrote BATCHED_HPRLP_ANALYSIS.md to clarify that the intended batched solver is a true GPU matrix-form implementation with C/L/U shaped n x B, AL/AU shaped m x B, batched workspaces/kernels, SpMM-style products, and no per-instance solve_scaled loop in the algorithmic path.

2026-06-12 through 2026-06-13: Earlier sequential shared-setup batch prototype notes were superseded after the prototype code was undone; the active direction is now the true matrix-form GPU batched implementation tracked above.

2026-06-09: Moved the multi-GPU limitations note out of the Documenter docs and kept it as a repo-local reference in MULTI_GPU_REFERENCE.md; removed the temporary docs navigation and parameter-guide changes so the published documentation stays unchanged.

2026-05-25: Added an explicit solved-empty-model fast path in solve for the 0x0 reduced problem case, so GPU presolve can fully eliminate an instance without falling into zero-grid scaling/kernel launches; added a focused regression test covering a (0,1) -> (0,0) GPU-presolve-to-postsolve solve.

2026-05-25: Parameterized the paper Curtis-Reid stop rule in scripts/compare_curtis_reid_scaling.jl and switched the source-backed comparison path to a tightly converged setting (maxit=10_000, smin=1e-12); re-testing now shows machine-precision agreement between the paper path and the project's alternating Curtis-Reid scaling on EnergyDispatch as well as Blending and Production.

2026-05-25: Tightened paper_curtis_reid_1972_logs in scripts/compare_curtis_reid_scaling.jl to a closer line-by-line transliteration of mc29d.f, including removing the incorrect per-iteration c reset and keeping the unclamped exp.(r) / exp.(c) output scaling; re-testing still shows machine-precision agreement with the exact MC29 least-squares reference on Blending and Production, but the same persistent mismatch on EnergyDispatch.

2026-05-25: Removed the min/max scale clamp from the paper/reference Curtis-Reid path in scripts/compare_curtis_reid_scaling.jl and re-tested; this did not change the alignment result, with the paper-based port still matching the exact MC29 least-squares reference on Blending and Production but not on EnergyDispatch.

2026-05-25: Added an explicit convergence-point verdict to scripts/compare_curtis_reid_scaling.jl, so each instance now reports directly whether the alternating project Curtis-Reid iterate and the paper-based 1972 Curtis-Reid target reach the same fixed point within a configurable max log-entry tolerance.

2026-05-25: Identified the original Curtis-Reid paper as A. R. Curtis and J. K. Reid, "On the Automatic Scaling of Matrices for Gaussian Elimination" (IMA Journal of Applied Mathematics, 10(1), 118-124, 1972; DOI 10.1093/imamat/10.1.118) and made the comparison script's MC29-source path explicit as a paper-based Curtis-Reid 1972 implementation.

2026-05-25: Refocused scripts/compare_curtis_reid_scaling.jl from solve-time benchmarking to a matrix-only convergence study: the script now compares the project's Curtis-Reid iterates at multiple iteration counts directly against the MC29-scaled matrix, reporting log-entry and scaled-matrix differences instead of downstream LP solve performance.

2026-05-24: Ported the downloaded mc29-1.0.0/src/mc29d.f routine directly into scripts/compare_curtis_reid_scaling.jl as a source-faithful Julia fallback so Curtis-Reid comparisons can use real MC29 behavior even on machines without a Fortran compiler or a licensed HSL_jll installation.

2026-05-22: Added HSL / HSL_jll to the root project environment so the standalone Curtis-Reid comparison script can call HSL.mc29 directly when available, instead of always using the in-script least-squares fallback.

2026-05-22: Added a standalone scripts/compare_curtis_reid_scaling.jl benchmark that extracts the project's CPU Curtis-Reid scaling into an individual script, compares it against an HSL.mc29 Curtis-Reid path when that licensed package is installed, falls back to an exact least-squares solve of the same MC29 objective otherwise, and reports scaling/solve metrics on several small but numerically meaningful LP instances.

2026-05-15: Replaced the temporary PSLP memory-estimation skip with dedicated Julia worker isolation for PSLP presolve/postsolve: the native PSLP state now lives on a per-solve worker process, parent-side presolve falls back cleanly if that worker dies during the native call, and a focused test now covers isolated worker bootstrap in the package project.

2026-05-15: Added a conservative PSLP native-memory preflight in the Julia wrapper so large instances skip PSLP before entering new_presolver when the estimated init footprint would exceed a configurable fraction of free system memory; this targets the native OOM/segfault path that cannot be caught once execution enters libPSLP, and includes a focused test for the guard calculation.

2026-05-15: Hardened both apply_presolve entrypoints against internal backend exceptions by routing GPU and PSLP presolve through a shared fallback helper that logs the failure, reclaims GPU memory on the GPU path when possible, and continues with the original model instead of aborting the solve; added a focused regression test for the fallback contract.

2026-05-07: Extended the GitHub Actions Pages documentation artifact to publish separate dev/ docs from the workflow checkout and stable/ docs from the latest v* release tag, with the site root redirecting to stable/.

2026-05-07: Migrated the documentation workflow from Documenter gh-pages branch pushes to GitHub Actions Pages artifacts: docs now build into docs/build, PRs remain build-only, and non-PR docs runs upload/deploy the artifact through actions/deploy-pages.

2026-04-30: Fixed the main Julia test failures by validating requested GPU devices before all GPU transfer paths, letting invalid device_number values fall back to CPU for non-presolve solves, and refreshed the Curtis-Reid parameter validation test to assert the current fixed 20-iteration scaling constant.

2026-04-30: Added Curtis-Reid geometric-mean scaling before Ruiz in both CPU and GPU scaling paths, exposed use_Curtis_Reid_scaling through solver parameters and MOI raw attributes, fixed Curtis-Reid to 20 solver iterations, and documented the new scaling option.

2026-04-26: Added focused intra-rule memory checkpoints inside apply_rule_primal_propagation! so large-instance GPU presolve runs now report memory after each major allocation / compute step within that rule; this is specifically to resolve the iter-2 OOM that occurs after subset:stats but before the subset-level plan checkpoint.

2026-04-26: Refined GPU presolve memory telemetry again so each subset log now includes stage deltas relative to subset start / previous stage plus a one-line subset summary reporting peak, net, and recovered GiB; this makes the large-instance logs directly comparable without manual subtraction and better exposes which subset actually consumes the remaining headroom.

2026-06-16: Expanded autosave into an exact-resume checkpoint: HDF5 files now retain current/best x/y/z plus the durable continuation state (x, y, bar variables, y_obj, last restart anchors, restart/tolerance state, sigma/lambda, and elapsed time) while leaving scratch x_hat/y_hat and dx/dy out. Added a public optimize_from_autosave continuation path whose iteration numbering resumes from the saved iteration and recomputes that iteration's residual/log/restart decision. JuMP raw attributes now expose auto_save and save_filename.

2026-04-26: Added a safe GPU presolve fallback in _optimize_impl so CUDA out-of-memory failures during the GPU presolve phase reclaim transient GPU memory, skip presolve, and continue solving the original problem instead of aborting the run.

2026-04-26: Reworked the GPU presolve/solve handoff so GPUPresolve.run_presolve now accepts only LP_info_gpu, _optimize_impl owns CPU->GPU transfer for both the no-presolve and PSLP-presolve GPU solve paths, GPU presolve reuses that already-uploaded model instead of copying again, and solve now dispatches on the model storage it is given (LP_info_cpu or LP_info_gpu) rather than performing its own implicit transfer.

2026-04-25: Restored the GPU presolve memory telemetry path after finding _log_presolve_memory! short-circuited, added per-rule subset memory logs (subset:start, stats, plan, apply, after_reclaim) in _run_phase_rule_subset, and introduced an opt-in PresolveParams.reclaim_between_rule_subsets probe so large-instance server runs can distinguish allocator-retention pressure from still-live presolve scratch/state.

2026-04-24: Closed another large-instance ingest gap versus the C reader by routing oversized plain .mps files through MPSReader's existing chunked text scanner instead of the older eachline streaming loop; large plain files now reuse the same reusable-buffer/carry parsing strategy as .mps.gz, which should reduce per-line allocation and line-iteration overhead on giant inputs.

2026-04-24: Reduced MPSReader free-format hot-path overhead by bypassing MPSCard construction and _process_card! dispatch inside the main line scanners; free-format reads now dispatch directly from each scanned line into the existing section handlers, preserving behavior while targeting a smaller per-line cost on large free-format instances.

2026-04-24: Hardened the vendored MPSReader fixed-format parser against silent field-width overflow by rejecting lines whose content spills into fixed-field separator columns; this prevents long free-format names from aliasing distinct rows/columns under mpsformat=:fixed, lets mpsformat=:auto fall back to :free, and adds a regression test covering the previous long-column-name collapse.

2026-04-16: Redesigned autotune_custom_update_backends! to benchmark x and y GPU backends independently, comparing cuSPARSE vs customized with bench_iters normal updates plus one check update per side. 2026-04-17: Removed the temporary PSLP compatibility shim and wired HPR-LP directly to a dedicated GPUPresolve backend over the vendored GPU presolve/postsolve code from HPR-LP-presolve-test/src/presolve, keeping the existing presolve seam while extending LP_info_gpu with transpose-slack metadata required by the new backend. 2026-04-17: Promoted the stronger GPU presolve schedule from HPR-LP-presolve-test into the default PresolveParams values inside the presolve backend and removed the extra public PresolveParams exposure from the standard HPR-LP user path, while keeping internal custom-parameter plumbing for presolve rule verification utilities. 2026-04-17: Disabled presolve in the Julia test suite's shared test parameter helper so solver, warm-start, GPU fallback, and auto-save tests exercise the non-presolve path explicitly instead of inheriting the default presolve setting. 2026-04-17: Updated the Julia test suite to match the current API by switching build_from_mps to positional verbosity, fixing the one stray build_from_Abc call that passed params as obj_constant, and rewriting original-KKT tests around the current five-value metrics function plus check_org_recovery_failures. 2026-04-21: Reviewed HPR-LP-presolve-test commit 4e3511a (gpu-presolve-test) and ported its core runtime changes into HPR-LP's dedicated GPUPresolve backend: tape-driven postsolve now restores z before x, parallel-column replay uses dual-aware recovery, direct tape replay handles more row-bound cases, PresolveParams carries postsolve_tol, and solver results now record presolve/postsolve timing plus reduced/original residual summaries. 2026-04-21: Fixed a GPU result-collection regression from the sync step: collect_results_gpu! had started storing results.x/y/z as CuVectors while materializing host Vectors into them with broadcast, which triggered CUDA's "non-bitstype argument" kernel error; the result buffers are now allocated on CPU again before copying scaled solution values back. 2026-04-21: Added a standalone MPS ingest package planning note in MPS_INGEST_PACKAGE_PLAN.md, covering direct streamed .mps.gz ingest, optional pigz integration for multicore decompression, cache strategy, and the rationale for keeping GPU parsing out of the initial design. 2026-04-21: Scaffolded a separate MPSReader package folder for standalone .mps and .mps.gz ingest with direct numeric output, Julia gzip streaming, optional pigz support, and package-local tests instead of modifying HPR-LP's main code path. 2026-04-21: Added a standalone MPSReader/bench benchmark entrypoint to compare JuMP.read_from_file against MPSReader.read_mps on plain .mps and Julia-gzip .mps.gz inputs without requiring pigz. 2026-04-21: Simplified MPSReader to the Julia-only gzip path for now, removing the deferred pigz branch from the API and shifting the default read path to solver-oriented ingestion without name retention or extra vector copies. 2026-04-21: Replaced MPSReader's temporary MOI-based backend with a native Julia MPS parser ported from the mps_reader.cpp reference, removing both QPSReader and MathOptInterface from the package dependencies; the new reader passes package tests and benchmarks faster than JuMP.read_from_file on the tested Hans savsched1 and scpm1_lp instances. 2026-04-22: Profiled the native MPSReader parser on Hans instances, then cut hot-path allocation overhead by removing transient String(...) conversions for dictionary lookups and replacing the per-line temporary array in free-format field parsing with direct local-field extraction; after those changes, MPSReader improved to roughly 0.66s vs 1.41s on savsched1.mps, 1.18s vs 1.69s on savsched1.mps.gz, 1.45s vs 3.15s on scpm1_lp.mps, and 2.72s vs 4.15s on scpm1_lp.mps.gz relative to JuMP.read_from_file. 2026-04-22: Replaced the parser's generic Dict{String,Int} name-index maps with a native chained hash map modeled on the C++ reference, preserving AbstractString lookup to avoid transient key allocation while storing owned String keys only on insert; package tests still pass, and the Hans benchmarks improved further to roughly 0.58s vs 1.40s on savsched1.mps, 1.07s vs 1.68s on savsched1.mps.gz, 1.36s vs 3.19s on scpm1_lp.mps, and 2.65s vs 4.18s on scpm1_lp.mps.gz relative to JuMP.read_from_file. 2026-04-22: Removed plain-file readline overhead in MPSReader by adding a whole-text scanner path for .mps inputs and sharing section-processing logic between streamed and text-backed parsing; package tests still pass, savsched1.mps improved again to roughly 0.57s vs 1.42s for JuMP.read_from_file, and larger-file validation on dlr1 reached roughly 11.27s vs 63.63s on plain .mps plus 19.60s vs 85.73s on .mps.gz. 2026-04-22: Optimized the plain .mps hot path again by caching consecutive column-name resolution in the COLUMNS section and removing modulo from the custom name-map hash via power-of-two bucket masking; package tests still pass, and dlr1.mps improved from the prior MPSReader baseline of roughly 11.27s to 7.58s with unchanged shape (1735470, 9142907, 18365107). 2026-04-22: Added a specialized ASCII decimal fast path for numeric field parsing, keeping fallback handling for unusual formats while targeting the overwhelmingly common non-scientific MPS tokens; package tests still pass, dlr1.mps improved again from 7.58s to 7.01s, and the larger plain instance thk_48.mps improved from 19.63s to 13.48s with unchanged shape (6366377, 8609262, 27802878). 2026-04-22: Removed the main .mps.gz overhead by replacing the GzipDecompressorStream per-line readline loop with a chunked decompressed-text scanner plus carry-over for split lines, allowing gzip inputs to reuse the low-allocation text parsing path; package tests still pass, the warmed dlr1.mps.gz read improved from the prior MPSReader baseline of roughly 19.60s to 7.56s with unchanged shape (1735470, 9142907, 18365107), and a larger warmed thk_48.mps.gz run completed in roughly 15.34s with shape (6366377, 8609262, 27802878). 2026-04-22: Reworked the giant-instance ingest path to estimate .mps.gz capacities from gzip-aware size hints rather than compressed bytes alone, added fixed-format packed integer keys for row/column lookup, and stopped storing all row/column name strings unless keep_names=true; package tests still pass, and a warmed Dual2_5000.mps.gz validation run remained roughly flat at 95.48s with shape (30000600, 33050602, 93001800), so these changes currently look more like groundwork for the next parser rewrite than an immediate wall-time win. 2026-04-22: Replaced the fixed-format hot path's generic MPSCard construction and _process_card! dispatch with direct section-specific field extraction/dispatch while keeping the free-format path unchanged; Pkg.test() still passes in MPSReader, and fresh-session restarted-REPL benchmarks on Dual2_5000.mps.gz currently reproduce at roughly 76s with unchanged shape (30000600, 33050602, 93001800), which is still materially better than the earlier ~95.48s baseline even though an earlier one-off 64.70s terminal reading was not reproduced. 2026-04-22: Applied the byte-range fixed-format parser incrementally and tested each step in isolation: first moving the COLUMNS hot path off per-field SubString creation gave a small but reproducible fresh-session improvement on Dual2_5000.mps.gz to roughly 75.3s-75.5s, then extending the same span-based handling to fixed RHS, RANGES, and BOUNDS improved the same benchmark further to roughly 74.2s-74.8s with unchanged shape (30000600, 33050602, 93001800); Pkg.test() still passes after each step. 2026-04-22: Tested a follow-up replacement of the fixed-format packed-name Dict{UInt64,Int} path with a custom chained UInt64 map, but fresh-session Dual2_5000.mps.gz benchmarks regressed to about 76.5s, so the experiment was reverted; the kept parser baseline after restart currently remains in the roughly 74.7s-75.9s range with unchanged shape (30000600, 33050602, 93001800). 2026-04-22: Reorganized the root integration so HPRLP no longer depends on MPSReader as a separately resolved package: src/HPRLP.jl now includes the vendored MPSReader/src/MPSReader.jl source directly as an internal submodule, root Project.toml dropped the MPSReader dependency and added CodecZlib, and the temporary bootstrap workaround was removed; this keeps MPSReader/ as an individual folder while avoiding manifest-free Pkg.instantiate() failures caused by unregistered package resolution. 2026-04-22: Embedded the PSLP presolve/postsolve API from branch HPR-LP-PSLP into the current dedicated presolve structure by adding a params.presolve backend selector with :GPU, :PSLP, and :NONE, routing solver-side presolve through either GPUPresolve or PSLP, dispatching postsolve by returned backend state, keeping MOI.RawOptimizerAttribute("use_presolve") as a compatibility alias to GPU-or-none behavior, and making PSLP an optional load-time dependency so missing libPSLP only disables that backend instead of breaking package import. 2026-04-22: Aligned the active HPRLP MPS ingest path with branch test/MPSReader by vendoring MPSReader/src, switching build_from_mps from QPSReader.readqps to MPSReader.read_mps, preserving the existing timing/verbosity wrapper while adding mpsformat forwarding, and removing the now-unused root QPSReader dependency in favor of the vendored MPSReader plus CodecZlib. 2026-04-22: Repaired the docs workflow after the merge by reattaching the public optimize docstring to HPRLP.optimize instead of the private _optimize_impl helper and refreshing docs/Manifest.toml so the docs environment picks up the current HPRLP dependency set including CodecZlib rather than the stale QPSReader entry. 2026-04-23: Fixed the GitHub docs build failure caused by unqualified API bindings in docs/src/api.md; the @docs blocks now reference HPRLP.* symbols explicitly so Documenter resolves the package API consistently in CI. 2026-04-23: Corrected the GPU presolve rebuild reclaim ordering so GC.gc(true) / CUDA.reclaim() now runs before constructing AT_new in both row and col rebuild branches, and verified the new rebuild:after_reclaim -> rebuild:AT_new sequence with a focused GPU presolve smoke test. 2026-04-23: Added explicit per-instance cleanup in run_dataset so each dataset solve drops its model/result references, forces GC.gc(true), and calls CUDA.reclaim() after GPU solves; this prevents the CUDA pool from looking like it accumulates memory across successive optimize calls in long dataset runs. 2026-04-23: Reduced peak GPU presolve memory during structural rebuilds in presolve_apply_plan by dropping the first transpose intermediates (A_rows, AT_rows) and reclaiming the CUDA pool before constructing the final AT_new; this targets large-instance OOMs triggered by cuSPARSE transpose workspace allocation while multiple huge CSR matrices were still live. 2026-04-23: Reverted the temporary GPU presolve intermediate-release experiment in presolve_apply_plan and replaced it with targeted presolve memory telemetry in src/presolve/gpu_presolve.jl; verbose runs now log each row/col rebuild stage (rebuild:start, A_rows, AT_rows, A_new, AT_new, done) with matrix dims, nnz, and GPU used/free GiB so large-instance OOMs can be tied to a specific phase and shape.# Implementation Status

2026-05-15: Replaced the temporary PSLP memory-estimation skip with dedicated Julia worker isolation for PSLP presolve/postsolve: the native PSLP state now lives on a per-solve worker process, parent-side presolve falls back cleanly if that worker dies during the native call, and a focused test now covers isolated worker bootstrap in the package project.

2026-05-15: Added a conservative PSLP native-memory preflight in the Julia wrapper so large instances skip PSLP before entering new_presolver when the estimated init footprint would exceed a configurable fraction of free system memory; this targets the native OOM/segfault path that cannot be caught once execution enters libPSLP, and includes a focused test for the guard calculation.

2026-05-15: Hardened both apply_presolve entrypoints against internal backend exceptions by routing GPU and PSLP presolve through a shared fallback helper that logs the failure, reclaims GPU memory on the GPU path when possible, and continues with the original model instead of aborting the solve; added a focused regression test for the fallback contract.

2026-05-07: Extended the GitHub Actions Pages documentation artifact to publish separate dev/ docs from the workflow checkout and stable/ docs from the latest v* release tag, with the site root redirecting to stable/.

2026-05-07: Migrated the documentation workflow from Documenter gh-pages branch pushes to GitHub Actions Pages artifacts: docs now build into docs/build, PRs remain build-only, and non-PR docs runs upload/deploy the artifact through actions/deploy-pages.

2026-04-30: Fixed the main Julia test failures by validating requested GPU devices before all GPU transfer paths, letting invalid device_number values fall back to CPU for non-presolve solves, and refreshed the Curtis-Reid parameter validation test to assert the current fixed 20-iteration scaling constant.

2026-04-30: Added Curtis-Reid geometric-mean scaling before Ruiz in both CPU and GPU scaling paths, exposed use_Curtis_Reid_scaling through solver parameters and MOI raw attributes, fixed Curtis-Reid to 20 solver iterations, and documented the new scaling option.

2026-04-26: Added focused intra-rule memory checkpoints inside apply_rule_primal_propagation! so large-instance GPU presolve runs now report memory after each major allocation / compute step within that rule; this is specifically to resolve the iter-2 OOM that occurs after subset:stats but before the subset-level plan checkpoint.

2026-04-26: Refined GPU presolve memory telemetry again so each subset log now includes stage deltas relative to subset start / previous stage plus a one-line subset summary reporting peak, net, and recovered GiB; this makes the large-instance logs directly comparable without manual subtraction and better exposes which subset actually consumes the remaining headroom.

2026-04-26: Added a safe GPU presolve fallback in _optimize_impl so CUDA out-of-memory failures during the GPU presolve phase reclaim transient GPU memory, skip presolve, and continue solving the original problem instead of aborting the run.

2026-04-26: Reworked the GPU presolve/solve handoff so GPUPresolve.run_presolve now accepts only LP_info_gpu, _optimize_impl owns CPU->GPU transfer for both the no-presolve and PSLP-presolve GPU solve paths, GPU presolve reuses that already-uploaded model instead of copying again, and solve now dispatches on the model storage it is given (LP_info_cpu or LP_info_gpu) rather than performing its own implicit transfer.

2026-04-25: Restored the GPU presolve memory telemetry path after finding _log_presolve_memory! short-circuited, added per-rule subset memory logs (subset:start, stats, plan, apply, after_reclaim) in _run_phase_rule_subset, and introduced an opt-in PresolveParams.reclaim_between_rule_subsets probe so large-instance server runs can distinguish allocator-retention pressure from still-live presolve scratch/state.

2026-04-24: Closed another large-instance ingest gap versus the C reader by routing oversized plain .mps files through MPSReader's existing chunked text scanner instead of the older eachline streaming loop; large plain files now reuse the same reusable-buffer/carry parsing strategy as .mps.gz, which should reduce per-line allocation and line-iteration overhead on giant inputs.

2026-04-24: Reduced MPSReader free-format hot-path overhead by bypassing MPSCard construction and _process_card! dispatch inside the main line scanners; free-format reads now dispatch directly from each scanned line into the existing section handlers, preserving behavior while targeting a smaller per-line cost on large free-format instances.

2026-04-24: Hardened the vendored MPSReader fixed-format parser against silent field-width overflow by rejecting lines whose content spills into fixed-field separator columns; this prevents long free-format names from aliasing distinct rows/columns under mpsformat=:fixed, lets mpsformat=:auto fall back to :free, and adds a regression test covering the previous long-column-name collapse.

2026-04-16: Redesigned autotune_custom_update_backends! to benchmark x and y GPU backends independently, comparing cuSPARSE vs customized with bench_iters normal updates plus one check update per side. 2026-04-17: Removed the temporary PSLP compatibility shim and wired HPR-LP directly to a dedicated GPUPresolve backend over the vendored GPU presolve/postsolve code from HPR-LP-presolve-test/src/presolve, keeping the existing presolve seam while extending LP_info_gpu with transpose-slack metadata required by the new backend. 2026-04-17: Promoted the stronger GPU presolve schedule from HPR-LP-presolve-test into the default PresolveParams values inside the presolve backend and removed the extra public PresolveParams exposure from the standard HPR-LP user path, while keeping internal custom-parameter plumbing for presolve rule verification utilities. 2026-04-17: Disabled presolve in the Julia test suite's shared test parameter helper so solver, warm-start, GPU fallback, and auto-save tests exercise the non-presolve path explicitly instead of inheriting the default presolve setting. 2026-04-17: Updated the Julia test suite to match the current API by switching build_from_mps to positional verbosity, fixing the one stray build_from_Abc call that passed params as obj_constant, and rewriting original-KKT tests around the current five-value metrics function plus check_org_recovery_failures. 2026-04-21: Reviewed HPR-LP-presolve-test commit 4e3511a (gpu-presolve-test) and ported its core runtime changes into HPR-LP's dedicated GPUPresolve backend: tape-driven postsolve now restores z before x, parallel-column replay uses dual-aware recovery, direct tape replay handles more row-bound cases, PresolveParams carries postsolve_tol, and solver results now record presolve/postsolve timing plus reduced/original residual summaries. 2026-04-21: Fixed a GPU result-collection regression from the sync step: collect_results_gpu! had started storing results.x/y/z as CuVectors while materializing host Vectors into them with broadcast, which triggered CUDA's "non-bitstype argument" kernel error; the result buffers are now allocated on CPU again before copying scaled solution values back. 2026-04-21: Added a standalone MPS ingest package planning note in MPS_INGEST_PACKAGE_PLAN.md, covering direct streamed .mps.gz ingest, optional pigz integration for multicore decompression, cache strategy, and the rationale for keeping GPU parsing out of the initial design. 2026-04-21: Scaffolded a separate MPSReader package folder for standalone .mps and .mps.gz ingest with direct numeric output, Julia gzip streaming, optional pigz support, and package-local tests instead of modifying HPR-LP's main code path. 2026-04-21: Added a standalone MPSReader/bench benchmark entrypoint to compare JuMP.read_from_file against MPSReader.read_mps on plain .mps and Julia-gzip .mps.gz inputs without requiring pigz. 2026-04-21: Simplified MPSReader to the Julia-only gzip path for now, removing the deferred pigz branch from the API and shifting the default read path to solver-oriented ingestion without name retention or extra vector copies. 2026-04-21: Replaced MPSReader's temporary MOI-based backend with a native Julia MPS parser ported from the mps_reader.cpp reference, removing both QPSReader and MathOptInterface from the package dependencies; the new reader passes package tests and benchmarks faster than JuMP.read_from_file on the tested Hans savsched1 and scpm1_lp instances. 2026-04-22: Profiled the native MPSReader parser on Hans instances, then cut hot-path allocation overhead by removing transient String(...) conversions for dictionary lookups and replacing the per-line temporary array in free-format field parsing with direct local-field extraction; after those changes, MPSReader improved to roughly 0.66s vs 1.41s on savsched1.mps, 1.18s vs 1.69s on savsched1.mps.gz, 1.45s vs 3.15s on scpm1_lp.mps, and 2.72s vs 4.15s on scpm1_lp.mps.gz relative to JuMP.read_from_file. 2026-04-22: Replaced the parser's generic Dict{String,Int} name-index maps with a native chained hash map modeled on the C++ reference, preserving AbstractString lookup to avoid transient key allocation while storing owned String keys only on insert; package tests still pass, and the Hans benchmarks improved further to roughly 0.58s vs 1.40s on savsched1.mps, 1.07s vs 1.68s on savsched1.mps.gz, 1.36s vs 3.19s on scpm1_lp.mps, and 2.65s vs 4.18s on scpm1_lp.mps.gz relative to JuMP.read_from_file. 2026-04-22: Removed plain-file readline overhead in MPSReader by adding a whole-text scanner path for .mps inputs and sharing section-processing logic between streamed and text-backed parsing; package tests still pass, savsched1.mps improved again to roughly 0.57s vs 1.42s for JuMP.read_from_file, and larger-file validation on dlr1 reached roughly 11.27s vs 63.63s on plain .mps plus 19.60s vs 85.73s on .mps.gz. 2026-04-22: Optimized the plain .mps hot path again by caching consecutive column-name resolution in the COLUMNS section and removing modulo from the custom name-map hash via power-of-two bucket masking; package tests still pass, and dlr1.mps improved from the prior MPSReader baseline of roughly 11.27s to 7.58s with unchanged shape (1735470, 9142907, 18365107). 2026-04-22: Added a specialized ASCII decimal fast path for numeric field parsing, keeping fallback handling for unusual formats while targeting the overwhelmingly common non-scientific MPS tokens; package tests still pass, dlr1.mps improved again from 7.58s to 7.01s, and the larger plain instance thk_48.mps improved from 19.63s to 13.48s with unchanged shape (6366377, 8609262, 27802878). 2026-04-22: Removed the main .mps.gz overhead by replacing the GzipDecompressorStream per-line readline loop with a chunked decompressed-text scanner plus carry-over for split lines, allowing gzip inputs to reuse the low-allocation text parsing path; package tests still pass, the warmed dlr1.mps.gz read improved from the prior MPSReader baseline of roughly 19.60s to 7.56s with unchanged shape (1735470, 9142907, 18365107), and a larger warmed thk_48.mps.gz run completed in roughly 15.34s with shape (6366377, 8609262, 27802878). 2026-04-22: Reworked the giant-instance ingest path to estimate .mps.gz capacities from gzip-aware size hints rather than compressed bytes alone, added fixed-format packed integer keys for row/column lookup, and stopped storing all row/column name strings unless keep_names=true; package tests still pass, and a warmed Dual2_5000.mps.gz validation run remained roughly flat at 95.48s with shape (30000600, 33050602, 93001800), so these changes currently look more like groundwork for the next parser rewrite than an immediate wall-time win. 2026-04-22: Replaced the fixed-format hot path's generic MPSCard construction and _process_card! dispatch with direct section-specific field extraction/dispatch while keeping the free-format path unchanged; Pkg.test() still passes in MPSReader, and fresh-session restarted-REPL benchmarks on Dual2_5000.mps.gz currently reproduce at roughly 76s with unchanged shape (30000600, 33050602, 93001800), which is still materially better than the earlier ~95.48s baseline even though an earlier one-off 64.70s terminal reading was not reproduced. 2026-04-22: Applied the byte-range fixed-format parser incrementally and tested each step in isolation: first moving the COLUMNS hot path off per-field SubString creation gave a small but reproducible fresh-session improvement on Dual2_5000.mps.gz to roughly 75.3s-75.5s, then extending the same span-based handling to fixed RHS, RANGES, and BOUNDS improved the same benchmark further to roughly 74.2s-74.8s with unchanged shape (30000600, 33050602, 93001800); Pkg.test() still passes after each step. 2026-04-22: Tested a follow-up replacement of the fixed-format packed-name Dict{UInt64,Int} path with a custom chained UInt64 map, but fresh-session Dual2_5000.mps.gz benchmarks regressed to about 76.5s, so the experiment was reverted; the kept parser baseline after restart currently remains in the roughly 74.7s-75.9s range with unchanged shape (30000600, 33050602, 93001800). 2026-04-22: Reorganized the root integration so HPRLP no longer depends on MPSReader as a separately resolved package: src/HPRLP.jl now includes the vendored MPSReader/src/MPSReader.jl source directly as an internal submodule, root Project.toml dropped the MPSReader dependency and added CodecZlib, and the temporary bootstrap workaround was removed; this keeps MPSReader/ as an individual folder while avoiding manifest-free Pkg.instantiate() failures caused by unregistered package resolution. 2026-04-22: Embedded the PSLP presolve/postsolve API from branch HPR-LP-PSLP into the current dedicated presolve structure by adding a params.presolve backend selector with :GPU, :PSLP, and :NONE, routing solver-side presolve through either GPUPresolve or PSLP, dispatching postsolve by returned backend state, keeping MOI.RawOptimizerAttribute("use_presolve") as a compatibility alias to GPU-or-none behavior, and making PSLP an optional load-time dependency so missing libPSLP only disables that backend instead of breaking package import. 2026-04-22: Aligned the active HPRLP MPS ingest path with branch test/MPSReader by vendoring MPSReader/src, switching build_from_mps from QPSReader.readqps to MPSReader.read_mps, preserving the existing timing/verbosity wrapper while adding mpsformat forwarding, and removing the now-unused root QPSReader dependency in favor of the vendored MPSReader plus CodecZlib. 2026-04-22: Repaired the docs workflow after the merge by reattaching the public optimize docstring to HPRLP.optimize instead of the private _optimize_impl helper and refreshing docs/Manifest.toml so the docs environment picks up the current HPRLP dependency set including CodecZlib rather than the stale QPSReader entry. 2026-04-23: Fixed the GitHub docs build failure caused by unqualified API bindings in docs/src/api.md; the @docs blocks now reference HPRLP.* symbols explicitly so Documenter resolves the package API consistently in CI. 2026-04-23: Corrected the GPU presolve rebuild reclaim ordering so GC.gc(true) / CUDA.reclaim() now runs before constructing AT_new in both row and col rebuild branches, and verified the new rebuild:after_reclaim -> rebuild:AT_new sequence with a focused GPU presolve smoke test. 2026-04-23: Added explicit per-instance cleanup in run_dataset so each dataset solve drops its model/result references, forces GC.gc(true), and calls CUDA.reclaim() after GPU solves; this prevents the CUDA pool from looking like it accumulates memory across successive optimize calls in long dataset runs. 2026-04-23: Reduced peak GPU presolve memory during structural rebuilds in presolve_apply_plan by dropping the first transpose intermediates (A_rows, AT_rows) and reclaiming the CUDA pool before constructing the final AT_new; this targets large-instance OOMs triggered by cuSPARSE transpose workspace allocation while multiple huge CSR matrices were still live. 2026-04-23: Reverted the temporary GPU presolve intermediate-release experiment in presolve_apply_plan and replaced it with targeted presolve memory telemetry in src/presolve/gpu_presolve.jl; verbose runs now log each row/col rebuild stage (rebuild:start, A_rows, AT_rows, A_new, AT_new, done) with matrix dims, nnz, and GPU used/free GiB so large-instance OOMs can be tied to a specific phase and shape.