dfbench 0.3.1__tar.gz → 0.3.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. {dfbench-0.3.1 → dfbench-0.3.2}/.gitignore +1 -5
  2. {dfbench-0.3.1 → dfbench-0.3.2}/PKG-INFO +4 -3
  3. {dfbench-0.3.1 → dfbench-0.3.2}/README.md +3 -2
  4. {dfbench-0.3.1 → dfbench-0.3.2}/docs/Architecture-Overview.md +23 -17
  5. {dfbench-0.3.1 → dfbench-0.3.2}/docs/Implementing-a-New-Algorithm.md +24 -5
  6. {dfbench-0.3.1 → dfbench-0.3.2}/docs/Objective-API-Reference.md +60 -42
  7. {dfbench-0.3.1 → dfbench-0.3.2}/docs/Problems.md +2 -2
  8. {dfbench-0.3.1 → dfbench-0.3.2}/docs/Storage-and-Checkpointing.md +23 -6
  9. {dfbench-0.3.1 → dfbench-0.3.2}/pyproject.toml +1 -1
  10. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/core/objective.py +378 -670
  11. dfbench-0.3.2/src/dfbench/core/storage/saveconfig.py +194 -0
  12. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/core/storage/state.py +12 -11
  13. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/problems/base_problem.py +16 -12
  14. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/problems/voyager/constrained_voyager_problem.py +1 -1
  15. {dfbench-0.3.1 → dfbench-0.3.2}/tests/test_objective_invariants.py +48 -3
  16. {dfbench-0.3.1 → dfbench-0.3.2}/tests/test_objective_penalty_fn.py +405 -35
  17. dfbench-0.3.1/src/dfbench/core/storage/saveconfig.py +0 -312
  18. {dfbench-0.3.1 → dfbench-0.3.2}/LICENSE +0 -0
  19. {dfbench-0.3.1 → dfbench-0.3.2}/docs/Algorithm-Status.md +0 -0
  20. {dfbench-0.3.1 → dfbench-0.3.2}/docs/Algorithms.md +0 -0
  21. {dfbench-0.3.1 → dfbench-0.3.2}/docs/Benchmarking.md +0 -0
  22. {dfbench-0.3.1 → dfbench-0.3.2}/docs/FAQ.md +0 -0
  23. {dfbench-0.3.1 → dfbench-0.3.2}/docs/Home.md +0 -0
  24. {dfbench-0.3.1 → dfbench-0.3.2}/docs/Installation.md +0 -0
  25. {dfbench-0.3.1 → dfbench-0.3.2}/docs/Metrics-Reference.md +0 -0
  26. {dfbench-0.3.1 → dfbench-0.3.2}/docs/Testing.md +0 -0
  27. {dfbench-0.3.1 → dfbench-0.3.2}/docs/Utilities-and-Helpers.md +0 -0
  28. {dfbench-0.3.1 → dfbench-0.3.2}/docs/_Sidebar.md +0 -0
  29. {dfbench-0.3.1 → dfbench-0.3.2}/docs/algorithm-status/main.typ +0 -0
  30. {dfbench-0.3.1 → dfbench-0.3.2}/docs/algorithm-status/template.typ +0 -0
  31. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/__init__.py +0 -0
  32. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/__init__.py +0 -0
  33. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/derivative_free/__init__.py +0 -0
  34. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/derivative_free/_dfo_common.py +0 -0
  35. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/derivative_free/_scipy_common.py +0 -0
  36. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/derivative_free/nelder_mead.py +0 -0
  37. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/derivative_free/omads_mads.py +0 -0
  38. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/derivative_free/pdfo/__init__.py +0 -0
  39. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/derivative_free/pdfo/lincoa.py +0 -0
  40. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/derivative_free/pdfo/newuoa.py +0 -0
  41. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/derivative_free/pdfo/uobyqa.py +0 -0
  42. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/derivative_free/powell.py +0 -0
  43. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/derivative_free/pybobyqa.py +0 -0
  44. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/evolutionary/__init__.py +0 -0
  45. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/evolutionary/cmaes_sep_cma.py +0 -0
  46. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/evolutionary/evosax_es.py +0 -0
  47. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/evolutionary/evox_es.py +0 -0
  48. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/evolutionary/evox_pso.py +0 -0
  49. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/evolutionary/jax_es.py +0 -0
  50. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/evolutionary/nevergrad/__init__.py +0 -0
  51. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/evolutionary/nevergrad/_common.py +0 -0
  52. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/evolutionary/nevergrad/ngopt.py +0 -0
  53. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/evolutionary/nevergrad/oneplusone.py +0 -0
  54. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/evolutionary/nevergrad/tbpsa.py +0 -0
  55. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/evolutionary/pycma_cmaes.py +0 -0
  56. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/generative/vae_sampling.py +0 -0
  57. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/global_search/__init__.py +0 -0
  58. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/global_search/basin_hopping.py +0 -0
  59. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/global_search/dual_annealing.py +0 -0
  60. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/global_search/random_search.py +0 -0
  61. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/__init__.py +0 -0
  62. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/adam_gd.py +0 -0
  63. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/custom_jax.py +0 -0
  64. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/lbfgs_gd.py +0 -0
  65. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/na_adam_gd.py +0 -0
  66. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/__init__.py +0 -0
  67. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/_common.py +0 -0
  68. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/adabelief.py +0 -0
  69. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/adadelta.py +0 -0
  70. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/adafactor.py +0 -0
  71. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/adagrad.py +0 -0
  72. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/adam.py +0 -0
  73. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/adamax.py +0 -0
  74. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/adamaxw.py +0 -0
  75. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/adamw.py +0 -0
  76. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/adan.py +0 -0
  77. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/amsgrad.py +0 -0
  78. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/lamb.py +0 -0
  79. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/lion.py +0 -0
  80. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/lookahead.py +0 -0
  81. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/nadam.py +0 -0
  82. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/nadamw.py +0 -0
  83. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/noisy_sgd.py +0 -0
  84. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/novograd.py +0 -0
  85. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/oadam.py +0 -0
  86. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/ogd.py +0 -0
  87. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/polyak_sgd.py +0 -0
  88. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/radam.py +0 -0
  89. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/rmsprop.py +0 -0
  90. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/rprop.py +0 -0
  91. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/sam.py +0 -0
  92. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/schedule_free_adam.py +0 -0
  93. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/sgd.py +0 -0
  94. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/sign.py +0 -0
  95. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/sm3.py +0 -0
  96. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/sophia.py +0 -0
  97. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax/yogi.py +0 -0
  98. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/optax_lbfgs.py +0 -0
  99. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/sa_gd.py +0 -0
  100. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/scipy/__init__.py +0 -0
  101. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/scipy/_common.py +0 -0
  102. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/scipy/bfgs.py +0 -0
  103. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/scipy/cobyla.py +0 -0
  104. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/scipy/cobyqa.py +0 -0
  105. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/scipy/dogleg.py +0 -0
  106. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/scipy/lbfgsb.py +0 -0
  107. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/scipy/newton_cg.py +0 -0
  108. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/scipy/nonlinear_cg.py +0 -0
  109. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/scipy/slsqp.py +0 -0
  110. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/scipy/sr1.py +0 -0
  111. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/scipy/tnc.py +0 -0
  112. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/scipy/trust_constr.py +0 -0
  113. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/scipy/trust_krylov.py +0 -0
  114. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/gradient_based/scipy/trust_ncg.py +0 -0
  115. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/surrogate_based/__init__.py +0 -0
  116. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/surrogate_based/ax_baxus.py +0 -0
  117. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/surrogate_based/ax_saasbo.py +0 -0
  118. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/surrogate_based/botorch/__init__.py +0 -0
  119. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/surrogate_based/botorch/_botorch_common.py +0 -0
  120. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/surrogate_based/botorch/botorch_bo.py +0 -0
  121. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/surrogate_based/botorch/botorch_gebo.py +0 -0
  122. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/surrogate_based/botorch/botorch_linebo.py +0 -0
  123. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/surrogate_based/botorch/botorch_qkg.py +0 -0
  124. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/surrogate_based/botorch/botorch_qnei.py +0 -0
  125. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/surrogate_based/botorch/botorch_rembo.py +0 -0
  126. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/surrogate_based/botorch/botorch_turbo.py +0 -0
  127. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/surrogate_based/hebo_bo.py +0 -0
  128. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/surrogate_based/restir.py +0 -0
  129. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/surrogate_based/smac_bo.py +0 -0
  130. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/surrogate_based/turbo_lbfgs.py +0 -0
  131. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/utils/__init__.py +0 -0
  132. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/utils/gp.py +0 -0
  133. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/utils/initial_design.py +0 -0
  134. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/utils/misc.py +0 -0
  135. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/algorithms/utils/weighted_acq.py +0 -0
  136. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/benchmark/__init__.py +0 -0
  137. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/benchmark/benchmark.py +0 -0
  138. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/benchmark/metrics.py +0 -0
  139. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/core/_init_env.py +0 -0
  140. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/core/algorithm.py +0 -0
  141. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/core/config.py +0 -0
  142. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/core/display.py +0 -0
  143. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/core/problem.py +0 -0
  144. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/core/storage/__init__.py +0 -0
  145. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/core/storage/backends.py +0 -0
  146. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/core/storage/exporter.py +0 -0
  147. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/core/storage/manager.py +0 -0
  148. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/core/storage/resolver.py +0 -0
  149. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/core/storage/serializers.py +0 -0
  150. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/core/utils.py +0 -0
  151. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/problems/__init__.py +0 -0
  152. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/problems/uifo/__init__.py +0 -0
  153. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/problems/uifo/uifo_problem.py +0 -0
  154. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/problems/voyager/__init__.py +0 -0
  155. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/problems/voyager/voyager_problem.py +0 -0
  156. {dfbench-0.3.1 → dfbench-0.3.2}/src/dfbench/problems/voyager/voyager_tuning_problem.py +0 -0
  157. {dfbench-0.3.1 → dfbench-0.3.2}/tests/__init__.py +0 -0
  158. {dfbench-0.3.1 → dfbench-0.3.2}/tests/conftest.py +0 -0
  159. {dfbench-0.3.1 → dfbench-0.3.2}/tests/slow/__init__.py +0 -0
  160. {dfbench-0.3.1 → dfbench-0.3.2}/tests/slow/test_algorithms_integration.py +0 -0
  161. {dfbench-0.3.1 → dfbench-0.3.2}/tests/slow/test_benchmark_full.py +0 -0
  162. {dfbench-0.3.1 → dfbench-0.3.2}/tests/slow/test_problem_bounds_gradients.py +0 -0
  163. {dfbench-0.3.1 → dfbench-0.3.2}/tests/slow/test_problems_full.py +0 -0
  164. {dfbench-0.3.1 → dfbench-0.3.2}/tests/test_algorithm_protocol.py +0 -0
  165. {dfbench-0.3.1 → dfbench-0.3.2}/tests/test_algorithms_uniform.py +0 -0
  166. {dfbench-0.3.1 → dfbench-0.3.2}/tests/test_algorithms_unit.py +0 -0
  167. {dfbench-0.3.1 → dfbench-0.3.2}/tests/test_api_imports.py +0 -0
  168. {dfbench-0.3.1 → dfbench-0.3.2}/tests/test_benchmark_smoke.py +0 -0
  169. {dfbench-0.3.1 → dfbench-0.3.2}/tests/test_bo_batch.py +0 -0
  170. {dfbench-0.3.1 → dfbench-0.3.2}/tests/test_cma_family.py +0 -0
  171. {dfbench-0.3.1 → dfbench-0.3.2}/tests/test_config.py +0 -0
  172. {dfbench-0.3.1 → dfbench-0.3.2}/tests/test_custom_jax_batch.py +0 -0
  173. {dfbench-0.3.1 → dfbench-0.3.2}/tests/test_dfo_algorithms.py +0 -0
  174. {dfbench-0.3.1 → dfbench-0.3.2}/tests/test_display.py +0 -0
  175. {dfbench-0.3.1 → dfbench-0.3.2}/tests/test_hebo_packaging.py +0 -0
  176. {dfbench-0.3.1 → dfbench-0.3.2}/tests/test_init_env.py +0 -0
  177. {dfbench-0.3.1 → dfbench-0.3.2}/tests/test_metrics.py +0 -0
  178. {dfbench-0.3.1 → dfbench-0.3.2}/tests/test_objective_randomness.py +0 -0
  179. {dfbench-0.3.1 → dfbench-0.3.2}/tests/test_objective_space_mode.py +0 -0
  180. {dfbench-0.3.1 → dfbench-0.3.2}/tests/test_problem_bounds_overrides.py +0 -0
  181. {dfbench-0.3.1 → dfbench-0.3.2}/tests/test_problem_protocol.py +0 -0
  182. {dfbench-0.3.1 → dfbench-0.3.2}/tests/test_problem_spec.py +0 -0
  183. {dfbench-0.3.1 → dfbench-0.3.2}/tests/test_scipy_wrapper.py +0 -0
  184. {dfbench-0.3.1 → dfbench-0.3.2}/tests/test_storage.py +0 -0
  185. {dfbench-0.3.1 → dfbench-0.3.2}/tests/test_utils.py +0 -0
@@ -23,11 +23,7 @@ temp/
23
23
  results/
24
24
  notebooks/
25
25
  .DS_Store
26
-
27
- runs/
28
- competition/
29
- thesis.typ
30
- training/
26
+ .github/
31
27
 
32
28
  # IDE related
33
29
  .vscode/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dfbench
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: dfbench: A Black-Box Optimization Framework for JAX-Differentiable Gravitational Wave Detector Discovery
5
5
  Project-URL: Homepage, https://github.com/artificial-scientist-lab/Differometor-Benchmark
6
6
  Project-URL: Repository, https://github.com/artificial-scientist-lab/Differometor-Benchmark
@@ -177,7 +177,7 @@ This adds negligible overhead compared to the objective function itself.
177
177
 
178
178
  Both constrained problems accept a `power_penalty_fn(value, threshold)` callable to control how power-constraint violations are penalized. Built-in presets: `squashed_relu_penalty` (default), `relu_penalty`, `zero_penalty`. Feel free to try own ones. The penalty function can also be swapped after construction via `obj.set_penalty_fn(fn)` (before `obj.start_logging()`), which re-traces the objective so the change takes effect. The unconstrained Voyager problems reject this call because they have no power-constraint path.
179
179
 
180
- Constrained problems also expose aux diagnostics. `obj.value_aux(params)` and `obj.vmap_value_aux(batch)` return `(loss, aux)` where `aux` carries the loss decomposition, a physical `is_feasible` flag, per-constraint violations, and the raw per-group power arrays. Enable `save` tokens like `is_feasible`, `power_values`, or the `aux` alias and the standard `obj.value` / `obj.value_and_grad` loop records the enabled aux fields in the same forward pass, with no code change. See [Problems](https://github.com/artificial-scientist-lab/Differometor-Benchmark/wiki/Problems) for the aux schema and [Objective API Reference](https://github.com/artificial-scientist-lab/Differometor-Benchmark/wiki/Objective-API-Reference) for the full method set.
180
+ Constrained problems also expose aux diagnostics. `obj.value_aux(params)` and `obj.vmap_value_aux(batch)` return `(loss, aux)` where `aux` carries the loss decomposition, a physical `is_feasible` flag, per-constraint violations, and the raw per-group power arrays. Enable `save` tokens like `is_feasible`, `power_values`, or the `aux` alias and standard value-bearing calls such as `obj.value` / `obj.value_and_grad` record the enabled aux fields in the same forward pass, with no code change. Derivative-only `grad` / `hessian` calls do not produce aux and append aligned `None` entries when aux histories are enabled. See [Problems](https://github.com/artificial-scientist-lab/Differometor-Benchmark/wiki/Problems) for the aux schema and [Objective API Reference](https://github.com/artificial-scientist-lab/Differometor-Benchmark/wiki/Objective-API-Reference) for the full method set.
181
181
 
182
182
  All problems also support `bounds_overrides` (e.g. `{"tuning": (0, 45)}`) to narrow default property bounds and `signal_floor` to floor detector signal magnitudes before sensitivity normalization. `signal_floor` defaults to `1e-20`. Use `problem.print_bounds()` to inspect effective bounds.
183
183
 
@@ -473,7 +473,8 @@ class MyAlgorithm(OptimizationAlgorithm):
473
473
  | `obj.vmap_value_and_grad(batch)` | Batched gradient methods | batch losses, grads, params |
474
474
  | `obj.vmap_hessian(batch)` | Batched second-order methods | batch hessians, batch params |
475
475
  | `obj.vmap_value_grad_and_hessian(batch)` | Batched second-order methods | batch losses, grads, hessians, params |
476
- | `obj.log_evaluation(...)` | Custom JIT'd loop | whatever you pass, including optional Hessians |
476
+ | `obj.value_function_aux(...)` | Raw aux-aware callable for custom JIT loops | nothing; use `log_evaluation` afterwards |
477
+ | `obj.log_evaluation(...)` | Custom JIT'd loop | supplied values; aux only for matching save tokens |
477
478
 
478
479
  ### Register It
479
480
 
@@ -87,7 +87,7 @@ This adds negligible overhead compared to the objective function itself.
87
87
 
88
88
  Both constrained problems accept a `power_penalty_fn(value, threshold)` callable to control how power-constraint violations are penalized. Built-in presets: `squashed_relu_penalty` (default), `relu_penalty`, `zero_penalty`. Feel free to try own ones. The penalty function can also be swapped after construction via `obj.set_penalty_fn(fn)` (before `obj.start_logging()`), which re-traces the objective so the change takes effect. The unconstrained Voyager problems reject this call because they have no power-constraint path.
89
89
 
90
- Constrained problems also expose aux diagnostics. `obj.value_aux(params)` and `obj.vmap_value_aux(batch)` return `(loss, aux)` where `aux` carries the loss decomposition, a physical `is_feasible` flag, per-constraint violations, and the raw per-group power arrays. Enable `save` tokens like `is_feasible`, `power_values`, or the `aux` alias and the standard `obj.value` / `obj.value_and_grad` loop records the enabled aux fields in the same forward pass, with no code change. See [Problems](https://github.com/artificial-scientist-lab/Differometor-Benchmark/wiki/Problems) for the aux schema and [Objective API Reference](https://github.com/artificial-scientist-lab/Differometor-Benchmark/wiki/Objective-API-Reference) for the full method set.
90
+ Constrained problems also expose aux diagnostics. `obj.value_aux(params)` and `obj.vmap_value_aux(batch)` return `(loss, aux)` where `aux` carries the loss decomposition, a physical `is_feasible` flag, per-constraint violations, and the raw per-group power arrays. Enable `save` tokens like `is_feasible`, `power_values`, or the `aux` alias and standard value-bearing calls such as `obj.value` / `obj.value_and_grad` record the enabled aux fields in the same forward pass, with no code change. Derivative-only `grad` / `hessian` calls do not produce aux and append aligned `None` entries when aux histories are enabled. See [Problems](https://github.com/artificial-scientist-lab/Differometor-Benchmark/wiki/Problems) for the aux schema and [Objective API Reference](https://github.com/artificial-scientist-lab/Differometor-Benchmark/wiki/Objective-API-Reference) for the full method set.
91
91
 
92
92
  All problems also support `bounds_overrides` (e.g. `{"tuning": (0, 45)}`) to narrow default property bounds and `signal_floor` to floor detector signal magnitudes before sensitivity normalization. `signal_floor` defaults to `1e-20`. Use `problem.print_bounds()` to inspect effective bounds.
93
93
 
@@ -383,7 +383,8 @@ class MyAlgorithm(OptimizationAlgorithm):
383
383
  | `obj.vmap_value_and_grad(batch)` | Batched gradient methods | batch losses, grads, params |
384
384
  | `obj.vmap_hessian(batch)` | Batched second-order methods | batch hessians, batch params |
385
385
  | `obj.vmap_value_grad_and_hessian(batch)` | Batched second-order methods | batch losses, grads, hessians, params |
386
- | `obj.log_evaluation(...)` | Custom JIT'd loop | whatever you pass, including optional Hessians |
386
+ | `obj.value_function_aux(...)` | Raw aux-aware callable for custom JIT loops | nothing; use `log_evaluation` afterwards |
387
+ | `obj.log_evaluation(...)` | Custom JIT'd loop | supplied values; aux only for matching save tokens |
387
388
 
388
389
  ### Register It
389
390
 
@@ -67,7 +67,7 @@ A problem defines *what* is being optimised. Every problem subclasses `Continuou
67
67
 
68
68
  - Maps unbounded coordinates into problem bounds when needed, then evaluates the bounded problem objective
69
69
  - Prepares `jax.grad`, `jax.hessian`, `jax.value_and_grad`, and `jax.vmap` variants
70
- - Records every evaluation with aligned loss / gradient / Hessian / params / timestamp histories
70
+ - Records every admitted call with aligned loss / gradient / Hessian / params / timestamp histories
71
71
  - Enforces wall-clock time and evaluation-count budgets
72
72
  - Provides deterministic random sampling via a splittable JAX PRNG
73
73
  - Delegates all file I/O to the modular `dfbench.core.storage` layer (see [Storage & Checkpointing](Storage-and-Checkpointing))
@@ -114,39 +114,45 @@ See [Storage & Checkpointing](Storage-and-Checkpointing) for the full reference.
114
114
  │ │ │ │
115
115
  └────────────┴───────┬───────┴──────────────────────┘
116
116
 
117
- Objective._func / _value_and_grad_func / _hessian_func / _vmap_func
117
+ Objective._value_func / derivative transforms / vmapped transforms
118
118
 
119
119
 
120
120
  ┌──────────────────────────────┐
121
121
  │ optional Objective mapping │
122
- problem.objective_function
122
+ │ objective_function or
123
+ │ objective_function_aux │
123
124
  └──────────────┬───────────────┘
124
125
 
125
126
 
126
127
  Differometor.simulate()
127
128
 
128
129
 
129
- scalar loss value
130
+ value / derivatives + optional aux
130
131
 
132
+
133
+ _log(params, loss, grad, hessian, aux)
134
+
135
+
136
+ _log_evals(..., aux)
131
137
 
138
+ ┌───────────┴────────────┐
139
+ │ admitted │ rejected
140
+ ▼ ▼
141
+ time + standard histories no history mutation
142
+ + best/stagnation + selected aux histories
143
+ │ │
144
+ └───────────┬────────────┘
132
145
 
133
- _log(params, loss, grad, hessian)
146
+ _log_to_file()
134
147
 
135
- ┌───────────┼───────────────────┐
136
- │ │ │
137
- time_steps _log_evals() _log_to_file()
138
- (append) │ │
139
- ▼ ▼ ▼
140
- _time_steps _loss_history periodic
141
- _params_history checkpoint
142
- _grad_history via CheckpointManager
143
- _hessian_history -> StorageBackend
144
- _best_loss / _best_params
148
+
149
+ periodic checkpoint via
150
+ CheckpointManager -> StorageBackend
145
151
  ```
146
152
 
147
- Every call to `obj.value()`, `obj.value_and_grad()`, `obj.hessian()`, `obj.value_grad_and_hessian()`, or any `vmap_*` variant follows this exact pipeline. The internal `_log()` coordinator handles time-step recording, delegates to `_log_evals()` for history tracking, and triggers `_log_to_file()` for periodic checkpoints. `_log_to_file()` calls `CheckpointManager.tick()`, which checks the cadence (`save_every`), lazily builds a `RunState` only when a checkpoint is due, saves it through the internal `StorageBackend`, and returns the save duration so the Objective can exclude it from the elapsed-time clock. The algorithm receives the computed result; the logging is a side-effect invisible to the caller.
153
+ Every call to `obj.value()`, `obj.value_and_grad()`, `obj.hessian()`, `obj.value_grad_and_hessian()`, or any `vmap_*` variant follows this pipeline. Value-bearing methods carry one primal aux pytree when aux storage is selected; derivative-only methods carry no aux and contribute `None` to selected aux histories. The internal `_log()` coordinator takes one time snapshot, delegates atomic admission and history tracking to `_log_evals()`, appends the timestamp only for an admitted call, and triggers `_log_to_file()` for periodic checkpoints. `_log_to_file()` calls `CheckpointManager.tick()`, which checks the cadence (`save_every`), lazily builds a `RunState` only when a checkpoint is due, saves it through the internal `StorageBackend`, and returns the save duration so the Objective can exclude it from the elapsed-time clock. The algorithm receives the computed result; logging is a side effect invisible to the caller.
148
154
 
149
- For algorithms with custom JIT-compiled evaluation loops (e.g. L-BFGS with line-search), `obj.value_function(...)` provides the same Objective-owned bounded/unbounded mapping without Python-side logging, and `obj.log_evaluation(params, loss, grad, hessian=None)` records the completed evaluation through the same logging pipeline. Do not call the private methods directly.
155
+ For algorithms with custom JIT-compiled evaluation loops (e.g. L-BFGS with line-search), `obj.value_function(...)` and `obj.value_function_aux(...)` provide the same Objective-owned bounded/unbounded mapping without Python-side logging. The latter returns an aux-aware callable whose invocation yields `(loss, aux)`, or `None` when unsupported. After logging has started, `obj.log_evaluation(params, loss, grad=None, hessian=None, aux=None)` records the completed evaluation through the same logging pipeline. Manual logging never manufactures aux; omitting it appends `None` to each enabled aux history. Do not call the private methods directly.
150
156
 
151
157
  ---
152
158
 
@@ -271,19 +271,38 @@ while not obj.budget_exceeded:
271
271
  | `obj.vmap_hessian(batch)` | Batched second-order optimization | batch hessians, batch params |
272
272
  | `obj.vmap_value_grad_and_hessian(batch)` | Batched second-order optimization | batch losses, grads, hessians, params |
273
273
  | `obj.value_function(...)` | Raw JAX callable for custom JIT loops | nothing; use `log_evaluation` afterwards |
274
- | `obj.log_evaluation(...)` | Custom JIT'd loop | whatever you pass, including optional Hessians |
274
+ | `obj.value_function_aux(...)` | Raw `(loss, aux)` JAX callable for custom JIT loops | nothing; use `log_evaluation` afterwards |
275
+ | `obj.log_evaluation(...)` | Custom JIT'd loop | supplied values; aux only for matching save tokens |
275
276
 
276
- **Important:** `obj.grad()` and `obj.hessian()` do **not** log a loss value. If you need the loss too, use `obj.value_and_grad()` or `obj.value_grad_and_hessian()`.
277
+ **Important:** `obj.grad()` and `obj.hessian()` do **not** log a loss value or produce aux diagnostics. If aux histories are enabled, derivative-only methods append aligned `None` entries. With an aux save token, `obj.value_and_grad()` and `obj.value_grad_and_hessian()` auto-log primal aux while retaining their standard return signatures. Use `obj.value_and_grad_aux()` when the algorithm itself must receive aux.
277
278
 
278
279
  ### Custom JIT-compiled loops with `log_evaluation()`
279
280
 
280
281
  Some optimizers (e.g. Optax's L-BFGS) need to call `value_and_grad` *inside* a JIT-compiled function, for instance because the optimizer's line-search requires the raw value function. In that case you can't use `obj.value_and_grad()` (which has Python-side logging). Instead:
281
282
 
282
- 1. Get an unlogged raw value function from `obj.value_function(...)`
283
+ 1. Get an unlogged scalar callable from `obj.value_function(...)`, or an aux-aware callable from `obj.value_function_aux(...)` whose invocation yields `(loss, aux)`
283
284
  2. Build your own JIT-compiled step
284
- 3. After each step, call `obj.log_evaluation(params, loss, grad, hessian=None)` to record the results
285
+ 3. After each step, call `obj.log_evaluation(params, loss, grad, hessian=None, aux=None)` to record the results
285
286
 
286
- `obj.value_function(unbounded=None)` follows the Objective's active space mode by default. Pass `unbounded=True` when the JIT loop works in unbounded coordinates and needs Objective's mapping into problem bounds; pass `unbounded=False` for bounded coordinates. The callable deliberately does not log anything.
287
+ `obj.value_function(unbounded=None)` follows the Objective's active space mode by default. Pass `unbounded=True` when the JIT loop works in unbounded coordinates and needs Objective's mapping into problem bounds; pass `unbounded=False` for bounded coordinates. `obj.value_function_aux(...)` follows the same mapping rules and returns an unlogged callable whose invocation yields `(loss, aux)`, or `None` when the problem has no aux objective. Neither callable logs anything.
288
+
289
+ `log_evaluation()` never computes missing aux diagnostics. If the Objective has aux save tokens and the custom loop does not pass `aux`, each selected aux history receives `None` for that call. A custom loop that already computed a conforming aux pytree may pass it explicitly.
290
+
291
+ To compute mapped aux inside the custom loop, preserve it as auxiliary output during differentiation:
292
+
293
+ ```python
294
+ value_aux_fn = obj.value_function_aux()
295
+ if value_aux_fn is None:
296
+ raise RuntimeError("this problem has no aux objective")
297
+ value_and_grad_aux_fn = jax.jit(jax.value_and_grad(value_aux_fn, has_aux=True))
298
+
299
+ _ = value_and_grad_aux_fn(params) # JIT warmup before timing
300
+ obj.start_logging()
301
+ (loss, aux), grads = value_and_grad_aux_fn(params)
302
+ obj.log_evaluation(params, loss, grads, aux=aux)
303
+ ```
304
+
305
+ Only aux fields selected in the Objective's `save` configuration are persisted.
287
306
 
288
307
  ```python
289
308
  # Get the raw function for JIT compilation
@@ -48,7 +48,7 @@ Objective(
48
48
  | `save_time_steps` | `bool` | `True` | Record elapsed-time timestamp for each evaluation. |
49
49
  | `save_params_history` | `bool` | `True` | Record the parameter vector at each evaluation. |
50
50
  | `save_batched_params_history` | `bool` | `False` | Store full `(batch, n_params)` parameter arrays for batched evals instead of the reduced representative point. |
51
- | `save` | `list[str] \| None` | `None` | List of advanced save tokens for recording additional / batched histories. Standard tokens: `"grad"`, `"hessian"`, `"eval_type"`, `"batched_loss"`, `"batched_grad"`, `"batched_hessian"`, `"batched"` (convenience alias expanding to the three batched tokens above). Aux diagnostics tokens (recorded by the `*_aux` evaluation methods on problems that opt into the power-penalty contract): `"sensitivity_loss"`, `"penalty"`, `"is_feasible"`, `"power_values"`, `"violations"`, `"aux"` (convenience alias expanding to all five), plus per-field `batched_*` variants and `"batched_aux"` (see Choosing what to save). The active configuration is recorded as a `SaveConfig` and embedded in every checkpoint so a resumed run can detect mismatches. |
51
+ | `save` | `list[str] \| None` | `None` | List of advanced save tokens for recording additional / batched histories. Standard tokens: `"grad"`, `"hessian"`, `"eval_type"`, `"batched_loss"`, `"batched_grad"`, `"batched_hessian"`, `"batched"` (convenience alias expanding to the three batched tokens above). Aux diagnostics tokens: `"sensitivity_loss"`, `"penalty"`, `"is_feasible"`, `"power_values"`, `"violations"`, `"aux"` (convenience alias expanding to all five), plus per-field `batched_*` variants and `"batched_aux"`. On a problem exposing `objective_function_aux`, these tokens persist diagnostics from explicit `*_aux` methods and standard value-bearing methods. Derivative-only calls append aligned `None` entries. On a problem without `objective_function_aux`, aux tokens emit `RuntimeWarning` and automatic aux logging remains disabled; explicit aux methods still raise `RuntimeError`. The active configuration is recorded as a `SaveConfig` and restored from checkpoints so a resumed run keeps the original history schema. |
52
52
  | `verbose` | `int` | `0` | Verbosity level. `0` = silent; `1` = periodic progress prints; `2` is WIP. |
53
53
  | `print_every` | `int` | `100` | When `verbose ≥ 1`, print a progress summary every N evaluations. |
54
54
  | `algorithm_str` | `str \| None` | `None` | If `None`, this is set by the algorithm via `prepare()` of `OptimizationAlgorithm`. Optional identifier string used in file names and logs. |
@@ -127,11 +127,11 @@ You do **not** need to handle bounds scaling; the Objective does that automatica
127
127
 
128
128
  ### Choosing what to save
129
129
 
130
- The Objective always records losses. Three standard boolean flags control the most commonly toggled histories:
130
+ The Objective always maintains an aligned `loss_history`: value-bearing calls store their loss, while derivative-only calls store a NaN placeholder because they do not return a primal loss. Three standard boolean flags control the most commonly toggled histories:
131
131
 
132
132
  | Flag | Default | Effect |
133
133
  |------|---------|--------|
134
- | `save_time_steps` | `True` | Record elapsed-time timestamps per evaluation |
134
+ | `save_time_steps` | `True` | Record one elapsed-time timestamp per admitted logged call |
135
135
  | `save_params_history` | `True` | Record parameter vectors (reduced for batches) |
136
136
  | `save_batched_params_history` | `False` | Store full `(batch, n_params)` parameter arrays instead of the reduced representative point |
137
137
 
@@ -139,23 +139,23 @@ For advanced combinations (gradients, Hessians, eval types, full batched arrays)
139
139
 
140
140
  | Token | Effect |
141
141
  |-------|--------|
142
- | `"grad"` | Record gradient history (reduced to one entry per eval for batches) |
143
- | `"hessian"` | Record Hessian history (reduced to one entry per eval for batches) |
144
- | `"eval_type"` | Record per-eval type bitmask history |
142
+ | `"grad"` | Record gradients (one entry per logged call; batches reduced) |
143
+ | `"hessian"` | Record Hessians (one entry per logged call; batches reduced) |
144
+ | `"eval_type"` | Record one call-type bitmask per logged call |
145
145
  | `"batched_loss"` | Store full `(batch,)` loss vectors instead of batch min |
146
146
  | `"batched_grad"` | Store full `(batch, n_params)` gradient arrays |
147
147
  | `"batched_hessian"` | Store full `(batch, n_params, n_params)` Hessian arrays |
148
148
  | `"batched"` | Convenience alias expanding to the three `batched_*` tokens above |
149
149
 
150
- Aux diagnostics tokens are recorded by the `*_aux` evaluation methods, and also by the standard loss-bearing methods (`value`, `value_and_grad`, `vmap_value`, `vmap_value_and_grad`, `value_grad_and_hessian`, `vmap_value_grad_and_hessian`) when auto-logging is active. Auto-logging turns on when at least one aux token is in the `save` list and the problem opts into the power-penalty contract (`ConstrainedVoyagerProblem`, `UIFOProblem`); the standard methods then run the aux objective in the same forward pass and populate the aux histories without changing their return signatures (see [Auto-logging aux](#auto-logging-aux-from-the-standard-methods)). Each token controls one aux field, so enabling `is_feasible` does not force storing the bulky `power_values` arrays.
150
+ Aux diagnostics tokens are recorded by the explicit `*_aux` evaluation methods and by the standard value-bearing methods (`value`, `value_and_grad`, `vmap_value`, `vmap_value_and_grad`, `value_grad_and_hessian`, `vmap_value_grad_and_hessian`). Auto-logging turns on when at least one aux token is in the `save` list and the problem exposes `objective_function_aux` (the built-in examples are `ConstrainedVoyagerProblem` and `UIFOProblem`). The standard methods then run the aux objective in the same forward pass and populate the aux histories without changing their return signatures (see [Auto-logging aux](#auto-logging-aux-from-standard-value-bearing-methods)). Each token controls one aux field, so enabling `is_feasible` does not force storing the bulky `power_values` arrays. On a problem without `objective_function_aux`, construction emits `RuntimeWarning`, standard methods continue through the scalar objective, and aux histories remain empty.
151
151
 
152
152
  | Token | Effect |
153
153
  |-------|--------|
154
- | `"sensitivity_loss"` | Record the unpenalised sensitivity loss per aux eval (reduced for batches) |
155
- | `"penalty"` | Record the summed penalty per aux eval (reduced for batches) |
156
- | `"is_feasible"` | Record the physical feasibility flag per aux eval (reduced for batches) |
157
- | `"power_values"` | Record per-group powers (hard, soft, detector) per aux eval (reduced for batches) |
158
- | `"violations"` | Record per-constraint penalty values per aux eval (reduced for batches) |
154
+ | `"sensitivity_loss"` | Record the unpenalised sensitivity loss per logged call (reduced for batches; `None` when no aux is produced) |
155
+ | `"penalty"` | Record the summed penalty per logged call (reduced for batches; `None` when no aux is produced) |
156
+ | `"is_feasible"` | Record the physical feasibility flag per logged call (reduced for batches; `None` when no aux is produced) |
157
+ | `"power_values"` | Record per-group powers (hard, soft, detector) per logged call (reduced for batches; `None` when no aux is produced) |
158
+ | `"violations"` | Record per-constraint penalty values per logged call (reduced for batches; `None` when no aux is produced) |
159
159
  | `"aux"` | Convenience alias expanding to the five non-batched aux tokens above |
160
160
  | `"batched_sensitivity_loss"` | Store full batched sensitivity loss arrays |
161
161
  | `"batched_penalty"` | Store full batched penalty arrays |
@@ -164,7 +164,7 @@ Aux diagnostics tokens are recorded by the `*_aux` evaluation methods, and also
164
164
  | `"batched_violations"` | Store full batched per-constraint violation arrays |
165
165
  | `"batched_aux"` | Convenience alias expanding to the five `batched_*` aux tokens above |
166
166
 
167
- When a `batched_*` aux token is off and the corresponding non-batched token is on, batched aux entries are reduced to the representative point (the index of the best loss within the batch), so the recorded `is_feasible` and `violations` reflect that best point. This matches the reduction rule used for gradients and Hessians.
167
+ When a `batched_*` aux token is off and the corresponding non-batched token is on, aux from every batched value-bearing call—including a singleton batch—is reduced to the representative point. Value-bearing calls select the index of the best loss, so the recorded `is_feasible` and `violations` reflect that best point. This matches the representative used for parameters, gradients, and Hessians.
168
168
 
169
169
  ```python
170
170
  # Record gradients and full batched losses
@@ -178,7 +178,7 @@ obj = Objective(problem, save=["grad", "hessian", "eval_type", "batched"])
178
178
  obj = Objective(problem, save=["aux", "batched_is_feasible"])
179
179
  ```
180
180
 
181
- The active configuration is stored as a `SaveConfig` and embedded in every checkpoint's `RunMetadata`. On `load_run_data`, the Objective warns if the checkpoint's save config differs from the current Objective's, preventing silent inconsistency.
181
+ The active configuration is stored as a `SaveConfig` and embedded in every checkpoint's `RunMetadata`. On `load_run_data`, the Objective warns if the checkpoint config differs and adopts the stored config before restoring state, so resumed evaluations keep the run's original history schema.
182
182
 
183
183
  ### Storage (internal)
184
184
 
@@ -203,7 +203,7 @@ obj.value_grad_and_hessian(params) # -> (float, Array[n_params], Array[n_params
203
203
  ```
204
204
 
205
205
  - `value(params)`: Evaluates the loss at `params`. Logs loss and params.
206
- - `grad(params)`: Computes the gradient. Logs grad and params, but **not** a loss value (the loss is not computed).
206
+ - `grad(params)`: Computes the gradient. The scalar primal is evaluated as part of differentiation, but its value is not returned or logged; `loss_history` receives a NaN placeholder.
207
207
  - `hessian(params)`: Computes the exact Hessian. Logs Hessian and params, but **not** a loss value.
208
208
  - `value_and_grad(params)`: Computes both in a single forward+backward pass. Logs all three. **Preferred when you need both loss and gradient** because it is more efficient than calling `value` and `grad` separately and it logs the loss.
209
209
  - `value_grad_and_hessian(params)`: Computes loss, gradient, and Hessian together and logs all four.
@@ -233,7 +233,7 @@ Batched methods use `jax.vmap` and evaluate the entire batch as **one** history
233
233
 
234
234
  ### Aux evaluation
235
235
 
236
- Available on problems that opt into the power-penalty contract (`ConstrainedVoyagerProblem`, `UIFOProblem`). These methods return the loss plus an `aux` pytree dict carrying the loss decomposition, a physical `is_feasible` flag, per-constraint violations, and the raw per-group power arrays. See the [Power thresholds and aux diagnostics](#power-thresholds-and-aux-diagnostics) section for the aux schema.
236
+ Available on problems that expose `objective_function_aux` (`ConstrainedVoyagerProblem` and `UIFOProblem` are the built-in examples). These methods return the loss plus an `aux` pytree dict carrying the loss decomposition, a physical `is_feasible` flag, per-constraint violations, and the raw per-group power arrays. See the [Power thresholds and aux diagnostics](#power-thresholds-and-aux-diagnostics) section for the aux schema.
237
237
 
238
238
  ```python
239
239
  obj.value_aux(params) # -> (float, dict)
@@ -243,21 +243,21 @@ obj.vmap_value_and_grad_aux(params_batch) # -> (Array[batch], Array[batch, n_par
243
243
  ```
244
244
 
245
245
  - `value_aux(params)` returns `(loss, aux)`. The loss is logged into the standard loss history; aux fields are recorded into the per-field aux histories only when the matching save token is enabled.
246
- - `value_and_grad_aux(params)` computes loss, gradient, and aux in one forward+backward pass via `jax.value_and_grad(..., has_aux=True)`. The gradient is taken with respect to the loss (the penalty preset does not affect the gradient because the powers are intermediate simulation outputs, not parameters).
246
+ - `value_and_grad_aux(params)` computes loss, gradient, and aux in one forward+backward pass via `jax.value_and_grad(..., has_aux=True)`. The gradient is taken with respect to the total returned loss, including any parameter-dependent penalty. `has_aux=True` carries the aux pytree through unchanged; derivatives of aux are neither returned nor logged.
247
247
  - `vmap_value_aux` and `vmap_value_and_grad_aux` are the batched variants. Because `aux` is a JAX pytree, a batched call adds a leading batch dim to every leaf, including the `power_values` sub-arrays.
248
- - The aux methods raise `RuntimeError` on problems that do not expose `objective_function_aux` (for example `VoyagerProblem`, `VoyagerTuningProblem`, or any non-optical `ContinuousProblem`).
248
+ - The explicit aux methods raise `RuntimeError` when the wrapped problem does not expose `objective_function_aux` (for example `VoyagerProblem` and `VoyagerTuningProblem`). A custom `ContinuousProblem` may opt in by implementing the same aux objective contract.
249
249
 
250
250
  Warmup helpers `warmup_value_aux`, `warmup_value_and_grad_aux`, `warmup_vmap_value_aux`, and `warmup_vmap_value_and_grad_aux` compile the aux callables before `start_logging()`. On problems without an aux objective they are a no-op (with a notice at `verbose >= 1`) rather than raising, so an algorithm that unconditionally warms up aux does not break on a non-constrained problem.
251
251
 
252
- ### Auto-logging aux from the standard methods
252
+ ### Auto-logging aux from standard value-bearing methods
253
253
 
254
- When one or more aux save tokens are enabled and the problem exposes `objective_function_aux`, the standard loss-bearing methods (`value`, `value_and_grad`, `vmap_value`, `vmap_value_and_grad`, `value_grad_and_hessian`, `vmap_value_grad_and_hessian`) run the aux objective in the same forward pass and record the enabled aux diagnostics. No code change is needed in the optimization loop: a plain `obj.value(params)` call populates `is_feasible_history`, `sensitivity_loss_history`, and so on, in addition to `loss_history`.
254
+ When one or more aux save tokens are enabled and the problem exposes `objective_function_aux`, the standard value-bearing methods (`value`, `value_and_grad`, `vmap_value`, `vmap_value_and_grad`, `value_grad_and_hessian`, `vmap_value_grad_and_hessian`) run the aux objective in the same forward pass and record the enabled aux diagnostics. No code change is needed in the optimization loop: a plain `obj.value(params)` call populates `is_feasible_history`, `sensitivity_loss_history`, and so on, in addition to `loss_history`.
255
255
 
256
- This rebinds the internal loss-bearing callables to the aux variants at bind time, so the aux pytree comes out of the same simulation that produced the loss. There is no second forward pass. The returned values keep their usual shapes (`value` still returns a scalar, `value_and_grad` still returns `(loss, grad)`); the aux pytree is stashed internally and fed to `_log_aux` without changing the public signatures.
256
+ This rebinds the internal value-bearing callables to the aux variants at bind time, so the aux pytree comes out of the same simulation that produced the loss. There is no second forward pass. The returned values keep their usual shapes (`value` still returns a scalar, `value_and_grad` still returns `(loss, grad)`); internally, exactly one aux pytree travels alongside the value result and is passed directly to `_log(..., aux=aux)`.
257
257
 
258
- Grad-only and Hessian-only calls (`grad`, `hessian`, `vmap_grad`, `vmap_hessian`) do not compute a loss, so they have no aux to record. They append `None` placeholders to the enabled aux histories so the aux histories stay length-aligned with `loss_history`. `best_is_feasible` and the aux history properties treat `None` entries as missing.
258
+ Grad-only and Hessian-only calls (`grad`, `hessian`, `vmap_grad`, `vmap_hessian`) differentiate the scalar objective but do not return or carry a primal loss/aux result. They therefore append `None` placeholders to enabled aux histories so those histories stay length-aligned with `loss_history`. The history properties preserve these `None` entries; `best_is_feasible` interprets one at the best-loss index as unavailable.
259
259
 
260
- Auto-logging is active only when both conditions hold: at least one aux token is in the `save` list, and the problem opts into the power-penalty contract. On problems without an aux objective, or when no aux token is enabled, the standard methods use the scalar primal and the aux histories stay empty, so non-aux runs pay no overhead. Toggling save tokens or calling `set_penalty_fn` (which retraces) re-evaluates the condition, so auto-logging stays in sync after a penalty swap.
260
+ Auto-logging is active only when both conditions hold: at least one aux token is in the construction-time `save` list, and the problem exposes `objective_function_aux`. With no aux token, standard methods use the scalar primal and aux histories stay empty, so non-aux runs pay no aux-computation overhead. Supplying an aux token for a problem without an aux objective emits `RuntimeWarning` and falls back to the same scalar path; explicit aux methods still raise `RuntimeError`. Calling `set_penalty_fn` rebinds the cached callables so the selected value family stays in sync after a penalty swap.
261
261
 
262
262
  ```python
263
263
  obj = Objective(problem, save=["is_feasible"])
@@ -289,13 +289,31 @@ When `unbounded=True`, the returned callable maps unbounded parameters into the
289
289
 
290
290
  Because this callable is intentionally unlogged, pair it with `obj.log_evaluation(...)` after each completed optimizer step if the evaluation should count toward benchmark histories. For ordinary algorithm loops, prefer `obj.value(...)`, `obj.value_and_grad(...)`, or the batched evaluation methods.
291
291
 
292
+ ### Unlogged raw value-and-aux function
293
+
294
+ ```python
295
+ aux_value_fn = obj.value_function_aux() # follows obj.unbounded
296
+ if aux_value_fn is None:
297
+ raise RuntimeError("this problem has no aux objective")
298
+
299
+ value_and_grad_aux_fn = jax.jit(
300
+ jax.value_and_grad(aux_value_fn, has_aux=True)
301
+ )
302
+ _ = value_and_grad_aux_fn(params) # JIT warmup before timing
303
+ obj.start_logging()
304
+ (loss, aux), grad = value_and_grad_aux_fn(params)
305
+ obj.log_evaluation(params=params, loss=loss, grad=grad, aux=aux)
306
+ ```
307
+
308
+ `value_function_aux(unbounded=None)` is the mapped, JAX-compatible counterpart to `value_function()`. It returns an unlogged callable whose invocation yields `(loss, aux)`, follows the same `unbounded` argument semantics, and returns `None` when the problem does not expose `objective_function_aux`. This is the safe way for a custom JIT loop to obtain aux without bypassing the Objective's space mapping. `log_evaluation` persists only fields selected by aux save tokens.
309
+
292
310
  ### Manual logging
293
311
 
294
312
  ```python
295
- obj.log_evaluation(params=..., loss=..., grad=..., hessian=...)
313
+ obj.log_evaluation(params=..., loss=..., grad=..., hessian=..., aux=...)
296
314
  ```
297
315
 
298
- For algorithms with custom JIT-compiled evaluation loops that use `obj.value_function(...)` instead of calling `obj.value()` directly. Accepts the same `params`, `loss`, `grad`, `hessian` arguments and performs identical history recording.
316
+ For algorithms with custom JIT-compiled evaluation loops that use `obj.value_function(...)` or `obj.value_function_aux(...)` instead of calling `obj.value()` directly. Accepts `params`, `loss`, `grad`, `hessian`, and optional `aux` results and performs the same history recording. Supplied aux is persisted only for matching save tokens. In an aux-enabled run, omitting `aux` records `None` in each selected aux history; manual logging never manufactures aux diagnostics.
299
317
 
300
318
  ---
301
319
 
@@ -376,7 +394,7 @@ obj.set_space_mode(
376
394
 
377
395
  Sets the wrapped problem's penalty function and re-binds all internal JAX evaluation paths.
378
396
 
379
- For problems that opt into the power-penalty contract (`ConstrainedVoyagerProblem`, `UIFOProblem`), this forwards to `problem.set_penalty_fn(fn)`, which updates the problem's penalty callable and re-traces its JIT-compiled `objective_function` so the new penalty actually takes effect. The Objective then re-binds its own cached `value` / `grad` / `hessian` / `vmap_*` callables, mirroring the tail of `set_space_mode`.
397
+ For problems that opt into the power-penalty contract (`ConstrainedVoyagerProblem`, `UIFOProblem`), this forwards to `problem.set_penalty_fn(fn)`, which updates the problem's penalty callable and rebuilds both its JIT-compiled scalar and aux objectives. The Objective then re-binds its own cached `value` / `grad` / `hessian` / `vmap_*` callables, mirroring the tail of `set_space_mode`.
380
398
 
381
399
  - Must be called before `start_logging()`
382
400
  - Raises `RuntimeError` if the wrapped problem does not opt into the power-penalty contract. The opt-in marker is the class attribute `_supports_power_penalty`; problems without a power-constraint path (`VoyagerProblem`, `VoyagerTuningProblem`, and any non-optical `ContinuousProblem`) leave it `False` and reject the call rather than silently rebuilding, even though they inherit the method from `OpticalSetupProblem`
@@ -402,10 +420,10 @@ The constrained problems also expose a JIT-compiled `objective_function_aux(para
402
420
  | `sensitivity_loss` | scalar | The unpenalised sensitivity loss. |
403
421
  | `penalty` | scalar | The summed penalty contribution. |
404
422
  | `is_feasible` | scalar bool | `True` iff every per-group power is at or below its threshold. This is a physical check, independent of the active `power_penalty_fn` preset. |
405
- | `violations` | `(n_constraints, n_freq)` | Per-constraint penalty values. |
423
+ | `violations` | `(n_constraints,)` | Per-constraint penalty values. |
406
424
  | `power_values` | dict with `hard`, `soft`, `detector` leaves | Raw per-group power arrays. |
407
425
 
408
- `aux` is a JAX pytree, so `objective_function_aux` vmapps cleanly: a batched call adds a leading batch dim to every leaf, including the `power_values` sub-arrays. The Objective-level `value_aux` / `value_and_grad_aux` / `vmap_*_aux` wrappers thread aux through logging and the save-token system; see the [Aux evaluation](#aux-evaluation) subsection and the aux tokens in [Choosing what to save](#choosing-what-to-save).
426
+ `aux` is a JAX pytree, so `objective_function_aux` vmapps cleanly: a batched call adds a leading batch dim to every leaf, including the `power_values` sub-arrays. The Objective-level explicit `*_aux` wrappers and standard value-bearing methods thread aux through logging and the save-token system; see the [Aux evaluation](#aux-evaluation) subsection and the aux tokens in [Choosing what to save](#choosing-what-to-save).
409
427
 
410
428
  ---
411
429
 
@@ -479,7 +497,7 @@ bounded ≈ lb + (ub - lb) * forward(random_params_unbounded(...))
479
497
  | `best_params_bounded` | `Array \| None` | Best parameters mapped to bounded space via the active mapping (custom mapping if configured, otherwise sigmoid). **Use this for final output.** |
480
498
  | `best_eval_index` | `int \| None` | Index into the loss history holding the best loss, or `None` before the first improvement. |
481
499
  | `best_batch_index` | `int \| None` | Within-batch index of the best loss when it came from a batched evaluation, or `None` for single-point evals. |
482
- | `best_is_feasible` | `bool \| None` | Feasibility of the best-loss point, or `None` when the `is_feasible` save token was never enabled, no evaluation has improved yet, or the best point came from a non-aux evaluation. Uses the physical `power <= threshold` check, so it stays meaningful even when the penalty is disabled with `zero_penalty`. For batched best losses, the feasibility of the winning batch element is returned when `batched_is_feasible` storage is on; otherwise the per-call reduced entry is used. |
500
+ | `best_is_feasible` | `bool \| None` | Feasibility of the best-loss point, or `None` when neither `is_feasible` nor `batched_is_feasible` was enabled, no evaluation has improved yet, or a manually logged best point omitted aux. Uses the physical `power <= threshold` check, so it stays meaningful even when the penalty is disabled with `zero_penalty`. For batched best losses, the feasibility of the winning batch element is returned when `batched_is_feasible` storage is on; otherwise the per-call reduced entry is used. |
483
501
 
484
502
  ### Current State
485
503
 
@@ -500,15 +518,15 @@ These properties return **copies** to prevent external mutation.
500
518
  | `params_history` | `list` | All recorded parameter vectors (raw space, i.e. as it was given to the `Objective`). |
501
519
  | `params_history_bounded` | `list` | Params history mapped to bounded space. |
502
520
  | `time_steps` | `list[float]` | Elapsed time at each recorded evaluation. |
503
- | `sensitivity_loss_history` | `list` | Per-eval unpenalised sensitivity loss (aux evals only, when `sensitivity_loss` or `batched_sensitivity_loss` is enabled). |
504
- | `penalty_history` | `list` | Per-eval summed penalty (aux evals only, when `penalty` or `batched_penalty` is enabled). |
505
- | `is_feasible_history` | `list` | Per-eval physical feasibility flag (aux evals only, when `is_feasible` or `batched_is_feasible` is enabled). |
506
- | `violations_history` | `list` | Per-eval per-constraint violation arrays (aux evals only, when `violations` or `batched_violations` is enabled). |
507
- | `power_hard_history` | `list` | Per-eval hard-group power arrays (aux evals only, when `power_values` or `batched_power_values` is enabled). |
508
- | `power_soft_history` | `list` | Per-eval soft-group power arrays (aux evals only, when `power_values` or `batched_power_values` is enabled). |
509
- | `power_detector_history` | `list` | Per-eval detector-group power arrays (aux evals only, when `power_values` or `batched_power_values` is enabled). |
521
+ | `sensitivity_loss_history` | `list` | Per-logged-call unpenalised sensitivity loss when selected; `None` for calls without aux. |
522
+ | `penalty_history` | `list` | Per-logged-call summed penalty when selected; `None` for calls without aux. |
523
+ | `is_feasible_history` | `list` | Per-logged-call physical feasibility flag when selected; `None` for calls without aux. |
524
+ | `violations_history` | `list` | Per-logged-call constraint violation arrays when selected; `None` for calls without aux. |
525
+ | `power_hard_history` | `list` | Per-logged-call hard-group power arrays when `power_values` storage is selected; `None` for calls without aux. |
526
+ | `power_soft_history` | `list` | Per-logged-call soft-group power arrays when `power_values` storage is selected; `None` for calls without aux. |
527
+ | `power_detector_history` | `list` | Per-logged-call detector-group power arrays when `power_values` storage is selected; `None` for calls without aux. |
510
528
 
511
- The aux histories are aligned with the standard histories by index. Non-aux evaluations (`value`, `grad`, `hessian`, `vmap_*`) do not append to them, so an aux history may be shorter than `loss_history` when a run mixes aux and non-aux calls.
529
+ Each enabled aux history is index-aligned with `loss_history`: value-bearing standard and explicit aux calls append the selected diagnostics, while derivative-only calls and `log_evaluation(..., aux=None)` append `None`. Aux histories whose save token is disabled remain empty.
512
530
 
513
531
  ### Reduced History
514
532
 
@@ -552,7 +570,7 @@ The first save without explicit overrides caches the path; subsequent periodic s
552
570
 
553
571
  ### `load_run_data(filepath)`
554
572
 
555
- Restores all tracking state from a checkpoint via `CheckpointManager.load()` -> `Objective._apply_run_state()`. Adjusts `start_time` so that `time_elapsed` continues seamlessly from where the checkpoint left off. The loaded path is cached so a later `save_run_data()` overwrites the same file. If the checkpoint's `SaveConfig` differs from the current Objective's, a warning is printed (when `verbose >= 1`).
573
+ Restores all tracking state from a checkpoint via `CheckpointManager.load()` -> `Objective._apply_run_state()`. Adjusts `start_time` so that `time_elapsed` continues seamlessly from where the checkpoint left off. The loaded path is cached so a later `save_run_data()` overwrites the same file. If the checkpoint's `SaveConfig` differs from the constructor configuration, `RuntimeWarning` is emitted and the checkpoint configuration is adopted before state is applied. When the problem supports aux logging, empty legacy aux histories selected by that config are backfilled with one `None` entry per prior logged call before evaluation resumes.
556
574
 
557
575
  The originating `Problem` can be rebuilt from the embedded `problem_spec`:
558
576
 
@@ -603,12 +621,12 @@ Returns a snapshot dictionary:
603
621
  Every evaluation method follows the same pipeline internally:
604
622
 
605
623
  1. **Execute** the JAX function (`_func`, `_value_and_grad_func`, `_vmap_func`, etc.)
606
- 2. **`_log(params, loss, grad, hessian)`**: the coordinator: checks `time_exceeded`, appends to `_time_steps`, then delegates to `_log_evals()` and `_log_to_file()`.
607
- 3. **`_log_evals(params, loss, grad, hessian, time_exceeded)`**: record histories; update `best_loss` / `best_params`; update `improvement_count` / `evals_since_improvement`; check eval budget. Receives `time_exceeded` as an explicit parameter from `_log()` to ensure a consistent time snapshot.
624
+ 2. **`_log(params, loss, grad, hessian, aux)`**: takes one elapsed-time snapshot, asks `_log_evals()` to admit and atomically record the call, appends the timestamp only when admitted, then invokes `_log_to_file()`.
625
+ 3. **`_log_evals(params, loss, grad, hessian, aux, time_exceeded)`**: checks the evaluation/time budget, resolves selected aux leaves before mutating histories, records all aligned histories, updates best/stagnation state, and returns whether the call was admitted.
608
626
  4. **`_log_to_file()`**: calls `CheckpointManager.tick(eval_count, state_factory)`, which checks the cadence (`save_every`, set from `save_to_file_every`), lazily builds a `RunState` only when a checkpoint is due, saves it through the internal `StorageBackend`, and returns the wall-clock duration of the save. The Objective advances `_start_time` by that duration so the checkpoint write does not consume wall-clock budget.
609
627
 
610
- > **Important:** These are private methods; do not call `_log()`, `_log_evals()`, or `_log_to_file()` directly from algorithm code. If you want manual logging, use the public `log_evaluation(params, loss, grad, hessian=None)` method instead, which delegates to `_log()`. See the [JIT-compiled loop guide](Implementing-a-New-Algorithm#custom-jit-compiled-loops-with-log_evaluation) for details.
628
+ > **Important:** These are private methods; do not call `_log()`, `_log_evals()`, or `_log_to_file()` directly from algorithm code. If you want manual logging, use the public `log_evaluation(params, loss, grad=None, hessian=None, aux=None)` method instead, which delegates to `_log()`. See the [JIT-compiled loop guide](Implementing-a-New-Algorithm#custom-jit-compiled-loops-with-log_evaluation) for details.
611
629
 
612
630
  Budget enforcement happens *after* the evaluation returns. This means the algorithm always receives a valid result, but once any budget is exceeded the history stops growing and `budget_exceeded` becomes `True`.
613
631
 
614
- When a batch evaluation (`vmap_*`) would push `eval_count` past `max_evals`, the evaluations are counted but *not logged*, preserving history alignment and setting the `budget_exceeded` flag to `True`. The `time_steps` entry added by `_log()` is also removed to keep all lists in sync. This may be subject to change but in the current setting, this is the most straight-forward way and irrelevant if budged is planned well (reducing population as `evals_left` nears zero).
632
+ When a batch evaluation (`vmap_*`) would push `eval_count` past `max_evals`, the evaluations are counted but *not logged*, preserving history alignment and setting the `budget_exceeded` flag to `True`. Because admission happens before history mutation, no loss, parameter, derivative, aux, or time entry needs to be rolled back. Reducing population as `evals_left` nears zero avoids evaluating an unrecorded batch.
@@ -197,7 +197,7 @@ problem = ConstrainedVoyagerProblem(n_frequencies=50, signal_floor=1e-20)
197
197
  problem = ConstrainedVoyagerProblem(power_penalty_fn=my_quadratic_penalty)
198
198
  ```
199
199
 
200
- The penalty function can also be swapped **after** the problem has been constructed (e.g. after wrapping it in an `Objective`), via `Objective.set_penalty_fn(fn)`. This re-traces the problem's JIT-compiled `objective_function` and re-binds the Objective's cached evaluation callables, so the new penalty takes effect on subsequent evaluations. It must be called before `Objective.start_logging()`:
200
+ The penalty function can also be swapped **after** the problem has been constructed (e.g. after wrapping it in an `Objective`), via `Objective.set_penalty_fn(fn)`. This rebuilds the problem's JIT-compiled scalar and aux objectives and re-binds the Objective's cached evaluation callables, so the new penalty takes effect on subsequent evaluations. It must be called before `Objective.start_logging()`:
201
201
 
202
202
  ```python
203
203
  from dfbench import Objective
@@ -229,7 +229,7 @@ The constrained problems also expose a JIT-compiled `objective_function_aux(para
229
229
 
230
230
  Because `aux` is a JAX pytree, `objective_function_aux` vmapps cleanly: a batched call adds a leading batch dim to every leaf, including the `power_values` sub-arrays.
231
231
 
232
- The `Objective` wraps this with `value_aux`, `value_and_grad_aux`, `vmap_value_aux`, and `vmap_value_and_grad_aux`. These thread aux through logging and the save-token system: each aux field has its own token (`sensitivity_loss`, `penalty`, `is_feasible`, `power_values`, `violations`) plus a `batched_*` variant and `aux` / `batched_aux` convenience aliases. When a `batched_*` token is off and the non-batched token is on, batched aux entries are reduced to the representative point picked by the loss minimum, so the recorded `is_feasible` and `violations` reflect that best point. `Objective.best_is_feasible` reports the feasibility of the best-loss point from that recorded history. Full reference in the [Objective API Reference](Objective-API-Reference).
232
+ The `Objective` wraps this with `value_aux`, `value_and_grad_aux`, `vmap_value_aux`, and `vmap_value_and_grad_aux`. These explicit methods—and standard value-bearing methods when an aux save token is selected—thread aux through logging without a second forward pass. Each aux field has its own token (`sensitivity_loss`, `penalty`, `is_feasible`, `power_values`, `violations`) plus a `batched_*` variant and `aux` / `batched_aux` convenience aliases. Derivative-only calls do not produce a primal aux pytree and append `None` to enabled aux histories. When a `batched_*` token is off and the non-batched token is on, aux from every batched value-bearing call, including a singleton batch, is reduced to the representative point picked by the loss minimum. `Objective.best_is_feasible` reports the feasibility of the best-loss point from that recorded history. Full reference in the [Objective API Reference](Objective-API-Reference).
233
233
 
234
234
  ---
235
235
 
@@ -70,14 +70,23 @@ CheckpointManager <- the only facade Objective talks to
70
70
  | `grad_history` | `np.ndarray` | Aligned gradient history |
71
71
  | `hessian_history` | `np.ndarray` | Aligned Hessian history |
72
72
  | `params_history` | `np.ndarray` | Aligned parameter history (raw space) |
73
- | `eval_type_history` | `np.ndarray` | Per-eval bitmask call type |
74
- | `time_steps` | `np.ndarray` | Elapsed-time stamps aligned with histories |
73
+ | `eval_type_history` | `np.ndarray` | One call-type bitmask per logged call when enabled |
74
+ | `time_steps` | `np.ndarray` | Elapsed-time stamps for admitted logged calls when enabled |
75
+ | `sensitivity_loss_history` | `np.ndarray` | Selected aux sensitivity losses; `None` for calls without aux |
76
+ | `penalty_history` | `np.ndarray` | Selected aux penalties; `None` for calls without aux |
77
+ | `is_feasible_history` | `np.ndarray` | Selected aux feasibility flags; `None` for calls without aux |
78
+ | `violations_history` | `np.ndarray` | Selected aux violation arrays; `None` for calls without aux |
79
+ | `power_hard_history` | `np.ndarray` | Selected aux hard-group powers; `None` for calls without aux |
80
+ | `power_soft_history` | `np.ndarray` | Selected aux soft-group powers; `None` for calls without aux |
81
+ | `power_detector_history` | `np.ndarray` | Selected aux detector powers; `None` for calls without aux |
75
82
  | `eval_count` | `int` | Total evaluations |
76
83
  | `best_loss` | `float` | Lowest loss observed |
77
84
  | `best_params` | `np.ndarray` | Parameters at `best_loss` (float64; empty if none) |
85
+ | `best_eval_index` | `int \| None` | Logged-call index containing `best_loss` |
86
+ | `best_batch_index` | `int \| None` | Winning within-batch index, if applicable |
78
87
  | `improvement_count` | `int` | Times `best_loss` was improved |
79
88
  | `evals_since_improvement` | `int` | Evaluations since last improvement |
80
- | `log_call_count` | `int` | Number of internal `_log_evals` invocations |
89
+ | `log_call_count` | `int` | Number of calls admitted into the histories |
81
90
  | `eval_type_counts` | `dict[int, int]` | Distribution of eval call types |
82
91
  | `metadata` | `RunMetadata` | Record with run identity + problem spec |
83
92
 
@@ -324,6 +333,14 @@ CheckpointManager(..., validate_on_save=False, validate_on_load=False)
324
333
 
325
334
  The first `save()` without explicit overrides caches the computed path. Subsequent saves without overrides overwrite the same file. Passing `explicit_path` or `hyper_param_str` bypasses the cache. `load()` caches the loaded path, so a resume-then-save cycle overwrites the same file.
326
335
 
336
+ `SaveConfig` is part of the persistent run identity. When `Objective.load_run_data()`
337
+ loads a checkpoint whose save configuration differs from the constructor, it
338
+ emits `RuntimeWarning` and adopts the checkpoint configuration before restoring
339
+ state. This prevents enabled histories from changing halfway through a run.
340
+ When the problem supports aux logging, legacy checkpoints that advertise an
341
+ enabled aux history but store no array for it are aligned with `None` entries up
342
+ to `log_call_count` before evaluation resumes.
343
+
327
344
  ### `tick`: periodic checkpointing
328
345
 
329
346
  `tick` is called by `Objective._log_to_file` after each evaluation. The manager checks the cadence (`save_every`); if a checkpoint is due, it lazily calls `state_factory` to build a `RunState`, saves it, and returns the wall-clock duration of the save. The `Objective` then advances `_start_time` by that duration, so the checkpoint write does not consume wall-clock budget. If no checkpoint is due, `tick` returns `0.0` and `state_factory` is never called.
@@ -344,7 +361,7 @@ The storage components remain modular and individually testable (see the section
344
361
 
345
362
  ## `RunState` invariant contract
346
363
 
347
- A `RunState` is the scoring contract: every metric the competition computes derives from one. A buggy or adversarial submitter algorithm can produce a state whose fields disagree (e.g. `best_loss` doesn't match `loss_history`, or a history length diverges from `eval_count`). Without a gate, that mismatch surfaces as a crash deep inside a serializer or the scoring layer, with no clean rejection path.
364
+ A `RunState` is the scoring contract: every metric the competition computes derives from one. A buggy or adversarial submitter algorithm can produce a state whose fields disagree (e.g. `best_loss` doesn't match `loss_history`, or a non-empty history length diverges from `log_call_count`). Without a gate, that mismatch surfaces as a crash deep inside a serializer or the scoring layer, with no clean rejection path.
348
365
 
349
366
  `validate_run_state(state, *, strict=True) -> ValidationReport` is the gate. It is a pure function: no I/O, and it does not raise on a malformed state. It collects every violation, so a rejection report lists all problems in one pass rather than one at a time.
350
367
 
@@ -365,7 +382,7 @@ if not report.ok:
365
382
 
366
383
  | Tier | Checks | When |
367
384
  |------|--------|------|
368
- | **Structural (A)** | Types, shapes, ranges: `eval_count` is a non-negative int; the six histories are `np.ndarray` (first axis = time; N-D allowed since NumPy collapses uniform object arrays); `best_params` is 1-D float64 or empty; `eval_type_counts` is a dict with int keys / non-negative int values; `metadata` is a `RunMetadata`; scalar counters are non-negative ints. A3: every non-empty history's first-axis length equals `eval_count`. Empty is allowed; it means that history was disabled by `SaveConfig` or dropped by `RunData.to_run_state`. | Always |
385
+ | **Structural (A)** | Types, shapes, ranges: `eval_count` is a non-negative int; all 13 history fields are `np.ndarray` (first axis = logged call; N-D allowed since NumPy collapses uniform object arrays); `best_params` is 1-D float64 or empty; `eval_type_counts` is a dict with int keys / non-negative int values; `metadata` is a `RunMetadata`; scalar counters are non-negative ints. A3: every non-empty history's first-axis length equals `log_call_count`. Empty is allowed; it means that history was disabled by `SaveConfig` or dropped by `RunData.to_run_state`. Legacy reduced states whose `log_call_count` is zero fall back to `eval_count`. | Always |
369
386
  | **Semantic (B)** | Cross-field consistency, skipped when `strict=False`: `best_loss` is finite iff a non-NaN loss was recorded (grad-only / hessian-only calls increment `eval_count` and append NaN to `loss_history` without updating `best_loss`, so `best_loss == inf` with `eval_count > 0` is legal); `best_loss == nanmin(loss_history)` within `1e-9`; `sum(eval_type_counts.values()) == log_call_count`; `improvement_count <= log_call_count`; `evals_since_improvement <= eval_count`. | `strict=True` only |
370
387
 
371
388
  The on-disk `format_version` is not re-checked here. It is already enforced at deserialization (`RunMetadata.from_dict` and each serializer's `deserialize`), and a constructed `RunMetadata` always reports `FORMAT_VERSION` via `to_dict`, so a future-version metadata cannot exist by construction.
@@ -374,7 +391,7 @@ The on-disk `format_version` is not re-checked here. It is already enforced at d
374
391
 
375
392
  These are deliberately out of scope for the current contract; see plan #2 for the rationale:
376
393
 
377
- - NaN inside histories (legal placeholder for missing grad/hessian/params when a `SaveConfig` flag is off).
394
+ - Placeholder contents are not semantically validated. `loss_history` uses NaN when a derivative-only call has no returned loss; enabled standard or aux histories use `None` when that result is absent; disabled histories are empty.
378
395
  - Batched-array internal shapes (expensive, fragile).
379
396
  - `metadata.extra["problem_spec"]` contents. Validated at construction by `ProblemSpec.__post_init__` (type/version/params shape) and at reconstruction by `build_problem_from_spec` (registry lookup). The `RunState` validator itself does not re-check the spec.
380
397
  - `SaveConfig` internal coherence (e.g. `grad=True` vs `batched_grad=False`). Belongs to `SaveConfig`.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "dfbench"
7
- version = "0.3.1"
7
+ version = "0.3.2"
8
8
  description = "dfbench: A Black-Box Optimization Framework for JAX-Differentiable Gravitational Wave Detector Discovery"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11,<3.14"